pax_global_header00006660000000000000000000000064145021376060014516gustar00rootroot0000000000000052 comment=3923526c6b4c048bbecad2506c4c9963bc46cd36 mosquitto-2.0.18/000077500000000000000000000000001450213760600136525ustar00rootroot00000000000000mosquitto-2.0.18/.github/000077500000000000000000000000001450213760600152125ustar00rootroot00000000000000mosquitto-2.0.18/.github/issue_template.md000066400000000000000000000015541450213760600205640ustar00rootroot00000000000000 mosquitto-2.0.18/.github/pull_request_template.md000066400000000000000000000022421450213760600221530ustar00rootroot00000000000000Thank you for contributing your time to the Mosquitto project! Before you go any further, please note that we cannot accept contributions if you haven't signed the [Eclipse Contributor Agreement](https://www.eclipse.org/legal/ECA.php). If you aren't able to do that, or just don't want to, please describe your bug fix/feature change in an issue. For simple bug fixes it is can be just as easy for us to be told about the problem and then go fix it directly. Then please check the following list of things we ask for in your pull request: - [ ] 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? - [ ] 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. - [ ] If you are contributing a new feature, is your work based off the develop branch? - [ ] If you are contributing a bugfix, is your work based off the fixes branch? - [ ] Have you added an explanation of what your changes do and why you'd like us to include them? - [ ] Have you successfully run `make test` with your changes locally? ----- mosquitto-2.0.18/.github/workflows/000077500000000000000000000000001450213760600172475ustar00rootroot00000000000000mosquitto-2.0.18/.github/workflows/codeql-analysis.yml000066400000000000000000000047311450213760600230670ustar00rootroot00000000000000# For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ master, fixes, develop, 1.6.x ] pull_request: # The branches below must be a subset of the branches above branches: [ master ] schedule: - cron: '44 18 * * 1' jobs: analyze: name: Analyze runs-on: ubuntu-latest strategy: fail-fast: false matrix: language: [ 'cpp', 'python' ] # CodeQL supports [ 'cpp', 'csharp', 'go', 'java', 'javascript', 'python' ] # Learn more: # 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 steps: - name: Checkout repository uses: actions/checkout@v2 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v1 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # queries: ./path/to/local/query, your-org/your-repo/queries@main # ℹ️ Command-line programs to run using the OS shell. # 📚 https://git.io/JvXDl - run: sudo apt-get update && sudo apt-get install -y gcc g++ git libssl-dev # Install cJSON - run: git clone https://github.com/DaveGamble/cJSON /tmp/cJSON - run: wget https://github.com/DaveGamble/cJSON/archive/v1.7.14.tar.gz -O /tmp/cjson.tar.gz - run: mkdir -p /tmp/build/cjson - run: tar --strip=1 -xf /tmp/cjson.tar.gz -C /tmp/build/cjson - run: rm /tmp/cjson.tar.gz - run: cd /tmp/build/cjson && cmake . -DCMAKE_BUILD_TYPE=MinSizeRel -DCJSON_BUILD_SHARED_LIBS=ON -DCMAKE_INSTALL_PREFIX=/usr - run: sudo make -C /tmp/build/cjson install # Now build Mosquitto - run: make binary - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v1 mosquitto-2.0.18/.github/workflows/coverity-scan-develop.yml000066400000000000000000000011331450213760600242120ustar00rootroot00000000000000name: Coverity Scan develop branch on a weekly basis on: workflow_dispatch: schedule: - cron: "7 3 * * 0" jobs: coverity: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: ref: develop - name: Dependencies run: sudo apt-get install -y libcjson-dev libsqlite3-dev libssl-dev uthash-dev - uses: vapier/coverity-scan-action@v1 with: build_language: 'cxx' project: "eclipse/mosquitto" token: ${{ secrets.COVERITY_SCAN_TOKEN }} email: ${{ secrets.COVERITY_SCAN_EMAIL }} command: "make binary" mosquitto-2.0.18/.github/workflows/coverity-scan-fixes.yml000066400000000000000000000011271450213760600236750ustar00rootroot00000000000000name: Coverity Scan fixes branch on a weekly basis on: workflow_dispatch: schedule: - cron: "7 3 * * 3" jobs: coverity: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 with: ref: fixes - name: Dependencies run: sudo apt-get install -y libcjson-dev libsqlite3-dev libssl-dev uthash-dev - uses: vapier/coverity-scan-action@v1 with: build_language: 'cxx' project: "eclipse/mosquitto" token: ${{ secrets.COVERITY_SCAN_TOKEN }} email: ${{ secrets.COVERITY_SCAN_EMAIL }} command: "make binary" mosquitto-2.0.18/.github/workflows/delete-old-workflow-runs.yml000066400000000000000000000006631450213760600246520ustar00rootroot00000000000000name: Delete old workflow runs on: workflow_dispatch: schedule: - cron: '0 0 1 * *' # Run monthly, at 00:00 on the 1st day of month. jobs: del_runs: runs-on: ubuntu-latest steps: - name: Delete workflow runs uses: Mattraks/delete-workflow-runs@v2 with: token: ${{ github.token }} repository: ${{ github.repository }} retain_days: 30 keep_minimum_runs: 6 mosquitto-2.0.18/.github/workflows/lock.yml000066400000000000000000000004631450213760600207250ustar00rootroot00000000000000name: 'Lock Threads' on: schedule: - cron: '0 0 * * 0' workflow_dispatch: permissions: issues: write pull-requests: write concurrency: group: lock jobs: action: runs-on: ubuntu-latest steps: - uses: dessant/lock-threads@v3 with: issue-inactive-days: '90' mosquitto-2.0.18/.github/workflows/windows.yml000066400000000000000000000046571450213760600215000ustar00rootroot00000000000000name: Windows build on: workflow_dispatch: push: branches: [ "master", "fixes" ] tags: [ "v[0-9]+.*" ] pull_request: branches: [ "master", "fixes" ] env: # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) BUILD_TYPE: Release jobs: cjson: runs-on: windows-2022 steps: - uses: actions/checkout@v3 with: repository: DaveGamble/cJSON ref: v1.7.15 - name: Configure CMake cJSON run: cmake -B ${{github.workspace}}/build64 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DENABLE_CJSON_TEST=OFF -DBUILD_SHARED_LIBS=OFF -DBUILD_SHARED_AND_STATIC_LIBS=OFF -DCJSON_BUILD_SHARED_LIBS=OFF -DCJSON_OVERRIDE_BUILD_SHARED_LIBS=OFF -DCMAKE_GENERATOR_PLATFORM=x64 - name: Build cJSON run: cmake --build ${{github.workspace}}/build64 --config ${{env.BUILD_TYPE}} - name: Install cJSON run: cmake --install ${{github.workspace}}/build64 --config ${{env.BUILD_TYPE}} - name: Upload cJSON uses: actions/upload-artifact@v3 with: name: cjson-bin path: C:\Program Files\cJSON mosquitto: runs-on: windows-2022 needs: - cjson env: CJSON_DIR: C:\Program Files\cJSON steps: - uses: actions/checkout@v3 - name: install openssl run: choco install openssl - name: Download cJSON uses: actions/download-artifact@v3 with: name: cjson-bin path: C:\Program Files\cJSON - name: Configure CMake run: cmake -B ${{github.workspace}}/build64 -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DWITH_TESTS=OFF -DCMAKE_GENERATOR_PLATFORM=x64 -DCJSON_INCLUDE_DIR="C:/Program Files/cJSON/include" -DCJSON_LIBRARY="C:/Program Files/cJSON/lib/cjson.lib" - name: Build run: cmake --build ${{github.workspace}}/build64 --config ${{env.BUILD_TYPE}} - uses: suisei-cn/actions-download-file@v1.0.1 id: vcredist name: Download VC redistributable with: url: https://aka.ms/vs/17/release/vc_redist.x64.exe target: ${{github.workspace}}/installer/ - name: Installer uses: joncloud/makensis-action@v3.7 with: script-file: ${{github.workspace}}/installer/mosquitto.nsi - name: Upload installer to artifacts uses: actions/upload-artifact/@v2 with: name: installer path: ${{ github.workspace }}/installer/mosquitto*.exe mosquitto-2.0.18/.gitignore000066400000000000000000000027671450213760600156560ustar00rootroot00000000000000# .gitignore *.db *.gcda *.gcno *.exe *.o *.old *.pyc *.so *.vglog callgrind.out.* dhat.out.* massif.out.* vglog* c/*.test cpp/*.test apps/db_dump/mosquitto_db_dump apps/mosquitto_ctrl/mosquitto_ctrl apps/mosquitto_passwd/mosquitto_passwd build/ build64/ client/mosquitto_pub client/mosquitto_rr client/mosquitto_sub client/testing client/testing.c cov-int/ dist/ docker/local/mosq.tar.gz examples/mysql_log/mosquitto_mysql_log examples/temperature_conversion/mqtt_temperature_conversion lib/cpp/libmosquittopp.so* lib/cpp/libmosquittopp.a lib/libmosquitto.so* lib/libmosquitto.a man/mosquitto.8 man/mosquitto-tls.7 man/mosquitto.conf.5 man/libmosquitto.3 man/mosquitto_ctrl.1 man/mosquitto_ctrl_dynsec.1 man/mosquitto_passwd.1 man/mosquitto_pub.1 man/mosquitto_rr.1 man/mosquitto_sub.1 man/mqtt.7 out/ src/mosquitto test/broker/broker.pid test/test_client test/fake_user test/msgsps_pub test/msgsps_sub test/msgsps_pub.dat test/msgsps_sub.dat test/broker/c/auth_plugin.so test/broker/c/*.test test/ssl/*.csr test/ssl/rootCA/ test/ssl/signingCA/ test/lib/c/*.test test/lib/cpp/*.test test/unit/bridge_topic_test test/unit/coverage.info test/unit/mosq_test test/unit/persist_read_test test/unit/persist_write_test test/unit/subs_test test/unit/tls_test test/unit/out/ www/cache/ __pycache__ # Debian generated files debian/.debhelper/ debian/debhelper-build-stamp debian/files debian/*.log debian/*.substvars debian/*mosquitto*/ debian/*.debhelper debian/tmp/ obj-*/ # Emacs generated files *~ # Others tmp/ mosquitto-2.0.18/.travis.yml000066400000000000000000000023611450213760600157650ustar00rootroot00000000000000language: c compiler: - gcc - clang os: - linux - osx dist: - bionic sudo: true env: global: # COVERITY_SCAN_TOKEN - secure: "j58a3zOmHbWvKOfBaR2WJAU0Lz95M0u3Ji9NUn0Gj+v91z/vd0COF8dR6QO03V9fDms44ghBiUOblgfb8z/GFOaagn2FoHUrSka/Sju+gIFejgeSqwyxL3CjcX69M1f/npiePQDDtD5mb4dZTDZ4Nl3BpTqg+Qtar9/S/7LoheecWdd6kFDDMr+0yx3/nibwbC0+tpXftiB62tgaGwABQBvfQbgBXUxJ+0zaKXORiKmhwnEC15Bub0WmBhmZxahzlDMU2cEzxKVQbYrcf0TqZpbPhN7H42d3ssQJ3ogbqJmptZpoRdv9de6G0Hzq5QDLjkQxd+3z4hxRqOPzICoawkljQ6CF/JOSlqRXTP0/272MBHzgancQpQhO5yfB3eV4OFldQAdtIKHV04jQ9uSPYOi48To//HGOG8GOp6jFMrpKs6T6P8UiB+GTe57GjBdQI5uqZiEBMdc5MNaqDDoskdr+jTsnGJg6uY9ARrap3WFFEWiaTbe/wbE7+o0isb79PmgBzFLpcwpBFgfyQUiDb+e8S2d3ugKeIVlsSlrF7ZTXC40Y+qVwGzvf/RSM3+c9N/ikFmrLdzn0rBrwna4t3vPrBoU1seoCAZu5hwAU0+xNCN4P9M1s4tOnrG7Lq5URqkP0n9gTXvI8rQ7dDJibjP+5RoZUOd1XPkHbyN4Kseg=" script: - ./travis-configure.sh - if [[ "$COVERITY_SCAN_BRANCH" != 1 ]]; then make ; make test ; fi before_install: - ./travis-install.sh addons: coverity_scan: project: name: "eclipse/mosquitto" description: "Build submitted via Travis CI" notification_email: roger@atchoo.org build_command_prepend: ./travis-configure.sh build_command: "make binary -j 4" branch_pattern: coverity-.* mosquitto-2.0.18/CMakeLists.txt000066400000000000000000000110071450213760600164110ustar00rootroot00000000000000# This is a cmake script. Process it with the CMake gui or command line utility # to produce makefiles / Visual Studio project files on Mac OS X and Windows. # # To configure the build options either use the CMake gui, or run the command # line utility including the "-i" option. cmake_minimum_required(VERSION 3.1) cmake_policy(SET CMP0042 NEW) project(mosquitto) set (VERSION 2.0.18) list(APPEND CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake/") add_definitions (-DCMAKE -DVERSION=\"${VERSION}\") if (WIN32) add_definitions("-D_CRT_SECURE_NO_WARNINGS") add_definitions("-D_CRT_NONSTDC_NO_DEPRECATE") endif (WIN32) if(APPLE) set(CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS "${CMAKE_SHARED_LIBRARY_CREATE_C_FLAGS} -undefined dynamic_lookup") endif(APPLE) include(GNUInstallDirs) option(WITH_BUNDLED_DEPS "Build with bundled dependencies?" ON) option(WITH_TLS "Include SSL/TLS support?" ON) option(WITH_TLS_PSK "Include TLS-PSK support (requires WITH_TLS)?" ON) option(WITH_EC "Include Elliptic Curve support (requires WITH_TLS)?" ON) if (WITH_TLS) find_package(OpenSSL REQUIRED) add_definitions("-DWITH_TLS") if (WITH_TLS_PSK) add_definitions("-DWITH_TLS_PSK") endif (WITH_TLS_PSK) if (WITH_EC) add_definitions("-DWITH_EC") endif (WITH_EC) else (WITH_TLS) set (OPENSSL_INCLUDE_DIR "") endif (WITH_TLS) option(WITH_UNIX_SOCKETS "Include Unix Domain Socket support?" ON) if (WITH_UNIX_SOCKETS AND NOT WIN32) add_definitions("-DWITH_UNIX_SOCKETS") endif (WITH_UNIX_SOCKETS AND NOT WIN32) option(WITH_SOCKS "Include SOCKS5 support?" ON) if (WITH_SOCKS) add_definitions("-DWITH_SOCKS") endif (WITH_SOCKS) option(WITH_SRV "Include SRV lookup support?" OFF) option(WITH_STATIC_LIBRARIES "Build static versions of the libmosquitto/pp libraries?" OFF) option(WITH_PIC "Build the static library with PIC (Position Independent Code) enabled archives?" OFF) option(WITH_THREADING "Include client library threading support?" ON) if (WITH_THREADING) add_definitions("-DWITH_THREADING") if (WIN32) find_package(Threads REQUIRED) set (PTHREAD_LIBRARIES Threads::Threads) set (PTHREAD_INCLUDE_DIR "") elseif (ANDROID) set (PTHREAD_LIBRARIES "") set (PTHREAD_INCLUDE_DIR "") else (WIN32) find_library(LIBPTHREAD pthread) if (LIBPTHREAD) set (PTHREAD_LIBRARIES pthread) else (LIBPTHREAD) set (PTHREAD_LIBRARIES "") endif() set (PTHREAD_INCLUDE_DIR "") endif (WIN32) else (WITH_THREADING) set (PTHREAD_LIBRARIES "") set (PTHREAD_INCLUDE_DIR "") endif (WITH_THREADING) option(WITH_DLT "Include DLT support?" OFF) message(STATUS "WITH_DLT = ${WITH_DLT}") if (WITH_DLT) #find_package(DLT REQUIRED) find_package(PkgConfig) pkg_check_modules(DLT "automotive-dlt >= 2.11") add_definitions("-DWITH_DLT") endif (WITH_DLT) option(WITH_CJSON "Build with cJSON support (required for dynamic security plugin and useful for mosquitto_sub)?" ON) if (WITH_CJSON) FIND_PACKAGE(cJSON) if (CJSON_FOUND) message(STATUS ${CJSON_FOUND}) else (CJSON_FOUND) message(STATUS "Optional dependency cJSON not found. Some features will be disabled.") endif(CJSON_FOUND) endif() # ======================================== # Include projects # ======================================== option(WITH_CLIENTS "Build clients?" ON) option(WITH_BROKER "Build broker?" ON) option(WITH_APPS "Build apps?" ON) option(WITH_PLUGINS "Build plugins?" ON) option(DOCUMENTATION "Build documentation?" ON) add_subdirectory(lib) if (WITH_CLIENTS) add_subdirectory(client) endif (WITH_CLIENTS) if (WITH_BROKER) add_subdirectory(src) endif (WITH_BROKER) if (WITH_APPS) add_subdirectory(apps) endif (WITH_APPS) if (WITH_PLUGINS) add_subdirectory(plugins) endif (WITH_PLUGINS) if (DOCUMENTATION) add_subdirectory(man) endif (DOCUMENTATION) # ======================================== # Install config file # ======================================== if (WITH_BROKER) install(FILES mosquitto.conf aclfile.example pskfile.example pwfile.example DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/mosquitto") endif (WITH_BROKER) # ======================================== # Install pkg-config files # ======================================== configure_file(libmosquitto.pc.in libmosquitto.pc @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libmosquitto.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") configure_file(libmosquittopp.pc.in libmosquittopp.pc @ONLY) install(FILES "${CMAKE_CURRENT_BINARY_DIR}/libmosquittopp.pc" DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") # ======================================== # Testing # ======================================== enable_testing() mosquitto-2.0.18/CONTRIBUTING.md000066400000000000000000000075121450213760600161100ustar00rootroot00000000000000Contributing to Mosquitto ========================= Thank you for your interest in this project. Project description: -------------------- The Mosquitto project has been created to provide a light weight, open-source implementation, of an MQTT broker to allow new, existing, and emerging applications for Machine-to-Machine (M2M) and Internet of Things (IoT). - - Source ------ The Mosquitto code is stored in a git repository. - https://github.com/eclipse/mosquitto You can contribute bugfixes and new features by sending pull requests through GitHub. ## Legal In order for your contribution to be accepted, it must comply with the Eclipse Foundation IP policy. Please read the [Eclipse Foundation policy on accepting contributions via Git](http://wiki.eclipse.org/Development_Resources/Contributing_via_Git). 1. Sign the [Eclipse ECA](http://www.eclipse.org/legal/ECA.php) 1. Register for an Eclipse Foundation User ID. You can register [here](https://accounts.eclipse.org/user/register). 2. Log into the [Accounts Portal](https://accounts.eclipse.org/), and click on the '[Eclipse Contributor Agreement](https://accounts.eclipse.org/user/eca)' link. 2. Go to your [account settings](https://accounts.eclipse.org/user/edit) and add your GitHub username to your account. 3. Make sure that you _sign-off_ your Git commits in the following format: ``` Signed-off-by: John Smith ``` 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"``` 4. 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. ## Contributing a change 1. [Fork the repository on GitHub](https://github.com/eclipse/mosquitto/fork) 2. Clone the forked repository onto your computer: ``` git clone https://github.com//mosquitto.git ``` 3. If you are adding a new feature, then create a new branch from the latest ```develop``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/develop``` 4. If you are fixing a bug, then create a new branch from the latest ```fixes``` branch with ```git checkout -b YOUR_BRANCH_NAME origin/fixes``` 5. Make your changes 6. Ensure that all new and existing tests pass. 7. Commit the changes into the branch: ``` git commit -s ``` Make sure that your commit message is meaningful and describes your changes correctly. 8. If you have a lot of commits for the change, squash them into a single / few commits. 9. Push the changes in your branch to your forked repository. 10. Finally, go to [https://github.com/eclipse/mosquitto](https://github.com/eclipse/mosquitto) and create a pull request from your "YOUR_BRANCH_NAME" branch to the ```develop``` or ```fixes``` branch as appropriate to request review and merge of the commits in your pushed branch. What happens next depends on the content of the patch. If it is 100% authored by the contributor and is less than 1000 lines (and meets the needs of the project), then it can be pulled into the main repository. If not, more steps are required. These are detailed in the [legal process poster](http://www.eclipse.org/legal/EclipseLegalProcessPoster.pdf). Contact: -------- Contact the project developers via the project's development [mailing list](https://dev.eclipse.org/mailman/listinfo/mosquitto-dev). Search for bugs: ---------------- This project uses [Github](https://github.com/eclipse/mosquitto/issues) to track ongoing development and issues. Create a new bug: ----------------- Be sure to search for existing bugs before you create another one. Remember that contributions are always welcome! - [Create new Mosquitto bug](https://github.com/eclipse/mosquitto/issues) mosquitto-2.0.18/ChangeLog.txt000066400000000000000000004170571450213760600162600ustar00rootroot000000000000002.0.18 - 2023-09-18 =================== Broker: - Fix crash on subscribe under certain unlikely conditions. Closes #2885. Closes #2881. Clients: - Fix mosquitto_rr not honouring `-R`. Closes #2893. 2.0.17 - 2023-08-22 =================== Broker: - Fix `max_queued_messages 0` stopping clients from receiving messages. Closes #2879. - Fix `max_inflight_messages` not being set correctly. Closes #2876. Apps: - Fix `mosquitto_passwd -U` backup file creation. Closes #2873. 2.0.16 - 2023-08-16 =================== Security: - CVE-2023-28366: Fix memory leak in broker when clients send multiple QoS 2 messages with the same message ID, but then never respond to the PUBREC commands. - CVE-2023-0809: Fix excessive memory being allocated based on malicious initial packets that are not CONNECT packets. - CVE-2023-3592: Fix memory leak when clients send v5 CONNECT packets with a will message that contains invalid property types. - Broker will now reject Will messages that attempt to publish to $CONTROL/. - Broker now validates usernames provided in a TLS certificate or TLS-PSK identity are valid UTF-8. - Fix potential crash when loading invalid persistence file. - Library will no longer allow single level wildcard certificates, e.g. *.com Broker: - Fix $SYS messages being expired after 60 seconds and hence unchanged values disappearing. - Fix some retained topic memory not being cleared immediately after used. - Fix error handling related to the `bind_interface` option. - Fix std* files not being redirected when daemonising, when built with assertions removed. Closes #2708. - Fix default settings incorrectly allowing TLS v1.1. Closes #2722. - Use line buffered mode for stdout. Closes #2354. Closes #2749. - Fix bridges with non-matching cleansession/local_cleansession being expired on start after restoring from persistence. Closes #2634. - Fix connections being limited to 2048 on Windows. The limit is now 8192, where supported. Closes #2732. - Broker will log warnings if sensitive files are world readable/writable, or if the owner/group is not the same as the user/group the broker is running as. In future versions the broker will refuse to open these files. - mosquitto_memcmp_const is now more constant time. - Only register with DLT if DLT logging is enabled. - Fix any possible case where a json string might be incorrectly loaded. This could have caused a crash if a textname or textdescription field of a role was not a string, when loading the dynsec config from file only. - Dynsec plugin will not allow duplicate clients/groups/roles when loading config from file, which matches the behaviour for when creating them. - Fix heap overflow when reading corrupt config with "log_dest file". Client library: - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the problem of the client OS sleeping and the client hence not being able to calculate the actual time for keepalive purposes. Closes #2760. - Fix default settings incorrectly allowing TLS v1.1. Closes #2722. - Fix high CPU use on slow TLS connect. Closes #2794. Clients: - Fix incorrect topic-alias property value in mosquitto_sub json output. - Fix confusing message on TLS certificate verification. Closes #2746. Apps: - mosquitto_passwd uses mkstemp() for backup files. - `mosquitto_ctrl dynsec init` will refuse to overwrite an existing file, without a race-condition. 2.0.15 - 2022-08-16 =================== Security: - Deleting the group configured as the anonymous group in the Dynamic Security plugin, would leave a dangling pointer that could lead to a single crash. This is considered a minor issue - only administrative users should have access to dynsec, the impact on availability is one-off, and there is no associated loss of data. It is now forbidden to delete the group configured as the anonymous group. Broker: - Fix memory leak when a plugin modifies the topic of a message in MOSQ_EVT_MESSAGE. - Fix bridge `restart_timeout` not being honoured. - Fix potential memory leaks if a plugin modifies the message in the MOSQ_EVT_MESSAGE event. - Fix unused flags in CONNECT command being forced to be 0, which is not required for MQTT v3.1. Closes #2522. - Improve documentation of `persistent_client_expiration` option. Closes #2404. - Add clients to session expiry check list when restarting and reloading from persistence. Closes #2546. - Fix bridges not sending failure notification messages to the local broker if the remote bridge connection fails. Closes #2467. Closes #1488. - Fix some PUBLISH messages not being counted in $SYS stats. Closes #2448. - Fix incorrect return code being sent in DISCONNECT when a client session is taken over. Closes #2607. - Fix confusing "out of memory" error when a client is kicked in the dynamic security plugin. Closes #2525. - Fix confusing error message when dynamic security config file was a directory. Closes #2520. - Fix bridge queued messages not being persisted when local_cleansession is set to false and cleansession is set to true. Closes #2604. - Dynamic security: Fix modifyClient and modifyGroup commands to not modify the client/group if a new group/client being added is not valid. Closes #2598. - Dynamic security: Fix the plugin being able to be loaded twice. Currently only a single plugin can interact with a unique $CONTROL topic. Using multiple instances of the plugin would produce duplicate entries in the config file. Closes #2601. Closes #2470. - Fix case where expired messages were causing queued messages not to be delivered. Closes #2609. - Fix websockets not passing on the X-Forwarded-For header. Client library: - Fix threads library detection on Windows under cmake. Bumps the minimum cmake version to 3.1, which is still ancient. - Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl ctx not being initialised until starting to connect. Closes #2537. - Fix incorrect use of SSL_connect. Closes #2594. - Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes #2564. - Add documentation of struct mosquitto_message to header. Closes #2561. - Fix documentation omission around mosquitto_reinitialise. Closes #2489. - Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with MOSQ_OPT_SSL_CTX_DEFAULTS. Closes #2463. - Fix failure to close thread in some situations. Closes #2545. Clients: - Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting. Closes #2494. Apps: - Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation. Closes #2471. 2.0.14 - 2021-11-17 =================== Broker: - Fix bridge not respecting receive-maximum when reconnecting with MQTT v5. Client library: - Fix mosquitto_topic_matches_sub2() not using the length parameters. Closes #2364. - Fix incorrect subscribe_callback in mosquittopp.h. Closes #2367. 2.0.13 - 2021-10-27 =================== Broker: - Fix `max_keepalive` option not being able to be set to 0. - Fix LWT messages not being delivered if `per_listener_settings` was set to true. Closes #2314. - Various fixes around inflight quota management. Closes #2306. - Fix problem parsing config files with Windows line endings. Closes #2297. - Don't send retained messages when a shared subscription is made. - Fix log being truncated in Windows. - Fix client id not showing in log on failed connections, where possible. - Fix broker sending duplicate CONNACK on failed MQTT v5 reauthentication. Closes #2339. - Fix mosquitto_plugin.h not including mosquitto_broker.h. Closes #2350. - Fix unlimited message quota not being properly checked for incoming messages. Closes #2593. - Fixed build for openssl compiled with OPENSSL_NO_ENGINE. Closes #2589. Client library: - Initialise sockpairR/W to invalid in `mosquitto_reinitialise()` to avoid closing invalid sockets in `mosquitto_destroy()` on error. Closes #2326. Clients: - Fix date format in mosquitto_sub output. Closes #2353. 2.0.12 - 2021-08-31 =================== Security: - An MQTT v5 client connecting with a large number of user-property properties could cause excessive CPU usage, leading to a loss of performance and possible denial of service. This has been fixed. - Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections. These clients are now rejected if their keepalive value exceeds max_keepalive. This option allows CVE-2020-13849, which is for the MQTT v3.1.1 protocol itself rather than an implementation, to be addressed. - Using certain listener related configuration options e.g. `cafile`, that apply to the default listener without defining any listener would cause a remotely accessible listener to be opened that was not confined to the local machine but did have anonymous access enabled, contrary to the documentation. This has been fixed. Closes #2283. - CVE-2021-34434: If a plugin had granted ACL subscription access to a durable/non-clean-session client, then removed that access, the client would keep its existing subscription. This has been fixed. - Incoming QoS 2 messages that had not completed the QoS flow were not being checked for ACL access when a clean session=False client was reconnecting. This has been fixed. Broker: - Fix possible out of bounds memory reads when reading a corrupt/crafted configuration file. Unless your configuration file is writable by untrusted users this is not a risk. Closes #567213. - Fix `max_connections` option not being correctly counted. - Fix TLS certificates and TLS-PSK not being able to be configured at the same time. - Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured. - Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections. These clients are now rejected if their keepalive value exceeds max_keepalive. This option allows CVE-2020-13849, which is for the MQTT v3.1.1 protocol itself rather than an implementation, to be addressed. - Fix broker not quiting if e.g. the `password_file` is specified as a directory. Closes #2241. - Fix listener mount_point not being removed on outgoing messages. Closes #2244. - Strict protocol compliance fixes, plus test suite. - Fix $share subscriptions not being recovered for durable clients that reconnect. - Update plugin configuration documentation. Closes #2286. Client library: - If a client uses TLS-PSK then force the default cipher list to use "PSK" ciphers only. This means that a client connecting to a broker configured with x509 certificates only will now fail. Prior to this, the client would connect successfully without verifying certificates, because they were not configured. - Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured. - Threaded mode is deconfigured when the mosquitto_loop_start() thread ends, which allows mosquitto_loop_start() to be called again. Closes #2242. - Fix MOSQ_OPT_SSL_CTX not being able to be set to NULL. Closes #2289. - Fix reconnecting failing when MOSQ_OPT_TLS_USE_OS_CERTS was in use, but none of capath, cafile, psk, nor MOSQ_OPT_SSL_CTX were set, and MOSQ_OPT_SSL_CTX_WITH_DEFAULTS was set to the default value of true. Closes #2288. Apps: - Fix `mosquitto_ctrl dynsec setDefaultACLAccess` command not working. Clients: - mosquitto_sub and mosquitto_rr now open stdout in binary mode on Windows so binary payloads are not modified when printing. - Document TLS certificate behaviour when using `-p 8883`. Build: - Fix installation using WITH_TLS=no. Closes #2281. - Fix builds with libressl 3.4.0. Closes #2198. - Remove some unnecessary code guards related to libressl. - Fix printf format build warning on MIPS. Closes #2271. 2.0.11 - 2021-06-08 =================== Security: - If a MQTT v5 client connects with a crafted CONNECT packet a memory leak will occur. This has been fixed. Broker: - Fix possible crash having just upgraded from 1.6 if `per_listener_settings true` is set, and a SIGHUP is sent to the broker before a client has reconnected to the broker. Closes #2167. - Fix bridge not reconnectng if the first reconnection attempt fails. Closes #2207. - Improve QoS 0 outgoing packet queueing. - Fix non-reachable bridge blocking the broker on Windows. Closes #2172. - Fix possible corruption of pollfd array on Windows when bridges were reconnecting. Closes #2173. - Fix QoS 0 messages not being queued when `queue_qos0_messages` was enabled. Closes #2224. - Fix openssl not being linked to dynamic security plugin. Closes #2277. Clients: - If sending mosquitto_sub output to a pipe, mosquitto_sub will now detect that the pipe has closed and disconnect. Closes #2164. - Fix `mosquitto_pub -l` quitting if a message publication is attempted when the broker is temporarily unavailable. Closes #2187. 2.0.10 - 2021-04-03 ================== Security: - CVE-2021-28166: If an authenticated client connected with MQTT v5 sent a malformed CONNACK message to the broker a NULL pointer dereference occurred, most likely resulting in a segfault. Affects versions 2.0.0 to 2.0.9 inclusive. Broker: - Don't over write new receive-maximum if a v5 client connects and takes over an old session. Closes #2134. - Fix CVE-2021-28166. Closes #2163. Clients: - Set `receive-maximum` to not exceed the `-C` message count in mosquitto_sub and mosquitto_rr, to avoid potentially lost messages. Closes #2134. - Fix TLS-PSK mode not working with port 8883. Closes #2152. Client library: - Fix possible socket leak. This would occur if a client was using `mosquitto_loop_start()`, then if the connection failed due to the remote server being inaccessible they called `mosquitto_loop_stop(, true)` and recreated the mosquitto object. Build: - A variety of minor build related fixes, like functions not having previous declarations. - Fix CMake cross compile builds not finding opensslconf.h. Closes #2160. - Fix build on Solaris non-sparc. Closes #2136. 2.0.9 - 2021-03-11 ================== Security: - If an empty or invalid CA file was provided to the client library for verifying the remote broker, then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes #2130. - If an empty or invalid CA file was provided to the broker for verifying the remote broker for an outgoing bridge connection then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes #2130. Broker: - Fix encrypted bridge connections incorrectly connecting when `bridge_cafile` is empty or invalid. Closes #2130. - Fix `tls_version` behaviour not matching documentation. It was setting the exact TLS version to use, not the minimium TLS version to use. Closes #2110. - Fix messages to `$` prefixed topics being rejected. Closes #2111. - Fix QoS 0 messages not being delivered when max_queued_bytes was configured. Closes #2123. - Fix bridge increasing backoff calculation. - Improve handling of invalid combinations of listener address and bind interface configurations. Closes #2081. - Fix `max_keepalive` option not applying to clients connecting with keepalive set to 0. Closes #2117. Client library: - Fix encrypted connections incorrectly connecting when the CA file passed to `mosquitto_tls_set()` is empty or invalid. Closes #2130. - Fix connections retrying very rapidly in some situations. Build: - Fix cmake epoll detection. 2.0.8 - 2021-02-25 ================== Broker: - Fix incorrect datatypes in `struct mosquitto_evt_tick`. This changes the size and offset of two of the members of this struct, and changes the size of the struct. This is an ABI break, but is considered to be acceptable because plugins should never be allocating their own instance of this struct, and currently none of the struct members are used for anything, so a plugin should not be accessing them. It would also be safe to read/write from the existing struct parameters. - Give compile time warning if libwebsockets compiled without external poll support. Closes #2060. - Fix memory tracking not being available on FreeBSD or macOS. Closes #2096. Client library: - Fix mosquitto_{pub|sub}_topic_check() functions not returning MOSQ_ERR_INVAL on topic == NULL. Clients: - Fix possible loss of data in `mosquitto_pub -l` when sending multiple long lines. Closes #2078. Build: - Provide a mechanism for Docker users to run a broker that doesn't use authentication, without having to provide their own configuration file. Closes #2040. 2.0.7 - 2021-02-04 ================== Broker: - Fix exporting of executable symbols on BSD when building via makefile. - Fix some minor memory leaks on exit only. - Fix possible memory leak on connect. Closes #2057. - Fix openssl engine not being able to load private key. Closes #2066. Clients: - Fix config files truncating options after the first space. Closes #2059. Build: - Fix man page building to not absolutely require xsltproc when using CMake. This now handles the case where we are building from the released tar, or building from git if xsltproc is available, or building from git if xsltproc is not available. 1.6.13 - 2021-02-04 =================== Broker: - Fix crash on Windows if loading a plugin fails. Closes #1866. - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes #1925. Closes #1476. - Fix local bridges being disconnected on SIGHUP. Closes #1942. - Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2 messages. Closes #1968. - Fix listener not being reassociated with client when reloading a persistence file and `per_listener_settings true` is set and the client did not set a username. Closes #1891. - Fix file logging on Windows. Closes #1880. - Fix bridge sock not being removed from sock hash on error. Closes #1897. Client library: - Fix build on Mac Big Sur. Closes #1905. - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes #1925. Closes #1476. Clients: - mosquitto_sub will now quit with an error if the %U option is used on Windows, rather than just quitting. Closes #1908. - Fix config files truncating options after the first space. Closes #2059. Apps: - Perform stricter parsing of input username in mosquitto_passwd. Closes #570126 (Eclipse bugzilla). Build: - Enable epoll support in CMake builds. 2.0.6 - 2021-01-28 ================== Broker: - Fix calculation of remaining length parameter for websockets clients that send fragmented packets. Closes #1974. Broker: - Fix potential duplicate Will messages being sent when a will delay interval has been set. - Fix message expiry interval property not being honoured in `mosquitto_broker_publish` and `mosquitto_broker_publish_copy`. - Fix websockets listeners with TLS not responding. Closes #2020. - Add notes that libsystemd-dev or similar is needed if building with systemd support. Closes #2019. - Improve logging in obscure cases when a client disconnects. Closes #2017. - Fix reloading of listeners where multiple listeners have been defined with the same port but different bind addresses. Closes #2029. - Fix `message_size_limit` not applying to the Will payload. Closes #2022. - The error topic-alias-invalid was being sent if an MQTT v5 client published a message with empty topic and topic alias set, but the topic alias hadn't already been configured on the broker. This has been fixed to send a protocol error, as per section 3.3.4 of the specification. - Note in the man pages that SIGHUP reloads TLS certificates. Closes #2037. - Fix bridges not always connecting on Windows. Closes #2043. Apps: - Allow command line arguments to override config file options in mosquitto_ctrl. Closes #2010. - mosquitto_ctrl: produce an error when requesting a new password if both attempts do not match. Closes #2011. Build: - Fix cmake builds using `WITH_CJSON=no` not working if cJSON not found. Closes #2026. Other: - The SPDX identifiers for EDL-1.0 have been changed to BSD-3-Clause as per The Eclipse legal documentation generator. The licenses are identical. 2.0.5 - 2021-01-11 ================== Broker: - Fix `auth_method` not being provided to the extended auth plugin event. Closes #1975. - Fix large packets not being completely published to slow clients. Closes #1977. - Fix bridge connection not relinquishing POLLOUT after messages are sent. Closes #1979. - Fix apparmor incorrectly denying access to /var/lib/mosquitto/mosquitto.db.new. Closes #1978. - Fix potential intermittent initial bridge connections when using poll(). - Fix `bind_interface` option. Closes #1999. - Fix invalid behaviour in dynsec plugin if a group or client is deleted before a role that was attached to the group or client is deleted. Closes #1998. - Improve logging in dynsec addGroupRole command. Closes #2005. - Improve logging in dynsec addGroupClient command. Closes #2008. Client library: - Improve documentation around the `_v5()` and non-v5 functions, e.g. `mosquitto_publish()` and `mosquitto_publish_v5(). Build: - `install` Makefile target should depend on `all`, not `mosquitto`, to ensure that man pages are always built. Closes #1989. - Fixes for lots of minor build warnings highlighted by Visual Studio. Apps: - Disallow control characters in mosquitto_passwd usernames. - Fix incorrect description in mosquitto_ctrl man page. Closes #1995. - Fix `mosquitto_ctrl dynsec getGroup` not showing roles. Closes #1997. 2.0.4 - 2020-12-22 ================== Broker: - Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2 messages. Closes #1968. - mosquitto_connect_bind_async() and mosquitto_connect_bind_v5() should not reset the bind address option if called with bind_address == NULL. - Fix dynamic security configuration possibly not being reloaded on Windows only. Closes #1962. - Add more log messages for dynsec load/save error conditions. - Fix websockets connections blocking non-websockets connections on Windows. Closes #1934. Build: - Fix man pages not being built when using CMake. Closes #1969. 2.0.3 - 2020-12-17 ================== Security: - Running mosquitto_passwd with the following arguments only `mosquitto_passwd -b password_file username password` would cause the username to be used as the password. Broker: - Fix excessive CPU use on non-Linux systems when the open file limit is set high. Closes #1947. - Fix LWT not being sent on client takeover when the existing session wasn't being continued. Closes #1946. - Fix bridges possibly not completing connections when WITH_ADNS is in use. Closes #1960. - Fix QoS 0 messages not being delivered if max_queued_messages was set to 0. Closes #1956. - Fix local bridges being disconnected on SIGHUP. Closes #1942. - Fix slow initial bridge connections for WITH_ADNS=no. - Fix persistence_location not appending a '/'. Clients: - Fix mosquitto_sub being unable to terminate with Ctrl-C if a successful connection is not made. Closes #1957. Apps: - Fix `mosquitto_passwd -b` using username as password (not if `-c` is also used). Closes #1949. Build: - Fix `install` target when using WITH_CJSON=no. Closes #1938. - Fix `generic` docker build. Closes #1945. 2.0.2 - 2020-12-10 ================== Broker: - Fix build regression for WITH_WEBSOCKETS=yes on non-Linux systems. 2.0.1 - 2020-12-10 ================== Broker: - Fix websockets connections on Windows blocking subsequent connections. Closes #1934. - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes #1925. Closes #1476. - Fix websockets listeners not causing the main loop not to wake up. Closes #1936. Client library: - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes #1925. Closes #1476. Apps: - Fix `mosquitto_passwd -U` Build: - Fix cjson include paths. - Fix build using WITH_TLS=no when the openssl headers aren't available. - Distribute cmake/ and snap/ directories in tar. 2.0.0 - 2020-12-03 ================== Breaking changes: - When the Mosquitto broker is run without configuring any listeners it will now bind to the loopback interfaces 127.0.0.1 and/or ::1. This means that only connections from the local host will be possible. Running the broker as `mosquitto` or `mosquitto -p 1883` will bind to the loopback interface. Running the broker with a configuration file with no listeners configured will bind to the loopback interface with port 1883. Running the broker with a listener defined will bind by default to `0.0.0.0` / `::` and so will be accessible from any interface. It is still possible to bind to a specific address/interface. If the broker is run as `mosquitto -c mosquitto.conf -p 1884`, and a listener is defined in the configuration file, then the port defined on the command line will be IGNORED, and no listener configured for it. - All listeners now default to `allow_anonymous false` unless explicitly set to true in the configuration file. This means that when configuring a listener the user must either configure an authentication and access control method, or set `allow_anonymous true`. When the broker is run without a configured listener, and so binds to the loopback interface, anonymous connections are allowed. - If Mosquitto is run on as root on a unix like system, it will attempt to drop privileges as soon as the configuration file has been read. This is in contrast to the previous behaviour where elevated privileges were only dropped after listeners had been started (and hence TLS certificates loaded) and logging had been started. The change means that clients will never be able to connect to the broker when it is running as root, unless the user explicitly sets it to run as root, which is not advised. It also means that all locations that the broker needs to access must be available to the unprivileged user. In particular those people using TLS certificates from Lets Encrypt will need to do something to allow Mosquitto to access those certificates. An example deploy renewal hook script to help with this is at `misc/letsencrypt/mosquitto-copy.sh`. The user that Mosquitto will change to are the one provided in the configuration, `mosquitto`, or `nobody`, in order of availability. - The `pid_file` option will now always attempt to write a pid file, regardless of whether the `-d` argument is used when running the broker. - The `tls_version` option now defines the *minimum* TLS protocol version to be used, rather than the exact version. Closes #1258. - The `max_queued_messages` option has been increased from 100 to 1000 by default, and now also applies to QoS 0 messages, when a client is connected. - The mosquitto_sub, mosquitto_pub, and mosquitto_rr clients will now load OS provided CA certificates by default if `-L mqtts://...` is used, or if the port is set to 8883 and no other CA certificates are loaded. - Minimum support libwebsockets version is now 2.4.0 - The license has changed from "EPL-1.0 OR EDL-1.0" to "EPL-2.0 OR EDL-1.0". Broker features: - New plugin interface which is more flexible, easier to develop for and easier to extend. - New dynamic security plugin, which allows clients, groups, and roles to be defined and updated as the broker is running. - Performance improvements, particularly for higher numbers of clients. - When running as root, if dropping privileges to the "mosquitto" user fails, then try "nobody" instead. This reduces the burden on users installing Mosquitto themselves. - Add support for Unix domain socket listeners. - Add `bridge_outgoing_retain` option, to allow outgoing messages from a bridge to have the retain bit completely disabled, which is useful when bridging to e.g. Amazon or Google. - Add support for MQTT v5 bridges to handle the "retain-available" property being false. - Allow MQTT v5.0 outgoing bridges to fall back to MQTT v3.1.1 if connecting to a v3.x only broker. - DLT logging is now configurable at runtime with `log_dest dlt`. Closes #1735. - Add `mosquitto_broker_publish()` and `mosquitto_broker_publish_copy()` functions, which can be used by plugins to publish messages. - Add `mosquitto_client_protocol_version()` function which can be used by plugins to determine which version of MQTT a client has connected with. - Add `mosquitto_kick_client_by_clientid()` and `mosquitto_kick_client_by_username()` functions, which can be used by plugins to disconnect clients. - Add support for handling $CONTROL/ topics in plugins. - Add support for PBKDF2-SHA512 password hashing. - Enabling certificate based TLS encryption is now through certfile and keyfile, not capath or cafile. - Added support for controlling UNSUBSCRIBE calls in v5 plugin ACL checks. - Add "deny" acl type. Closes #1611. - The broker now sends the receive-maximum property for MQTT v5 CONNACKs. - Add the `bridge_max_packet_size` option. Closes #265. - Add the `bridge_bind_address` option. Closes #1311. - TLS certificates for the server are now reloaded on SIGHUP. - Default for max_queued_messages has been changed to 1000. - Add `ciphers_tls1.3` option, to allow setting TLS v1.3 ciphersuites. Closes #1825. - Bridges now obey MQTT v5 server-keepalive. - Add bridge support for the MQTT v5 maximum-qos property. - Log client port on new connections. Closes #1911. Broker fixes: - Send DISCONNECT with `malformed-packet` reason code on invalid PUBLISH, SUBSCRIBE, and UNSUBSCRIBE packets. - Document that X509_free() must be called after using mosquitto_client_certificate(). Closes #1842. - Fix listener not being reassociated with client when reloading a persistence file and `per_listener_settings true` is set and the client did not set a username. Closes #1891. - Fix bridge sock not being removed from sock hash on error. Closes #1897. - mosquitto_password now forbids the : character. Closes #1833. - Fix `log_timestamp_format` not applying to `log_dest topic`. Closes #1862. - Fix crash on Windows if loading a plugin fails. Closes #1866. - Fix file logging on Windows. Closes #1880. - Report an error if the config file is set to a directory. Closes #1814. - Fix bridges incorrectly setting Wills to manage remote notifications when `notifications_local_only` was set true. Closes #1902. Client library features: - Client no longer generates random client ids for v3.1.1 clients, these are now expected to be generated on the broker. This matches the behaviour for v5 clients. Closes #291. - Add support for connecting to brokers through Unix domain sockets. - Add `mosquitto_property_identifier()`, for retrieving the identifier integer for a property. - Add `mosquitto_property_identifier_to_string()` for converting a property identifier integer to the corresponding property name string. - Add `mosquitto_property_next()` to retrieve the next property in a list, for iterating over property lists. - mosquitto_pub now handles the MQTT v5 retain-available property by never setting the retain bit. - Added MOSQ_OPT_TCP_NODELAY, to allow disabling Nagle's algorithm on client sockets. Closes #1526. - Add `mosquitto_ssl_get()` to allow clients to access their SSL structure and perform additional verification. - Add MOSQ_OPT_BIND_ADDRESS to allow setting of a bind address independently of the `mosquitto_connect*()` call. - Add `MOSQ_OPT_TLS_USE_OS_CERTS` option, to instruct the client to load and trust OS provided CA certificates for use with TLS connections. Client library fixes: - Fix send quota being incorrecly reset on reconnect. Closes #1822. - Don't use logging until log mutex is initialised. Closes #1819. - Fix missing mach/mach_time.h header on OS X. Closes #1831. - Fix connect properties not being sent when the client automatically reconnects. Closes #1846. Client features: - Add timeout return code (27) for `mosquitto_sub -W ` and `mosquitto_rr -W `. Closes #275. - Add support for connecting to brokers through Unix domain sockets with the `--unix` argument. - Use cJSON library for producing JSON output, where available. Closes #1222. - Add support for outputting MQTT v5 property information to mosquitto_sub/rr JSON output. Closes #1416. - Add `--pretty` option to mosquitto_sub/rr for formatted/unformatted JSON output. - Add support for v5 property printing to mosquitto_sub/rr in non-JSON mode. Closes #1416. - Add `--nodelay` to all clients to allow them to use the MOSQ_OPT_TCP_NODELAY option. - Add `-x` to all clients to all the session-expiry-interval property to be easily set for MQTT v5 clients. - Add `--random-filter` to mosquitto_sub, to allow only a certain proportion of received messages to be printed. - mosquitto_sub %j and %J timestamps are now in a ISO 8601 compatible format. - mosquitto_sub now supports extra format specifiers for field width and precision for some parameters. - Add `--version` for all clients. - All clients now load OS provided CA certificates if used with `-L mqtts://...`, or if port is set to 8883 and no other CA certificates are used. Closes #1824. - Add the `--tls-use-os-certs` option to all clients. Client fixes: - mosquitto_sub will now exit if all subscriptions were denied. - mosquitto_pub now sends 0 length files without an error when using `-f`. - Fix description of `-e` and `-t` arguments in mosquitto_rr. Closes #1881. - mosquitto_sub will now quit with an error if the %U option is used on Windows, rather than just quitting. Closes #1908. 1.6.12 - 2020-08-19 =================== Security: - In some circumstances, Mosquitto could leak memory when handling PUBLISH messages. This is limited to incoming QoS 2 messages, and is related to the combination of the broker having persistence enabled, a clean session=false client, which was connected prior to the broker restarting, then has reconnected and has now sent messages at a sufficiently high rate that the incoming queue at the broker has filled up and hence messages are being dropped. This is more likely to have an effect where max_queued_messages is a small value. This has now been fixed. Closes #1793. Broker: - Build warning fixes when building with WITH_BRIDGE=no and WITH_TLS=no. Clients: - All clients exit with an error exit code on CONNACK failure. Closes #1778. - Don't busy loop with `mosquitto_pub -l` on a slow connection. 1.5.10 - 2020-08-19 =================== Security: - In some circumstances, Mosquitto could leak memory when handling PUBLISH messages. This is limited to incoming QoS 2 messages, and is related to the combination of the broker having persistence enabled, a clean session=false client, which was connected prior to the broker restarting, then has reconnected and has now sent messages at a sufficiently high rate that the incoming queue at the broker has filled up and hence messages are being dropped. This is more likely to have an effect where max_queued_messages is a small value. This has now been fixed. Closes #1793. 1.6.11 - 2020-08-11 =================== Security: - On Windows the Mosquitto service was being installed without appropriate path quoting, this has been fixed. Broker: - Fix usage message only mentioning v3.1.1. Closes #1713. - Fix broker refusing to start if only websockets listeners were defined. Closes #1740. - Change systemd unit files to create /var/log/mosquitto before starting. Closes #821. - Don't quit with an error if opening the log file isn't possible. Closes #821. - Fix bridge topic remapping when using "" as the topic. Closes #1749. - Fix messages being queued for disconnected bridges when clean start was set to true. Closes #1729. - Fix `autosave_interval` not being triggered by messages being delivered. Closes #1726. - Fix websockets clients sometimes not being disconnected promptly. Closes #1718. - Fix "slow" file based logging by switching to line based buffering. Closes #1689. Closes #1741. - Log protocol error message where appropriate from a bad UNSUBSCRIBE, rather than the generic "socket error". - Don't try to start DLT logging if DLT unavailable, to avoid a long delay when shutting down the broker. Closes #1735. - Fix potential memory leaks. Closes #1773. Closes #1774. - Fix clients not receiving messages after a previous client with the same client ID and positive will delay interval quit. Closes #1752. - Fix overly broad HAVE_PTHREAD_CANCEL compile guard. Closes #1547. Client library: - Improved documentation around connect callback return codes. Close #1730. - Fix `mosquitto_publish*()` no longer returning `MOSQ_ERR_NO_CONN` when not connected. Closes #1725. - `mosquitto_loop_start()` now sets a thread name on Linux, FreeBSD, NetBSD, and OpenBSD. Closes #1777. - Fix `mosquitto_loop_stop()` not stopping on Windows. Closes #1748. Closes #117. 1.6.10 - 2020-05-25 =================== Broker: - Report invalid bridge prefix+pattern combinations at config parsing time rather than letting the bridge fail later. Issue #1635. - Fix `mosquitto_passwd -b` not updating passwords for existing users correctly. Creating a new user with `-b` worked without problem. Closes #1664. - Fix memory leak when connecting clients rejected. - Don't disconnect clients that are already disconnected. This prevents the session expiry being extended on SIGHUP. Closes #1521. - Fix support for openssl 3.0. - Fix check when loading persistence file of a different version than the native version. Closes #1684. - Fix possible assert crash associated with bridge reconnecting when compiled without epoll support. Closes #1700. Client library: - Don't treat an unexpected PUBACK, PUBREL, or PUBCOMP as a fatal error. Issue #1629. - Fix support for openssl 3.0. - Fix memory leaks from multiple calls to `mosquitto_lib_init()`/`mosquitto_lib_cleanup()`. Closes #1691. - Fix documentation on return code of `mosquitto_lib_init()` for Windows. Closes #1690. Clients: - Fix mosquitto_sub %j or %J not working on Windows. Closes #1674. Build: - Various fixes for building with user not being freed on exit. Closes #1564. - Fix trailing whitespace not being trimmed on acl users. Closes #1539. - Fix `bind_interface` not working for the default listener. Closes #1533. - Improve password file parsing in the broker and mosqitto_passwd. Closes #1584. - Print OpenSSL errors in more situations, like when loading certificates fails. Closes #1552. - Fix `mosquitto_client_protocol() returning incorrect values. Client library: - Set minimum keepalive argument to `mosquitto_connect*()` to be 5 seconds. Closes #1550. - Fix `mosquitto_topic_matches_sub()` not returning MOSQ_ERR_INVAL if the topic contains a wildcard. Closes #1589. Clients: - Fix `--remove-retained` not obeying the `-T` option for filtering out topics. Closes #1585. - Default behaviour for v5 clients using `-c` is now to use infinite length sessions, as with v3 clients. Closes #1546. 1.6.8 - 20191128 ================ Broker: - Various fixes for `allow_zero_length_clientid` config, where this option was not being set correctly. Closes #1429. - Fix incorrect memory tracking causing problems with memory_limit option. Closes #1437. - Fix subscription topics being limited to 200 characters instead of 200 hierarchy levels. Closes #1441. - Only a single CRL could be loaded at once. This has been fixed. Closes #1442. - Fix problems with reloading config when `per_listener_settings` was true. Closes #1459. - Fix retained messages with an expiry interval not being expired after being restored from persistence. Closes #1464. - Fix messages with an expiry interval being sent without an expiry interval property just before they were expired. Closes #1464. - Fix TLS Websockets clients not receiving messages after taking over a previous connection. Closes #1489. - Fix MQTT 3.1.1 clients using clean session false, or MQTT 5.0 clients using session-expiry-interval set to infinity never expiring, even when the global `persistent_client_expiration` option was set. Closes #1494. Client library: - Fix publish properties not being passed to on_message_v5 callback for QoS 2 messages. Closes #1432. - Fix documentation issues in mosquitto.h. Closes #1478. - Document `mosquitto_connect_srv()`. Closes #1499. Clients: - Fix duplicate cfg definition in rr_client. Closes #1453. - Fix `mosquitto_pub -l` hang when stdin stream ends. Closes #1448. - Fix `mosquitto_pub -l` not sending the final line of stdin if it does not end with a new line. Closes #1473. - Make documentation for `mosquitto_pub -l` match reality - blank lines are sent as empty messages. Closes #1474. - Free memory in `mosquitto_sub` when quiting without having made a successful connection. Closes #1513. Build: - Added `CLIENT_STATIC_LDADD` to makefile builds to allow more libraries to be linked when compiling the clients with a static libmosquitto, as required for e.g. openssl on some systems. Installer: - Fix mosquitto_rr.exe not being included in Windows installers. Closes #1463. 1.6.7 - 20190925 ================ Broker: - Add workaround for working with libwebsockets 3.2.0. - Fix potential crash when reloading config. Closes #1424, #1425. Client library: - Don't use `/` in autogenerated client ids, to avoid confusing with topics. - Fix `mosquitto_max_inflight_messages_set()` and `mosquitto_int_option(..., MOSQ_OPT_*_MAX, ...)` behaviour. Closes #1417. - Fix regression on use of `mosquitto_connect_async()` not working. Closes #1415 and #1422. Clients: - mosquitto_sub: Fix `-E` incorrectly not working unless `-d` was also specified. Closes #1418. - Updated documentation around automatic client ids. 1.6.6 - 20190917 ================ Security: - Restrict topic hierarchy to 200 levels to prevent possible stack overflow. Closes #1412. Broker: - Restrict topic hierarchy to 200 levels to prevent possible stack overflow. Closes #1412. - mosquitto_passwd now returns 1 when attempting to update a user that does not exist. Closes #1414. 1.6.5 - 20190912 ================ Broker: - Fix v5 DISCONNECT packets with remaining length == 2 being treated as a protocol error. Closes #1367. - Fix support for libwebsockets 3.x. - Fix slow websockets performance when sending large messages. Closes #1390. - Fix bridges potentially not connecting on Windows. Closes #478. - Fix clients authorised using `use_identity_as_username` or `use_subject_as_username` being disconnected on SIGHUP. Closes #1402. - Improve error messages in some situations when clients disconnect. Reduces the number of "Socket error on client X, disconnecting" messages. - Fix Will for v5 clients not being sent if will delay interval was greater than the session expiry interval. Closes #1401. - Fix CRL file not being reloaded on HUP. Closes #35. - Fix repeated "Error in poll" messages on Windows when only websockets listeners are defined. Closes #1391. Client library: - Fix reconnect backoff for the situation where connections are dropped rather than refused. Closes #737. - Fix missing locks on `mosq->state`. Closes #1374. Documentation: - Improve details on global/per listener options in the mosquitto.conf man page. Closes #274. - Clarify behaviour when clients exceed the `message_size_limit`. Closes #448. - Improve documentation for `max_inflight_bytes`, `max_inflight_messages`, and `max_queued_messages`. Build: - Fix missing function warnings on NetBSD. - Fix WITH_STATIC_LIBRARIES using CMake on Windows. Closes #1369. - Guard ssize_t definition on Windows. Closes #522. 1.6.4 - 20190801 ================ Broker: - Fix persistent clients being incorrectly expired on Raspberry Pis. Closes #1272. - Windows: Allow other applications access to the log file when running. Closes #515. - Fix incoming QoS 2 messages being blocked when `max_inflight_messages` was set to 1. Closes #1332. - Fix incoming messages not being removed for a client if the topic being published to does not have any subscribers. Closes #1322. Client library: - Fix MQTT v5 subscription options being incorrectly set for MQTT v3 subscriptions. Closes #1353. - Make behaviour of `mosquitto_connect_async()` consistent with `mosquitto_connect()` when connecting to a non-existent server. Closes #1345. - `mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, ...)` was incorrectly returning `MOSQ_ERR_INVAL` with valid input. This has been fixed. Closes #1360. - on_connect callback is now called with the correct v5 reason code if a v5 client connects to a v3.x broker and is sent a CONNACK with the "unacceptable protocol version" connack reason code. - Fix memory leak when setting v5 properties in mosquitto_connect_v5(). - Fix properties not being sent on QoS>0 PUBLISH messages. Clients: - mosquitto_pub: fix error codes not being returned when mosquitto_pub exits. Closes #1354. - All clients: improve error messages when connecting to a v3.x broker when in v5 mode. Closes #1344. Other: - Various documentation fixes. 1.6.3 - 20190618 ================ Broker: - Fix detection of incoming v3.1/v3.1.1 bridges. Closes #1263. - Fix default max_topic_alias listener config not being copied to the in-use listener when compiled without TLS support. - Fix random number generation if compiling using `WITH_TLS=no` and on Linux with glibc >= 2.25. Without this fix, no random numbers would be generated for e.g. on broker client id generation, and so clients connecting expecting this feature would be unable to connect. - Fix compilation problem related to `getrandom()` on non-glibc systems. - Fix Will message for a persistent client incorrectly being sent when the client reconnects after a clean disconnect. Closes #1273. - Fix Will message for a persistent client not being sent on disconnect. Closes #1273. - Improve documentation around the upgrading of persistence files. Closes #1276. - Add 'extern "C"' on mosquitto_broker.h and mosquitto_plugin.h for C++ plugin writing. Closes #1290. - Fix persistent Websockets clients not receiving messages after they reconnect, having sent DISCONNECT on a previous session. Closes #1227. - Disable TLS renegotiation. Client initiated renegotiation is considered to be a potential attack vector against servers. Closes #1257. - Fix incorrect shared subscription topic '$shared'. - Fix zero length client ids being rejected for MQTT v5 clients with clean start set to true. - Fix MQTT v5 overlapping subscription behaviour. Clients now receive message from all matching subscriptions rather than the first one encountered, which ensures the maximum QoS requirement is met. - Fix incoming/outgoing quota problems for QoS>0. - Remove obsolete `store_clean_interval` from documentation. - Fix v4 authentication plugin never calling psk_key_get. Client library: - Fix typo causing build error on Windows when building without TLS support. Closes #1264. Clients: - Fix -L url parsing when `/topic` part is missing. - Stop some error messages being printed even when `--quiet` was used. Closes #1284. - Fix mosquitto_pub exiting with error code 0 when an error occurred. Closes #1285. - Fix mosquitto_pub not using the `-c` option. Closes #1273. - Fix MQTT v5 clients not being able to specify a password without a username. Closes #1274. - Fix `mosquitto_pub -l` not handling network failures. Closes #1152. - Fix `mosquitto_pub -l` not handling zero length input. Closes #1302. - Fix double free on exit in mosquitto_pub. Closes #1280. Documentation: - Remove references to Python binding and C++ wrapper in libmosquitto man page. Closes #1266. Build: - CLIENT_LDFLAGS now uses LDFLAGS. Closes #1294. 1.6.2 - 20190430 ================ Broker: - Fix memory access after free, leading to possible crash, when v5 client with Will message disconnects, where the Will message has as its first property one of `content-type`, `correlation-data`, `payload-format-indicator`, or `response-topic`. Closes #1244. - Fix build for WITH_TLS=no. Closes #1250. - Fix Will message not allowing user-property properties. - Fix broker originated messages (e.g. $SYS/broker/version) not being published when `check_retain_source` set to true. Closes #1245. - Fix $SYS/broker/version being incorrectly expired after 60 seconds. Closes #1245. Library: - Fix crash after client has been unable to connect to a broker. This occurs when the client is exiting and is part of the final library cleanup routine. Closes #1246. Clients: - Fix -L url parsing. Closes #1248. 1.6.1 - 20190426 ================ Broker: - Document `memory_limit` option. Clients: - Fix compilation on non glibc systems due to missing sys/time.h header. Build: - Add `make check` target and document testing procedure. Closes #1230. - Document bundled dependencies and how to disable. Closes #1231. - Split CFLAGS and CPPFLAGS, and LDFLAGS and LDADD/LIBADD. - test/unit now respects CPPFLAGS and LDFLAGS. Closes #1232. - Don't call ldconfig in CMake scripts. Closes #1048. - Use CMAKE_INSTALL_* variables when installing in CMake. Closes #1049. 1.6 - 20190417 ============== Broker features: - Add support for MQTT v5 - Add support for OCSP stapling. - Add support for ALPN on bridge TLS connections. Closes #924. - Add support for Automotive DLT logging. - Add TLS Engine support. - Persistence file read/write performance improvements. - General performance improvements. - Add max_keepalive option, to allow a maximum keepalive value to be set for MQTT v5 clients only. - Add `bind_interface` option which allows a listener to be bound to a specific network interface, in a similar fashion to the `bind_address` option. Linux only. - Add improved bridge restart interval based on Decorrelated Jitter. - Add `dhparamfile` option, to allow DH parameters to be loaded for Ephemeral DH support - Disallow writing to $ topics where appropriate. - Fix mosquitto_passwd crashing on corrupt password file. Closes #1207. - Add explicit support for TLS v1.3. - Drop support for TLS v1.0. - Improved general support for broker generated client ids. Removed libuuid dependency. - auto_id_prefix now defaults to 'auto-'. - QoS 1 and 2 flow control improvements. Client library features: - Add support for MQTT v5 - Add mosquitto_subscribe_multiple() for sending subscriptions to multiple topics in one command. - Add TLS Engine support. - Add explicit support for TLS v1.3. - Drop support for TLS v1.0. - QoS 1 and 2 flow control improvements. Client features: - Add support for MQTT v5 - Add mosquitto_rr client, which can be used for "request-response" messaging, by sending a request message and awaiting a response. - Add TLS Engine support. - Add support for ALPN on TLS connections. Closes #924. - Add -D option for all clients to specify MQTT v5 properties. - Add -E to mosquitto_sub, which causes it to exit immediately after having its subscriptions acknowledged. Use with -c to create a durable client session without requiring a message to be received. - Add --remove-retained to mosquitto_sub, which can be used to clear retained messages on a broker. - Add --repeat and --repeat-delay to mosquitto_pub, which can be used to repeat single message publishes at a regular interval. - -V now accepts `5, `311`, `31`, as well as `mqttv5` etc. - Add explicit support for TLS v1.3. - Drop support for TLS v1.0. Broker fixes: - Improve error reporting when creating listeners. - Fix build on SmartOS due to missing IPV6_V6ONLY. Closes #1212. Client library fixes - Add missing `mosquitto_userdata()` function. Client fixes: - mosquitto_pub wouldn't always publish all messages when using `-l` and QoS>0. This has been fixed. - mosquitto_sub was incorrectly encoding special characters when using %j output format. Closes #1220. 1.5.8 - 20190228 ================ Broker: - Fix clients being disconnected when ACLs are in use. This only affects the case where a client connects using a username, and the anonymous ACL list is defined but specific user ACLs are not defined. Closes #1162. - Make error messages for missing config file clearer. - Fix some Coverity Scan reported errors that could occur when the broker was already failing to start. - Fix broken mosquitto_passwd on FreeBSD. Closes #1032. - Fix delayed bridge local subscriptions causing missing messages. Closes #1174. Library: - Use higher resolution timer for random initialisation of client id generation. Closes #1177. - Fix some Coverity Scan reported errors that could occur when the library was already quitting. 1.5.7 - 20190213 ================ Broker: - Fix build failure when using WITH_ADNS=yes - Ensure that an error occurs if `per_listener_settings true` is given after other security options. Closes #1149. - Fix include_dir not sorting config files before loading. This was partially fixed in 1.5 previously. - Improve documentation around the `include_dir` option. Closes #1154. - Fix case where old unreferenced msg_store messages were being saved to the persistence file, bloating its size unnecessarily. Closes #389. Library: - Fix `mosquitto_topic_matches_sub()` not returning MOSQ_ERR_INVAL for invalid subscriptions like `topic/#abc`. This only affects the return value, not the match/no match result, which was already correct. Build: - Don't require C99 compiler. - Add rewritten build test script and remove some build warnings. 1.5.6 - 20190206 ================ Security: - CVE-2018-12551: If Mosquitto is configured to use a password file for authentication, any malformed data in the password file will be treated as valid. This typically means that the malformed data becomes a username and no password. If this occurs, clients can circumvent authentication and get access to the broker by using the malformed username. In particular, a blank line will be treated as a valid empty username. Other security measures are unaffected. Users who have only used the mosquitto_passwd utility to create and modify their password files are unaffected by this vulnerability. Affects version 1.0 to 1.5.5 inclusive. - CVE-2018-12550: If an ACL file is empty, or has only blank lines or comments, then mosquitto treats the ACL file as not being defined, which means that no topic access is denied. Although denying access to all topics is not a useful configuration, this behaviour is unexpected and could lead to access being incorrectly granted in some circumstances. This is now fixed. Affects versions 1.0 to 1.5.5 inclusive. - CVE-2018-12546. If a client publishes a retained message to a topic that they have access to, and then their access to that topic is revoked, the retained message will still be delivered to future subscribers. This behaviour may be undesirable in some applications, so a configuration option `check_retain_source` has been introduced to enforce checking of the retained message source on publish. Broker: - Fixed comment handling for config options that have optional arguments. - Improved documentation around bridge topic remapping. - Handle mismatched handshakes (e.g. QoS1 PUBLISH with QoS2 reply) properly. - Fix spaces not being allowed in the bridge remote_username option. Closes #1131. - Allow broker to always restart on Windows when using `log_dest file`. Closes #1080. - Fix Will not being sent for Websockets clients. Closes #1143. - Windows: Fix possible crash when client disconnects. Closes #1137. - Fixed durable clients being unable to receive messages when offline, when per_listener_settings was set to true. Closes #1081. - Add log message for the case where a client is disconnected for sending a topic with invalid UTF-8. Closes #1144. Library: - Fix TLS connections not working over SOCKS. - Don't clear SSL context when TLS connection is closed, meaning if a user provided an external SSL_CTX they have less chance of leaking references. Build: - Fix comparison of boolean values in CMake build. Closes #1101. - Fix compilation when openssl deprecated APIs are not available. Closes #1094. - Man pages can now be built on any system. Closes #1139. 1.5.5 - 20181211 ================ Security: - If `per_listener_settings` is set to true, then the `acl_file` setting was ignored for the "default listener" only. This has been fixed. This does not affect any listeners defined with the `listener` option. Closes #1073. This is now tracked as CVE-2018-20145. Broker: - Add `socket_domain` option to allow listeners to disable IPv6 support. This is required to work around a problem in libwebsockets that means sockets only listen on IPv6 by default if IPv6 support is compiled in. Closes #1004. - When using ADNS, don't ask for all network protocols when connecting, because this can lead to confusing "Protocol not supported" errors if the network is down. Closes #1062. - Fix outgoing retained messages not being sent by bridges on initial connection. Closes #1040. - Don't reload auth_opt_ options on reload, to match the behaviour of the other plugin options. Closes #1068. - Print message on error when installing/uninstalling as a Windows service. - All non-error connect/disconnect messages are controlled by the `connection_messages` option. Closes #772. Closes #613. Closes #537. Library: - Fix reconnect delay backoff behaviour. Closes #1027. - Don't call on_disconnect() twice if keepalive tests fail. Closes #1067. Client: - Always print leading zeros in mosquitto_sub when output format is hex. Closes #1066. Build: - Fix building where TLS-PSK is not available. Closes #68. 1.5.4 - 20181108 ================ Security: - When using a TLS enabled websockets listener with "require_certificate" enabled, the mosquitto broker does not correctly verify client certificates. This is now fixed. All other security measures operate as expected, and in particular non-websockets listeners are not affected by this. Closes #996. Broker: - Process all pending messages even when a client has disconnected. This means a client that send a PUBLISH then DISCONNECT quickly, then disconnects will have its DISCONNECT message processed properly and so no Will will be sent. Closes #7. - $SYS/broker/clients/disconnected should never be negative. Closes #287. - Give better error message if a client sends a password without a username. Closes #1015. - Fix bridge not honoring restart_timeout. Closes #1019. - Don't disconnect a client if an auth plugin denies access to SUBSCRIBE. Closes #1016. Library: - Fix memory leak that occurred if mosquitto_reconnect() was used when TLS errors were present. Closes #592. - Fix TLS connections when using an external event loop with mosquitto_loop_read() and mosquitto_write(). Closes #990. Build: - Fix clients not being compiled with threading support when using CMake. Closes #983. - Header fixes for FreeBSD. Closes #977. - Use _GNU_SOURCE to fix build errors in websockets and getaddrinfo usage. Closes #862 and #933. - Fix builds on QNX 7.0.0. Closes #1018. 1.5.3 - 20180925 ================ Security: - Fix CVE-2018-12543. If a message is sent to Mosquitto with a topic that begins with $, but is not $SYS, then an assert that should be unreachable is triggered and Mosquitto will exit. Broker: - Elevate log level to warning for situation when socket limit is hit. - Remove requirement to use `user root` in snap package config files. - Fix retained messages not sent by bridges on outgoing topics at the first connection. Closes #701. - Documentation fixes. Closes #520, #600. - Fix duplicate clients being added to by_id hash before the old client was removed. Closes #645. - Fix Windows version not starting if include_dir did not contain any files. Closes #566. - When an authentication plugin denied access to a SUBSCRIBE, the client would be disconnected incorrectly. This has been fixed. Closes #1016. Build: - Various fixes to ease building. 1.5.2 - 20180919 ================ Broker: - Fix build when using WITH_ADNS=yes. - Fix incorrect call to setsockopt() for TCP_NODELAY. Closes #941. - Fix excessive CPU usage when the number of sockets exceeds the system limit. Closes #948. - Fix for bridge connections when using WITH_ADNS=yes. - Fix round_robin false behaviour. Closes #481. - Fix segfault on HUP when bridges and security options are configured. Closes #965. Library: - Fix situation where username and password is used with SOCKS5 proxy. Closes #927. - Fix SOCKS5 behaviour when passing IP addresses. Closes #927. Build: - Make it easier to build without bundled uthash.h using "WITH_BUNDLED_DEPS=no". - Fix build with OPENSSL_NO_ENGINE. Closes #932. 1.5.1 - 20180816 ================ Broker: - Fix plugin cleanup function not being called on exit of the broker. Closes #900. - Print more OpenSSL errors when loading certificates/keys fail. - Use AF_UNSPEC etc. instead of PF_UNSPEC to comply with POSIX. Closes #863. - Remove use of AI_ADDRCONFIG, which means the broker can be used on systems where only the loopback interface is defined. Closes #869, Closes #901. - Fix IPv6 addresses not being able to be used as bridge addresses. Closes #886. - All clients now time out if they exceed their keepalive*1.5, rather than just reach it. This was inconsistent in two places. - Fix segfault on startup if bridge CA certificates could not be read. Closes #851. - Fix problem opening listeners on Pi caused by unsigned char being default. Found via #849. - ACL patterns that do not contain either %c or %u now produce a warning in the log. Closes #209. - Fix bridge publishing failing when per_listener_settings was true. Closes #860. - Fix `use_identity_as_username true` not working. Closes #833. - Fix UNSUBACK messages not being logged. Closes #903. - Fix possible endian issue when reading the `memory_limit` option. - Fix building for libwebsockets < 1.6. - Fix accessor functions for username and client id when used in plugin auth check. Library: - Fix some places where return codes were incorrect, including to the on_disconnect() callback. This has resulted in two new error codes, MOSQ_ERR_KEEPALIVE and MOSQ_ERR_LOOKUP. - Fix connection problems when mosquitto_loop_start() was called before mosquitto_connect_async(). Closes #848. Clients: - When compiled using WITH_TLS=no, the default port was incorrectly being set to -1. This has been fixed. - Fix compiling on Mac OS X <10.12. Closes #813 and #240. Build: - Fixes for building on NetBSD. Closes #258. - Fixes for building on FreeBSD. - Add support for compiling with static libwebsockets library. 1.5 - 20180502 ============== Security: - Fix memory leak that could be caused by a malicious CONNECT packet. CVE-2017-7654. Closes #533493 (on Eclipse bugtracker) Broker features: - Add per_listener_settings to allow authentication and access control to be per listener. - Add limited support for reloading listener settings. This allows settings for an already defined listener to be reloaded, but port numbers must not be changed. - Add ability to deny access to SUBSCRIBE messages as well as the current read/write accesses. Currently for auth plugins only. - Reduce calls to malloc through the use of UHPA. - Outgoing messages with QoS>1 are no longer retried after a timeout period. Messages will be retried when a client reconnects. This change in behaviour can be justified by considering when the timeout may have occurred. * If a connection is unreliable and has dropped, but without one end noticing, the messages will be retried on reconnection. Sending additional PUBLISH or PUBREL would not have changed anything. * If a client is overloaded/unable to respond/has a slow connection then sending additional PUBLISH or PUBREL would not help the client catch up. Once the backlog has cleared the client will respond. If it is not able to catch up, sending additional duplicates would not help either. - Add use_subject_as_username option for certificate based client authentication to use the entire certificate subject as a username, rather than just the CN. Closes #469467. - Change sys tree printing output. This format shouldn't be relied upon and may change at any time. Closes #470246. - Minimum supported libwebsockets version is now 1.3. - Add systemd startup notification and services. Closes #471053. - Reduce unnecessary malloc and memcpy when receiving a message and storing it. Closes #470258. - Support for Windows XP has been dropped. - Bridge connections now default to using MQTT v3.1.1. - mosquitto_db_dump tool can now output some stats on clients. - Perform utf-8 validation on incoming will, subscription and unsubscription topics. - new $SYS/broker/store/messages/count (deprecates $SYS/broker/messages/stored) - new $SYS/broker/store/messages/bytes - max_queued_bytes feature to limit queues by real size rather than than just message count. Closes Eclipse #452919 or Github #100 - Add support for bridges to be configured to only send notifications to the local broker. - Add set_tcp_nodelay option to allow Nagle's algorithm to be disabled on client sockets. Closes #433. - The behaviour of allow_anonymous has changed. In the old behaviour, the default if not set was to allow anonymous access. The new behaviour is to default is to allow anonymous access unless another security option is set. For example, if password_file is set and allow_anonymous is not set, then anonymous access will be denied. It is still possible to allow anonymous access by setting it explicitly. Broker fixes: - Fix UNSUBSCRIBE with no topic is accepted on MQTT 3.1.1. Closes #665. - Produce an error if two bridges share the same local_clientid. - Miscellaneous fixes on Windows. - queue_qos0_messages was not observing max_queued_** limits - When using the include_dir configuration option sort the files alphabetically before loading them. Closes #17. - IPv6 is no longer disabled for websockets listeners. - Remove all build timestamp information including $SYS/broker/timestamp. Close #651. - Correctly handle incoming strings that contain a NULL byte. Closes #693. - Use constant time memcmp for password comparisons. - Fix incorrect PSK key being used if it had leading zeroes. - Fix memory leak if a client provided a username/password for a listener with use_identity_as_username configured. - Fix use_identity_as_username not working on websockets clients. - Don't crash if an auth plugin returns MOSQ_ERR_AUTH for a username check on a websockets client. Closes #490. - Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507. - Lines in the config file are no longer limited to 1024 characters long. Closes #652. - Fix $SYS counters of messages and bytes sent when message is sent over a Websockets. Closes #250. - Fix upgrade_outgoing_qos for retained message. Closes #534. - Fix CONNACK message not being sent for unauthorised connect on websockets. Closes #8. - Maximum connections on Windows increased to 2048. - When a client with an in-use client-id connects, if the old client has a will, send the will message. Closes #26. - Fix parsing of configuration options that end with a space. Closes #804. Client library features: - Outgoing messages with QoS>1 are no longer retried after a timeout period. Messages will be retried when a client reconnects. - DNS-SRV support is now disabled by default. - Add mosquitto_subscribe_simple() This is a helper function to make retrieving messages from a broker very straightforward. Examples of its use are in examples/subscribe_simple. - Add mosquitto_subscribe_callback() This is a helper function to make processing messages from a broker very straightforward. An example of its use is in examples/subscribe_simple. - Connections now default to using MQTT v3.1.1. - Add mosquitto_validate_utf8() to check whether a string is valid UTF-8 according to the UTF-8 spec and to the additional restrictions imposed by the MQTT spec. - Topic inputs are checked for UTF-8 validity. - Add mosquitto_userdata function to allow retrieving the client userdata member variable. Closes #111. - Add mosquitto_pub_topic_check2(), mosquitto_sub_topic_check2(), and mosquitto_topic_matches_sub2() which are identical to the similarly named functions but also take length arguments. - Add mosquitto_connect_with_flags_callback_set(), which allows a second connect callback to be used which also exposes the connect flags parameter. Closes #738 and #128. - Add MOSQ_OPT_SSL_CTX option to allow a user specified SSL_CTX to be used instead of the one generated by libmosquitto. This allows greater control over what options can be set. Closes #715. - Add MOSQ_OPT_SSL_CTX_WITH_DEFAULTS to work with MOSQ_OPT_SSL_CTX and have the default libmosquitto SSL_CTX configuration applied to the user provided SSL_CTX. Closes #567. Client library fixes: - Fix incorrect PSK key being used if it had leading zeroes. - Initialise "result" variable as soon as possible in mosquitto_topic_matches_sub. Closes #654. - No need to close socket again if setting non-blocking failed. Closes #649. - Fix mosquitto_topic_matches_sub() not correctly matching foo/bar against foo/+/#. Closes #670. - SNI host support added. Client features: - Add -F to mosquitto_sub to allow the user to choose the output format. - Add -U to mosquitto_sub for unsubscribing from topics. - Add -c (clean session) to mosquitto_pub. - Add --retained-only to mosquitto_sub to exit after receiving all retained messages. - Add -W to allow mosquitto_sub to stop processing incoming messages after a timeout. - Connections now default to using MQTT v3.1.1. - Default to using port 8883 when using TLS. - mosquitto_sub doesn't continue to keep connecting if CONNACK tells it the connection was refused. Client fixes: - Correctly handle empty files with "mosquitto_pub -l". Closes #676. Build: - Add WITH_STRIP option (defaulting to "no") that when set to "yes" will strip executables and shared libraries when installing. - Add WITH_STATIC_LIBRARIES (defaulting to "no") that when set to "yes" will build and install static versions of the client libraries. - Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636. - Support for openssl versions 1.0.0 and 1.0.1 has been removed as these are no longer supported by openssl. Documentation: - Replace mentions of deprecated 'c_rehash' with 'openssl rehash'. 1.4.15 - 20180228 ================= Security: - Fix CVE-2017-7652. If a SIGHUP is sent to the broker when there are no more file descriptors, then opening the configuration file will fail and security settings will be set back to their default values. - Fix CVE-2017-7651. Unauthenticated clients can cause excessive memory use by setting "remaining length" to be a large value. This is now mitigated by limiting the size of remaining length to valid values. A "memory_limit" configuration option has also been added to allow the overall memory used by the broker to be limited. Broker: - Use constant time memcmp for password comparisons. - Fix incorrect PSK key being used if it had leading zeroes. - Fix memory leak if a client provided a username/password for a listener with use_identity_as_username configured. - Fix use_identity_as_username not working on websockets clients. - Don't crash if an auth plugin returns MOSQ_ERR_AUTH for a username check on a websockets client. Closes #490. - Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507. - Lines in the config file are no longer limited to 1024 characters long. Closes #652. - Fix $SYS counters of messages and bytes sent when message is sent over a Websockets. Closes #250. - Fix upgrade_outgoing_qos for retained message. Closes #534. - Fix CONNACK message not being sent for unauthorised connect on websockets. Closes #8. Client library: - Fix incorrect PSK key being used if it had leading zeroes. - Initialise "result" variable as soon as possible in mosquitto_topic_matches_sub. Closes #654. - No need to close socket again if setting non-blocking failed. Closes #649. - Fix mosquitto_topic_matches_sub() not correctly matching foo/bar against foo/+/#. Closes #670. Clients: - Correctly handle empty files with "mosquitto_pub -l". Closes #676. Build: - Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636. 1.4.14 - 20170710 ================= Broker: - Fix regression from 1.4.13 where persistence data was not being saved. 1.4.13 - 20170627 ================= Security: - Fix CVE-2017-9868. The persistence file was readable by all local users, potentially allowing sensitive information to be leaked. This can also be fixed administratively, by restricting access to the directory in which the persistence file is stored. Broker: - Fix for poor websockets performance. - Fix lazy bridges not timing out for idle_timeout. Closes #417. - Fix problems with large retained messages over websockets. Closes #427. - Set persistence file to only be readable by owner, except on Windows. Closes #468. - Fix CONNECT check for reserved=0, as per MQTT v3.1.1 check MQTT-3.1.2-3. - When the broker stop, wills for any connected clients are now "sent". Closes #477. - Auth plugins can be configured to disable the check for +# in usernames/client ids with the auth_plugin_deny_special_chars option. Partially closes #462. - Restrictions for CVE-2017-7650 have been relaxed - '/' is allowed in usernames/client ids. Remainder of fix for #462. Clients: - Don't use / in auto-generated client ids. 1.4.12 - 20170528 ================= Security: - Fix CVE-2017-7650, which allows clients with username or client id set to '#' or '+' to bypass pattern based ACLs or third party plugins. The fix denies message sending or receiving of messages for clients with a '#' or '+' in their username or client id and if the message is subject to a pattern ACL check or plugin check. Patches for other versions are available at https://mosquitto.org/files/cve/2017-7650/ Broker: - Fix mosquitto.db from becoming corrupted due to client messages being persisted with no stored message. Closes #424. - Fix bridge not restarting properly. Closes #428. - Fix unitialized memory in gets_quiet on Windows. Closes #426. - Fix building with WITH_ADNS=no for systems that don't use glibc. Closes #415. - Fixes to readme.md. - Fix deprecation warning for OpenSSL 1.1. PR #416. - Don't segfault on duplicate bridge names. Closes #446. - Fix CVE-2017-7650. 1.4.11 - 20170220 ================= Broker: - Fix crash when "lazy" type bridge attempts to reconnect. Closes #259. - maximum_connections now applies to websockets listeners. Closes #271. - Allow bridges to use TLS with IPv6. - Don't error on zero length persistence files. Closes #316. - For http only websockets clients, close files served over http in all cases when the client disconnects. Closes #354. - Fix error message when websockets http_dir directory does not exist. - Improve password utility error message. Closes #379. Clients: - Use of --ciphers no longer requires you to also pass --tls-version. Closes #380. Client library: - Clients can now use TLS with IPv6. - Fix potential socket leakage when reconnecting. Closes #304. - Fix potential negative timeout being passed to pselect. Closes #329. 1.4.10 - 20160816 ================= Broker: - Fix TLS operation with websockets listeners and libwebsockts 2.x. Closes #186. - Don't disconnect client on HUP before reading the pending data. Closes #7. - Fix some $SYS messages being incorrectly persisted. Closes #191. - Support OpenSSL 1.1.0. - Call fsync after persisting data to ensure it is correctly written. Closes #189. - Fix persistence saving of subscription QoS on big-endian machines. - Fix will retained flag handling on Windows. Closes #222. - Broker now displays an error if it is unable to open the log file. Closes #234. Client library: - Support OpenSSL 1.1.0. - Fixed the C++ library not allowing SOCKS support to be used. Closes #198. - Fix memory leak when verifying a server certificate with a subjectAltName section. Closes #237. Build: - Don't attempt to install docs when WITH_DOCS=no. Closes #184. 1.4.9 - 20160603 ================ Broker: - Ensure websockets clients that previously connected with clean session set to false have their queued messages delivered immediately on reconnecting. Closes #476314. - Reconnecting client with clean session set to false doesn't start with mid=1 again. - Will topic isn't truncated by one byte when using a mount_point any more. - Network errors are printed correctly on Windows. - Fix incorrect $SYS heap memory reporting when using ACLs. - Bridge config parameters couldn't contain a space, this has been fixed. Closes #150. - Fix saving of persistence messages that start with a '/'. Closes #151. - Fix reconnecting for bridges that use TLS on Windows. Closes #154. - Broker and bridges can now cope with unknown incoming PUBACK, PUBREC, PUBREL, PUBCOMP without disconnecting. Closes #57. - Fix websockets listeners not being able to bind to an IP address. Closes #170. - mosquitto_passwd utility now correctly deals with unknown command line arguments in all cases. Closes #169. - Fix publishing of $SYS/broker/clients/maximum - Fix order of #includes in lib/send_mosq.c to ensure struct mosquitto doesn't differ between source files when websockets is being used. Closes #180. - Fix possible rare crash when writing out persistence file and a client has incomplete messages inflight that it has been denied the right to publish. Client library: - Fix the case where a message received just before the keepalive timer expired would cause the client to miss the keepalive timer. - Return value of pthread_create is now checked. - _mosquitto_destroy should not cancel threads that weren't created by libmosquitto. Closes #166. - Clients can now cope with unknown incoming PUBACK, PUBREC, PUBREL, PUBCOMP without disconnecting. Closes #57. - Fix mosquitto_topic_matches_sub() reporting matches on some invalid subscriptions. Clients: - Handle some unchecked malloc() calls. Closes #1. Build: - Fix string quoting in CMakeLists.txt. Closes #4. - Fix building on Visual Studio 2015. Closes #136. 1.4.8 - 20160214 ================ Broker: - Wills published by clients connected to a listener with mount_point defined now correctly obey the mount point. This was a potential security risk because it allowed clients to publish messages outside of their restricted mount point. This is only affects brokers where the mount_point option is in use. Closes #487178. - Fix detection of broken connections on Windows. Closes #485143. - Close stdin etc. when daemonised. Closes #485589. - Fix incorrect detection of FreeBSD and OpenBSD. Closes #485131. Client library: - mosq->want_write should be cleared immediately before a call to SSL_write, to allow clients using mosquitto_want_write() to get accurate results. 1.4.7 - 20151221 ================ Broker: - Fix support for libwebsockets 1.22. 1.4.6 - 20151220 ================ Broker: - Add support for libwebsockets 1.6. Client library: - Fix _mosquitto_socketpair() on Windows, reducing the chance of delays when publishing. Closes #483979. Clients: - Fix "mosquitto_pub -l" stripping the final character on a line. Closes #483981. 1.4.5 - 20151108 ================ Broker: - Fix possible memory leak if bridge using SSL attempts to connect to a host that is not up. - Free unused topic tree elements (fix in 1.4.3 was incomplete). Closes #468987. Clients: - "mosquitto_pub -l" now no longer limited to 1024 byte lines. Closes #478917. 1.4.4 - 20150916 ================ Broker: - Don't leak sockets when outgoing bridge with multiple addresses cannot connect. Closes #477571. - Fix cross compiling of websockets. Closes #475807. - Fix memory free related crashes on openwrt. Closes #475707. - Fix excessive calls to message retry check. 1.4.3 - 20150818 ================ Broker: - Fix incorrect bridge notification on initial connection. Closes #467096. - Build fixes for OpenBSD. - Fix incorrect behaviour for autosave_interval, most noticable for autosave_interval=1. Closes #465438. - Fix handling of outgoing QoS>0 messages for bridges that could not be sent because the bridge connection was down. - Free unused topic tree elements. Closes #468987. - Fix some potential memory leaks. Closes #470253. - Fix potential crash on libwebsockets error. Client library: - Add missing error strings to mosquitto_strerror. - Handle fragmented TLS packets without a delay. Closes #470660. - Fix incorrect loop timeout being chosen when using threaded interface and keepalive = 0. Closes #471334. - Increment inflight messages count correctly. Closes #474935. Clients: - Report error string on connection failure rather than error code. 1.4.2 - 20150507 ================ Broker: - Fix bridge prefixes only working for the first outgoing message. Closes #464437. - Fix incorrect bridge connection notifications on local broker. - Fix persistent db writing on Windows. Closes #464779. - ACLs are now checked before sending a will message. - Fix possible crash when using bridges on Windows. Closes #465384. - Fix parsing of auth_opt_ arguments with extra spaces/tabs. - Broker will return CONNACK rc=5 when a username/password is not authorised. This was being incorrectly set as rc=4. - Fix handling of payload lengths>4096 with websockets. Client library: - Inflight message count wasn't being decreased for outgoing messages using QoS 2, meaning that only up to 20 QoS 2 messages could be sent. This has been fixed. Closes #464436. - Fix CMake dependencies for C++ wrapper building. Closes #463884. - Fix possibility of select() being called with a socket that is >FD_SETSIZE. This is a fix for #464632 that will be followed up by removing the select() call in a future version. - Fix calls to mosquitto_connect*_async() not completing. 1.4.1 - 20150403 ================ Broker: - Fix possible crash under heavy network load. Closes #463241. - Fix possible crash when using pattern ACLs. - Fix problems parsing config strings with multiple leading spaces. Closes #462154. - Websockets clients are now periodically disconnected if they have not maintained their keepalive timer. Closes #461619. - Fix possible minor memory leak on acl parsing. Client library: - Inflight limits should only apply to outgoing messages. Closes #461620. - Fix reconnect bug on Windows. Closes #463000. - Return -1 on error from mosquitto_socket(). Closes #461705. - Fix crash on multiple calls to mosquitto_lib_init/mosquitto_lib_cleanup. Closes #462780. - Allow longer paths on Windows. Closes #462781. - Make _mosquitto_mid_generate() thread safe. Closes #463479. 1.4 - 20150218 ============== Important changes: - Websockets support in the broker. - Bridge behaviour on the local broker has changed due to the introduction of the local_* options. This may affect you if you are using authentication and/or ACLs with bridges. - The default TLS behaviour has changed to accept all of TLS v1.2, v1.1 and v1.0, rather than only only one version of the protocol. It is still possible to restrict a listener to a single version of TLS. - The Python client has been removed now that the Eclipse Paho Python client has had a release. - When a durable client reconnects, its queued messages are now checked against ACLs in case of a change in username/ACL state since it last connected. - New use_username_as_clientid option on the broker, for preventing hijacking of a client id. - The client library and clients now have experimental SOCKS5 support. - Wildcard TLS certificates are now supported for bridges and clients. - The clients have support for config files with default options. - Client and client libraries have support for MQTT v3.1.1. - Bridge support for MQTT v3.1.1. Broker: - Websockets support in the broker. - Add local_clientid, local_username, local_password for bridge connections to authenticate to the local broker. - Default TLS mode now accepts TLS v1.2, v1.1 and v1.0. - Support for ECDHE-ECDSA family ciphers. - Fix bug #1324411, which could have had unexpected consequences for delayed messages in rare circumstances. - Add support for "session present" in CONNACK messages for MQTT v3.1.1. - Remove strict protocol #ifdefs. - Change $SYS/broker/clients/active -> $SYS/broker/clients/connected - Change $SYS/broker/clients/inactive -> $SYS/broker/clients/disconnected - When a durable client reconnects, its queued messages are now checked against ACLs in case of a change in username/ACL state since it last connected. - libuuid is used to generate client ids, where it is available, when an MQTT v3.1.1 client connects with a zero length client id. - Anonymous clients are no longer accidently disconnected from the broker after a SIGHUP. - mosquitto_passwd now supports -b (batch mode) to allow the password to be provided at the command line. - Removed $SYS/broker/changeset. This was intended for use with debugging, but in practice is of no use. - Add support for use_username_as_clientid which can be used with authentication to restrict ownership of client ids and hence prevent one client disconnecting another by using the same client id. - When "require_certificate" was false, the broker was incorrectly asking for a certificate (but not checking it). This caused problems with some clients and has been fixed so the broker no longer asks. - When using syslog logging on non-Windows OSs, it is now possible to specify the logging facility to one of local0-7 instead of the default "daemon". - The bridge_attempt_unsubscribe option has been added, to allow the sending of UNSUBSCRIBE requests to be disabled for topics with "out" direction. Closes bug #456899. - Wildcard TLS certificates are now supported for bridges. - Support for "hour" client expiration lengths for the persistent_client_expiration option. Closes bug #425835. - Bridge support for MQTT v3.1.1. - Root privileges are now dropped after starting listeners and loading certificates/private keys, to allow private keys to have their permissions restricted to the root user only. Closes bug #452914. - Usernames and topics given in ACL files can now include a space. Closes bug #431780. - Fix hang if pattern acl contains a %u but an anonymous client connect. Closes bug #455402. - Fix man page installation with cmake. Closes bug #458843. - When using "log_dest file" the output file is now flushed periodically. Clients: - Both clients can now load default configuration options from a file. - Add -C option to mosquitto_sub to allow the client to quit after receiving a certain count of messages. Closes bug #453850. - Add --proxy SOCKS5 support for both clients. - Pub client supports setting its keepalive. Closes bug #454852. - Add support for config files with default options. - Add support for MQTT v3.1.1. Client library: - Add experimental SOCKS5 support. - mosquitto_loop_forever now quits after a fatal error, rather than blindly retrying. - SRV support is now not compiled in by default. - Wildcard TLS certificates are now supported. - mosquittopp now has a virtual destructor. Closes bug #452915. - Add support for MQTT v3.1.1. - Don't quit mosquitto_loop_forever() if broker not available on first connect. Closes bug #453293, but requires more work. - Don't reset queued messages state on CONNACK. Fixes bug with duplicate messages on connection. 1.3.5 - 20141008 ================ Broker: - Fix possible memory leak when using a topic that has a leading slash. Fixes bug #1360985. - Fix saving persistent database on Windows. - Temporarily disable ACL checks on subscriptions when using MQTT v3.1.1. This is due to the complexity of checking wildcard ACLs against wildcard subscriptions. This does not have a negative impact on security because checks are still made before a message is sent to a client. Fixes bug #1374291. - When using -v and the broker receives a SIGHUP, verbose logging was being disabled. This has been fixed. Client library: - Fix mutex being incorrectly passed by value. Fixes bug #1373785. 1.3.4 - 20140806 ================ Broker: - Don't ask client for certificate when require_certificate is false. - Backout incomplete functionality that was incorrectly included in 1.3.2. 1.3.3 - 20140801 ================ Broker: - Fix incorrect handling of anonymous bridges on the local broker. 1.3.2 - 20140713 ================ Broker: - Don't allow access to clients when authenticating if a security plugin returns an application error. Fixes bug #1340782. - Ensure that bridges verify certificates by default when using TLS. - Fix possible crash when using pattern ACLs that do not include a %u and clients that connect without a username. - Fix subscriptions being deleted when clients subscribed to a topic beginning with a $ but that is not $SYS. - When a durable client reconnects, its queued messages are now checked against ACLs in case of a change in username/ACL state since it last connected. - Fix bug #1324411, which could have had unexpected consequences for delayed messages in rare circumstances. - Anonymous clients are no longer accidently disconnected from the broker after a SIGHUP. Client library: - Fix topic matching edge case. - Fix callback deadlocks after calling mosquitto_disconnect(), when using the threaded interfaces. Closes bug #1313725. - Fix SRV support when building with CMake. - Remove strict protocol #ifdefs. General: - Use $(STRIP) for stripping binaries when installing, to allow easier cross compilation. 1.3.1 - 20140324 ================ Broker: - Prevent possible crash on client reconnect. Closes bug #1294108. - Don't accept zero length unsubscription strings (MQTT v3.1.1 fix) - Don't accept QoS 3 (MQTT v3.1.1 fix) - Don't disconnect clients immediately on HUP to give chance for all data to be read. - Reject invalid un/subscriptions e.g. foo/+bar #/bar. - Take more care not to disconnect clients that are sending large messages. Client library: - Fix socketpair code on the Mac. - Fix compilation for WITH_THREADING=no. - Break out of select() when calling mosquitto_loop_stop(). - Reject invalid un/subscriptions e.g. foo/+bar #/bar. - Add mosquitto_threaded_set(). Clients: - Fix keepalive value on mosquitto_pub. - Fix possibility of mosquitto_pub not exiting after sending messages when using -l. 1.3 - 20140316 ============== Broker: - The broker no longer ignores the auth_plugin_init() return value. - Accept SSLv2/SSLv3 HELLOs when using TLSv1, whilst keeping SSLv2 and SSLv3 disabled. This increases client compatibility without sacrificing security. - The $SYS tree can now be disabled at runtime as well as at compile time. - When remapping bridged topics, only check for matches when the message direction is correct. This allows two identical topics to be remapped differently for both in and out. - Change "$SYS/broker/heap/current size" to "$SYS/broker/heap/current" for easier parsing. - Change "$SYS/broker/heap/maximum size" to "$SYS/broker/heap/maximum" for easier parsing. - Topics are no longer normalised from e.g a///topic to a/topic. This matches the behaviour as clarified by the Oasis MQTT spec. This will lead to unexpected behaviour if you were using topics of this form. - Log when outgoing messages for a client begin to drop off the end of the queue. - Bridge clients are recognised as bridges even after reloading from persistence. - Basic support for MQTT v3.1.1. This does not include being able to bridge to an MQTT v3.1.1 broker. - Username is displayed in log if present when a client connects. - Support for 0 length client ids (v3.1.1 only) that result in automatically generated client ids on the broker (see option allow_zero_length_clientid). - Ability to set the prefix of automatically generated client ids (see option auto_id_prefix). - Add support for TLS session resumption. - When using TLS, the server now chooses the cipher to use when negotiating with the client. - Weak TLS ciphers are now disabled by default. Client library: - Fix support for Python 2.6, 3.0, 3.1. - Add support for un/subscribing to multiple topics at once in un/subscribe(). - Clients now close their socket after sending DISCONNECT. - Python client now contains its version number. - C library mosquitto_want_write() now supports TLS clients. - Fix possible memory leak in C/C++ library when communicating with a broker that doesn't follow the spec. - Return strerror() through mosquitto_strerror() to make error printing easier. - Topics are no longer normalised from e.g a///topic to a/topic. This matches the behaviour as clarified by the Oasis MQTT spec. This will lead to unexpected behaviour if you were using topics of this form. - Add support for SRV lookups. - Break out of select() on publish(), subscribe() etc. when using the threaded interface. Fixes bug #1270062. - Handle incoming and outgoing messages separately. Fixes bug #1263172. - Don't terminate threads on mosquitto_destroy() when a client is not using the threaded interface but does use their own thread. Fixes bug #1291473. Clients: - Add --ciphers to allow specifying which TLS ciphers to support. - Add support for SRV lookups. - Add -N to sub client to suppress printing of EOL after the payload. - Add -T to sub client to suppress printing of a topic hierarchy. 1.2.3 - 20131202 ================ Broker: - Don't always attempt to call read() for SSL clients, irrespective of whether they were ready to read or not. Reduces syscalls significantly. - Possible memory leak fixes. - Further fix for bug #1226040: multiple retained messages being delivered for subscriptions ending in #. - Fix bridge reconnections when using multiple bridge addresses. Client library: - Fix possible memory leak in C/C++ library when communicating with a broker that doesn't follow the spec. - Block in Python loop_stop() until all messages are sent, as the documentation states should happen. - Fix for asynchronous connections on Windows. Closes bug #1249202. - Module version is now available in mosquitto.py. Clients: - mosquitto_sub now uses fwrite() instead of printf() to output messages, so messages with NULL characters aren't truncated. 1.2.2 - 20131021 ================ Broker: - Fix compliance with max_inflight_messages when a non-clean session client reconnects. Closes one of the issues on bug #1237389. Client library: - Fix incorrect inflight message accounting, which caused messages to go unsent. Partial fix for bug #1237351. - Fix potential memory corruption when sending QoS>0 messages at a high rate using the threaded interface. Further fix for #1237351. - Fix incorrect delay scaling when exponential_backoff=true in mosquitto_reconnect_delay_set(). - Some pep8 fixes for Python. 1.2.1 - 20130918 ================ Broker: - The broker no longer ignores the auth_plugin_init() return value. Closes bug #1215084. - Use RTLD_GLOBAL when opening authentication plugins on posix systems. Fixes resolving of symbols in libraries used by authentication plugins. - Add/fix some config documentation. - Fix ACLs for topics with $SYS. - Clients loaded from the persistence file on startup were not being added to the client hash, causing subtle problems when the client reconnected, including ACLs failing. This has been fixed. - Add note to mosquitto-tls man page stating that certificates need to be unique. Closes bug #1221285. - Fix incorrect retained message delivery when using wildcard subs in some circumstances. Fixes bug #1226040. Client library: - Fix support for Python 2.6, 3.0, 3.1. - Fix TLS subjectAltName verification and segfaults. - Handle EAGAIN in Python on Windows. Closes bug #1220004. - Fix compilation when using WITH_TLS=no. - Don't fail reconnecting in Python when broker is temporarily unavailable. 1.2 - 20130708 ============== Broker: - Replace O(n) username lookup on CONNECT with a roughly O(1) hashtable version. - It is now possible to disable $SYS at compile time. - Add dropped publish messages to load tree in $SYS. Closes bug #1183318. - Add support for logging SUBSCRIBE/UNSUBSCRIBE events. - Add "log_dest file" logging support. - Auth plugin ACL check function now passes the client id as well as username and password. - The queue_qos0_messages option wasn't working correctly, this has now been fixed. Closes bug #1125200. - Don't drop all messages for disconnected durable clients when max_queued_messages=0. - Add support for "log_type all". - Add support for "-v" option on the command line to provide the equivalent of "log_type all" without needing a config file. - Add the "upgrade_outgoing_qos" option, a non-standard feature. - Persistence data is now written to a temporary file which is atomically renamed on completion, so a crash during writing will not produce a corrupt file. - mosquitto.conf is now installed as mosquitto.conf.example - Configuration file errors are now reported with filename and line number. - The broker now uses a monotonic clock if available, to avoid changes in time causing client disconnections or message retries. - Clean session and keepalive status are now display the log when a client connects. - Add support for TLSv1.2 and TLSv1.1. - Clients that connect with zero length will topics are now rejected. - Add the ability to set a maximum allowed PUBLISH payload size. - Fix an ACL with topic "#" incorrectly granting access to $SYS. - Fix retained messages incorrectly being set on wildcard topics, leading to duplicate retained messages being sent on subscription. Closes bug #1116233. - Don't discard listener values when no "port" option given. Closes bug #1131406. - Client password check was always failing when security was being reapplied after a config reload. This meant that all clients were being disconnected. This has been fixed. - Fix build when WITH_TLS=no. Closes bug #1174971. - Fix single outgoing packets not being sent in a timely fashion if they were not sent in one call to write(). Closes bug #1176796. - Fix remapping of messages for clients connected to a listener with mount_point set. Closes bug #1180765. - Fix duplicate retained messages being sent for some wildcard patterns. - If a client connects with a will topic to which they do not have write access, they are now disconnected with CONNACK "not authorised". - Fix retained messages on topic foo being incorrectly delivered to subscriptions of /# - Fix handling of SSL errors on SSL_accept(). - Fix handling of QoS 2 messages on client reconnect. - Drop privileges now sets supplementary groups correctly. - Fix load reporting interval (is now 60s). - Be strict with malformed PUBLISH packets - clients are now disconnected rather than the packet discarded. This goes inline with future OASIS spec changes and makes other changes more straightforward. - Process incoming messages denied by ACL properly so that clients don't keep resending them. - Add support for round_robin bridge option. - Add bridge support for verifying remote server certificate subject against the remote hostname. - Fix problem with out of order calls to free() when restarting a lazy bridge. - The broker now attempts to resolve bind_address and bridge addresses immediately when parsing the config file in order to detect invalid hosts. - Bridges now set their notification state before attempting to connect, so if they fail to connect the state can still be seen. - Fix bridge notification payload length - no need to send a null byte. - mosquitto_passwd utility now reports errors more clearly. - Fix "mosquitto_passwd -U". Client library: - Add support for TLSv1.2 and TLSv1.1, except for on the Python module. - Add support for verifying remote server certificate subject against the remote hostname. - Add mosquitto_reconnect_async() support and make asynchronous connections truely asynchronous rather than simply deferred. DNS lookups are still blocking, so asynchronous connections require an IP address instead of hostname. - Allow control of reconnection timeouts in mosquitto_loop_forever() and after mosquitto_loop_start() by using mosquitto_reconnect_delay_set(). - Fix building on Android NDK. - Re-raise unhandled errors in Python so as not to provide confusing error messages later on. - Python module supports IPv6 connections. - mosquitto_sub_topic_tokenise() was behaving incorrectly if the last topic hierarchy had only a single character. This has been fixed. Closes bug #1163348. - Fix possible crash after disconnects when using the threaded interface with TLS. - Allow build/install without Python. Closes bug #1174972. - Add support for binding connection to a local interface. - Implement maximum inflight messages handling. - Fix Python client not handling will_payload==None. - Fix potential memory leak when setting username/password. - Fix handling of QoS 2 messages on reconnect. - Improve handling of mosquitto_disconnect() with threaded mode. Clients: - Add support for TLSv1.2 and TLSv1.1. - Sub client can now suppress printing of messages with the retain bit set. - Add support for binding connection to a local interface. - Implement maximum inflight messages handling for the pub client. 1.1.3 - 20130211 ================ Broker: - mosquitto_passwd utility now uses tmpfile() to generate its temporary data storage file. It also creates a backup file that can be used to recover data if an errors occur. Other: - Build script fixes to help packaging on Debian. 1.1.2 - 20130130 ================ Client library: - Fix tls_cert_reqs not being set to SSL_VERIFY_PEER by default. This meant that clients were not verifying the server certificate when connecting over TLS. This affects the C, C++ and Python libraries. 1.1.1 - 20130116 ================ Broker: - Fix crash on reload if using acl patterns. Client library: - Fix static C++ functions not being exported on Windows. Fixes bug #1098256. 1.1 - 20121219 ============== Broker: - Add $SYS/broker/messages/dropped - Add $SYS/broker/clients/expired - Replace $SYS/broker/+/per second/+ with moving average versions published at $SYS/broker/load/# - Add $SYS/broker/load/sockets/+ and $SYS/broker/load/connections/+ - Documentation on password file format has been fixed. - Disable SSL compression. This reduces memory usage significantly and removes the possibility of CRIME type attacks. - Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further. - Add allow_duplicate_messages option. - ACL files can now have comment lines with # as the first character. - Display message on startup about which config is being loaded. - Fix max_inflight_messages and max_queued_messages not being applied. - Fix documentation error in mosquitto.conf. - Ensure that QoS 2 queued messages are sent out in a timely manner. - Local bridges now act on clean_session correctly. - Local bridges with clean_session==false now remove unused subscriptions on broker restart. - The $SYS/broker/heap/# messages now no longer include "bytes" as part of the string for ease of use. Client library: - Free memory used by OpenSSL in mosquitto_lib_cleanup() where possible. - Change WebSocket subprotocol name to mqttv3.1 to make future changes easier and for compatibility with other implementations. - mosquitto_loop_read() and mosquitto_loop_write() now handle errors themselves rather than having mosquitto_loop() handle their errors. This makes using them in a separate event loop more straightforward. - Add mosquitto_loop_forever() / loop_forever() function call to make simple clients easier. - Disable SSL compression. This reduces memory usage significantly and removes the possibility of CRIME type attacks. - Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further. - mosquitto_tls_set() will now return an error or raise an exception immediately if the CA certificate or client certificate/key cannot be accessed. - Fix potential memory leaks on connection failures. - Don't produce return error from mosquitto_loop() if a system call is interrupted. This prevents disconnects/reconnects in threaded mode and simplifies non-threaded client handling. - Ignore SIGPIPE to prevent unnecessary client quits in threaded mode. - Fix document error for mosquitto_message_retry_set(). - Fix mosquitto_topic_matches_sub() for subscriptions with + as the final character. Fixes bug #1085797. - Rename all "obj" parameters to "userdata" for consistency with other libraries. - Reset errno before network read/write to ensure EAGAIN isn't mistakenly returned. - The message queue length is now tracked and used to determine the maximum number of packets to process at once. This removes the need for the max_packets parameter which is now unused. - Fix incorrect error value in Python error_string() function. Fixes bug #1086777. - Reset last message in/out timer in Python module when we send a PINGREQ. Fixes too-early disconnects. Clients: - Clients now display their own version number and library version number in their help messages. - Fix "mosquitto_pub -l -q 2" disconnecting before all messages were transmitted. - Fix potential out-of-bounds array access with client ids. Fixes bug #1083182. Other: - mosquitto_passwd can now convert password files with plain text files to hashed versions. 1.0.5 - 20121103 ================ Broker: - Fix crash when the broker has use_identity_as_username set to true but a client connects without a certificate. - mosquitto_passwd should only be installed if WITH_TLS=yes. Library: - Use symbolic errno values rather than numbers in Python module to avoid cross platform issues (incorrect errno on Mac OS). Other: - Build script fixes for FreeBSD. 1.0.4 - 20121017 ================ Broker: - Deal with poll() POLLIN/POLLOUT before POLL[RD]HUP to correctly handle the case where a client sends data and immediately closes its socket. Library: - Fix memory leak with messages of QoS=2. Fixes bug #1064981. - Fix potential thread synchronisation problem with outgoing packets in the Python module. Fixes bug #1064977. Clients: - Fix "mosquitto_sub -l" incorrectly only sending one message per second. 1.0.3 - 20120927 ================ Broker: - Fix loading of psk files. - Don't return an error when reloading config if an ACL file isn't defined. This was preventing psk files being reloaded. - Clarify meaning of $SYS/broker/clients/total in mosquitto(8) man page. - Clarify meaning of $SYS/broker/messages/stored in mosquitto(8) man page. - Fix non-retained message delivery when subscribing to #. - Fix retained message delivery for subs to foo/# with retained messages at foo. - Include the filename in password/acl file loading errors. Library: - Fix possible AttributeError when self._sock == None in Python module. - Fix reconnecting after a timeout in Python module. - Fix reconnecting when there were outgoing packets in the queue in the Python module. - Fix problem with mutex initialisation causing crashes on some Windows installations. 1.0.2 - 20120919 ================ Broker: - If the broker was configured for persistence, a durable client had a subscription to topics in $SYS/# and had messages in its queue when the broker restarted, then the persistent database would have messages missing and so the broker would not restart properly. This has been fixed. Library: - Fix threading problem on some systems. Tests: - Close socket after 08-ssl-connect-no-auth-wrong-ca.py test to prevent subsequent tests having problems. Build scripts: - Install pskfile.example in CMake. Fixes bug #1037504. Other: - Fix db_dump parameter printing message store and sub chunks. 1.0.1 - 20120815 ================ Broker: - Fix default log_dest when running as a Windows service. Client library: - Fix incorrect parameters in Python on_log() callback call. Fixes bug #1036818. Clients: - Clients now don't display TLS/TLS-PSK usage help if they don't support it. Build scripts: - Fix TLS-PSK support in the CMake build files. - Fix man page installation in the CMake build files. - Fix SYSCONFDIR in cmake on *nix when installing to /usr. Fixes bug #1036908. Documentation: - Fix mqtt/MQTT capitalisation in man pages. - Update compiling.txt. - Fix incorrect callback docs in mosquitto.py. Fixes bug #1036607. - Fix various doc typos and remove obsolete script. Fixes bug #1037088. 1.0 - 20120814 ============== Broker: - Add SSL/TLS support. - Add TLS-PSK support, providing a simpler encryption method for constrained devices. - Passwords are now salted+hashed if compiled with WITH_TLS (recommended). - Add mosquitto_passwd for handling password files. - Add $SYS/broker/publish/messages/{sent|received} to show the number of PUBLISH messages sent/received. - Add $SYS/broker/publish/bytes/{sent|received} to show the number of PUBLISH bytes sent/received. - Add reload parameter for security init/cleanup functions. - Add option for expiring disconnected persistent clients. - Add option for queueing of QoS 0 messages when persistent clients are disconnected. - Enforce client id limits in the broker (only when WITH_STRICT_PROTOCOL is defined). - Fix reloading of log configuration. - Add support for try_private config option for bridge connections. - Add support for autosave_on_changes config option. - Add support for include_dir config option. - Add support for topic remapping. - Usernames were being lost when a non clean-session client reconnected, potentially causing problems with ACLs. This has been fixed. - Significant improvement to memory handling on Windows. - Bridges with outgoing topics will now set the retain flag correctly so that messages will be retained on the remote broker. - Incoming bridge connections are now detected by checking if bit 8 of the protocol version number is set. This requires support from the remote broker. - Add support for notification_topic option. - Add $SYS/broker/subscriptions/count and $SYS/broker/retained messages/count. - Add restart_timeout to control the amount of time an automatic bridge will wait before reconnecting. - Overlapping subscriptions are now handled properly. Fixes bug #928538. - Fix reloading of persistence_file and persistence_location. - Fix broker crash on incorrect protocol number. - Fix missing COMPAT_ECONNRESET define on Windows. - Clients that had disconnected were not always being detected immediately on Linux. This has been fixed. - Don't save $SYS messages to the on-disk persistent db. All $SYS messages should be reconstructed on a restart. This means bridge connection notifications will now be correct on a restart. - Fix reloading of bridge clients from the persistent db. This means that outgoing bridged topics should always work. - Local bridges are now no longer restricted by local ACLs. - Discard publish messages with zero length topics. - Drop to "mosquitto" user even if no config file specified. - Don't incorrectly allow topic access if ACL patterns but no normal ACL rules are defined. Client library: - Add SSL/TLS support. - Add TLS-PSK support, providing a simpler encryption method for constrained devices. - Add javascript/websockets client library. - Add "struct mosquitto *mosq" parameter for all callbacks in the client library. This is a binary incompatible change so the soversion of the libraries has been incremented. The new parameter should make it easier to use callbacks in practice. - Add mosquitto_want_write() for use when using own select() loop with mosquitto_socket(). - Add mosquitto_connect_async() to provide a non-blocking connect client call. - Add mosquitto_user_data_set() to allow user data pointer to be updated. - Add "int rc" parameter to disconnect callback to indicate whether disconnect was unexpected or the result of calling mosquitto_disconnect(). - Add mosquitto_strerror() for obtaining a string description of error numbers. - Add mosquitto_connack_string() for obtaining a string description of MQTT connection results. - Add mosquitto_will_clear() and change mosquitto_will_set() to only set the will. - Add mosquitto_sub_topic_tokenise() and mosquitto_sub_topic_tokens_free() utility functions to tokenise a subscription/topic string into a string array. - Add mosquitto_topic_matches_sub() to check whether a topic matches a subscription. - Replaced mosquitto_log_init() with mosquitto_log_callback_set() to allow clients to decide what to do with log messages. - Client will now disconnect itself from the broker if it doesn't receive a PINGRESP in the keepalive period after sending a PINGREQ. - Client will now send a PINGREQ if it has not received a message from the broker in keepalive seconds. - mosquitto_new() will now generate a random client id if the id parameter is NULL. - Added max_packets to mosquitto_loop(), mosquitto_loop_read() and mosquitto_loop_write() to control the maximum number of packets that are handled per call. - Payload parameters are now void * instead of uint8_t *. - The clean_session parameter has been moved from mosquitto_connect() to mosquitto_new() because it is a client parameter rather than a connection parameter. - Functions now use int instead of uint*_t where possible. - mosquitto_new() now sets errno to indicate failure type. - Return MOSQ_ERR_INVAL on zero length topic. - Fix automatic client id generation on Windows. - mosquitto_loop_misq() can now return MOSQ_ERR_NO_CONN. - Compile static library as well as dynamic library with default makefiles. - Rename C++ namespace from mosquittopp to mosqpp to remove ambiguity. - C++ lib_init(), lib_version() and lib_cleanup() are now in the mosqpp namespace directly, not mosquittopp class members. - The Python library is now written in pure Python and so no longer depends on libmosquitto. - The Python library includes SSL/TLS support. - The Python library should now be compatible with Python 3. Other: - Fix db_dump reading of retained messages. - Add example of logging all messages to mysql. - Add C++ client example. - Fix potential buffer overflow in pub/sub clients. - Add "make binary" target that doesn't make documents. - Add "--help" arguments to pub/sub clients. - Fix building on Solaris. 0.15 - 20120205 =============== - Add support for $SYS/broker/clients/maximum and $SYS/broker/clients/active topics. - Add support for $SYS messages/byte per second received/sent topics. - Updated mosquitto man page - $SYS hierarchy and signal support were out of date. - Auto generated pub/sub client ids now include the hostname. - Tool for dumping persistent DB contents is available in src/db_dump. It isn't installed by default. - Enforce topic length checks in client library. - Implement "once" and "lazy" bridge start types. - Add new return type MOSQ_ERR_ERRNO to indicate that the errno variable should be checked for the real error code. - Add support for connection_messages config option. - mosquitto_sub will now refuse to run if the -c option (disable clean session) is given and no client id is provided. - mosquitto_pub now gives more useful error messages on invalid input or other error conditions. - Fix Python will_set() true/True typo. - Fix messages to topic "a/b" incorrectly matching on a subscription "a" if another subscription "a/#" exists. 0.14.4 - 20120106 ================= - Fix local bridge notification messages. - Fix return values for more internal library calls. - Fix incorrect out of memory checks in library and broker. - Never time out local bridge connections. 0.14.3 - 20111210 ================= - Fix potential crash when client connects with an invalid CONNECT packet. - Fix incorrect invalid socket comparison on Windows. - Server shouldn't crash when a message is published to foo/ when a subscription to foo/# exists (bug #901697). - SO_REUSEADDR doesn't work the same on Windows, so don't use it. - Cygwin builds now support Windows service features. - Fix $SYS/broker/bytes/sent reporting. 0.14.2 - 20111123 ================= - Add uninstall target for libs. - Don't try to write packet whilst in a callback. 0.14.1 - 20111117 ================= - Fix Python sytax errors (bug #891673). 0.14 - 20111116 =============== - Add support for matching ACLs based on client id and username. - Add a Windows installer file (NSIS based). - Add native support for running the broker as a Windows service. This is the default when installed using the new installer. - Fix client count for listeners. When clients disconnect, decrement the count. Allow max_connections to work again. - Attempt to send all packets immediately upon being queued. This will result in more immediate network communication in many cases. - Log IP address when reporting CONNACK packets if the client id isn't yet known. - Fix payload length calculation in python will_set function. - Fix Python publish and will_set functions for payload=None. - Fix keepalive value being lost when reconnecting a client (bug #880863). - Persistence file writing now uses portable file functions, so the Cygwin broker build should no longer be necessary. - Duplicate code between the client and broker side has been reduced. - Queued messages for clients reconnecting with clean_session=false set were not being sent until the next message for that client was received. This has been fixed (bug #890724). - Fix subscriptions to # incorrectly matching against topics beginning with / 0.13 - 20110920 =============== - Implement bridge state notification messages. - Save client last used mid in persistent database (DB version number bumped). - Expose message id in Python MosquittoMessage. - It is now possible to set the topic QoS level for bridges. - Python MosquittoMessage payload parameter is now a Python string, not a ctypes object which makes it much easier to use. - Fix queueing of messages for disconnected clients. The max_queued_messages option is now obeyed. - C++ library is now in its own namespace, mosquittopp. - Add support for adding log message timestamps in the broker. - Fix missing mosquitto_username_pw_set() python binding. - Fix keepalive timeout for reconnecting non clean-session clients. Prevents immediate disconnection on reconnection. - Fix subscription wildcard matching - a subscription of +/+ will now match against /foo - Fix subscription wildcard matching - a subscription of foo/# will now match against foo - When restoring persistent database, clients should be set to non clean-session or their subscriptions will be immediately removed. - Fix SUBACK payload for multiple topic subscriptions. - Don't send retained messages when a client subscribes to a topic it is already subscribed to. 0.12 - 20110725 =============== - Reload (most) configuration on SIGHUP. - Memory tracking is no longer compiled in the client library. - Add --help option to mosquitto to display usage. - Add --id-prefix option to clients to allow easier use with brokers that are using the clientid_prefix option. - Fix compilation on QNX. - Add -P as a synonym argument for --pw in the clients. - Fix python MosquittoMessage payload parameter. This is now returned as a pointer to an array of c_uint8 values so binary data is handled correctly. If a string is needed, use msg.payload_str - Fix memory leaks on client authentication. - If password_file is not defined then clients can now connect even if they use a username/password. - Add mosquitto_reconnect() to the client library. - Add option for compiling with liberal protocol compliance support (enabled by default). - Fix problems with clients reconnecting and old messages remaining in the message store. - Display both ip and client id in the log message when a client connects. Change the socket connection message to make it more obvious that it is just a socket connection being made (bug #801135). - Fix retained message delivery where a subscription contains a +. - Be more lenient when reloading persistent database to reduce errors with empty retained messages. 0.11.3 - 20110707 ================= - Don't complain and quit if persistence_file option is given (bug #802423). - Initialise listeners correctly when clients with duplicate client ids connect. Bug #801678. - Memory tracking is now disabled for Symbian builds due to lack of malloc.h. - Fix memory tracking compilation for kFreeBSD. - Python callbacks can now be used with class member functions. - Fix persistent database writing of client message chunks which caused errors when restoring (bug #798164). 0.11.2 - 20110626 ================= - Don't free contexts in mqtt3_context_disconnect() (bug #799688 / #801678). - Only free will if present when freeing a client context. 0.11.1 - 20110620 ================= - Fix buffer overrun when checking for + and # in topics (bug #799688). - Pub client now quits if publish fails. 0.11 - 20110619 =============== - Removed all old sqlite code. - Remove client id limit in clients. - Implemented $SYS/broker/heap/maximum size - Implemented $SYS/broker/clients/inactive to show the number of disconnected non-clean session clients. - $SYS/broker/heap/current size and maximum size messages now include "bytes" to match rsmb message format. - Implemented the retained_persistence config file option - a synonym of the "persistence" option. - Added security_external.c to broker source to make it easier for third parties to add support for their existing username/password and ACL database for security checks. See external_security_checks.txt. - $SYS messages are now only republished when their value changes. - Windows native broker now responds to command line arguments. - Simplify client disconnecting so wills gets sent in all cases (bug #792468). - Clients now have a --quiet option. - The on_disconnect() callback will always be called now, even if the client has disconnected unexpectedly. - Always close persistent DB file after restoring. - Return error code when exiting the clients. - mosquitto_publish() now returns MOSQ_ERR_INVAL if the topic contains + or # - mosquitto now silently rejects published messages with + or # in the topic. - max_connections is now a per-listener setting instead of global. - Connection count is now reduced when clients disconnect (bug #797983). 0.10.2 - 20110106 ================= - Don't abort when connecting if the first connection fails. This is important on e.g. Windows 7, where IPV6 is offered as the first choice but may not be available. - Deal with long logging messages properly (bug #785882). - Fix library compilation on Symbian - no pselect() available. - Don't stop processing subscriptions on received messages after a subscription with # matches. (bug #791206). 0.10.1 - 20110512 ================= - Fix Windows compilation. - Fix mosquitto.py on Windows - call lib init/cleanup. - Don't abort when connecting if given an unknown address type (assuming an IPv4 or IPv6 address is given). 0.10 - 20110429 =============== - Implement support for the password_file option and accompanying authentication requirements in the broker. - Implement topic Access Control Lists. - mosquitto_will_set() and mosquitto_publish() now return MOSQ_ERR_PAYLOAD_SIZE if the payload is too large (>268,435,455 bytes). - Bridge support can now be disabled at compile time. - Group together network writes for outgoing packets - don't send single byte writes! - Add support for clientid_prefixes variable. - Add support for the clientid config variable for controlling bridge client ids. - Remove 32-bit database ID support because htobe64() no longer used. - Multiple client subscriptions to the same topic result in only a single subscription. Bug #744077. 0.9.3 - 20110310 ================ - Set retained message status for QoS 2 messages (bug #726535). - 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. - Don't clean queued messages when a non clean session client reconnects. - Make mosquitto.py compatible with Python <2.6. - Fix mosquitto.h header includes for Windows. 0.9.2 - 20110208 ================ - Only send a single DISCONNECT command when using -l in the pub client. - Set QoS=1 on PUBREL commands to meet protocol spec. - Don't leak sockets on connection failure in the library. - Install man pages when building under cmake. - Fix crash bug on malformed CONNECT message. - Clients are now rejected if their socket peer name cannot be obtained on connection. - Fix a number of potential problems caused when a client with a duplicate id connects. - Install mosquitto.conf under cmake. 0.9.1 - 20101203 ================ - Add missing code for parsing the "bind_address" configuration option. - Fix missing include when compiling with tcp-wrappers support. - Add linker version script for C library to control exported functions. 0.9 - 20101114 ============== - Client and message data is now stored in memory with custom routines rather than a sqlite database. This removes the dependencies on sqlite, pcre and sqlite3-pcre. It also means that the persistent database format has had to be reimplemented in a custom format. Optional support for importing old sqlite databases is provided. - Added IPv6 support for mosquitto and the clients. - Provide username and password support for the clients and client libraries. This is part of the new MQTT v3.1 spec. - The broker supports the username and password connection flags, but will not do anything with the username and password. - Python callback functions now optionally take an extra argument which will return the user object passed to the Mosquitto() constructor, or the calling python object itself if nothing was given to Mosquitto(). - Remove the mosquitto command line option "-i interface". - Remove the mosquitto.conf "interface" variable. - Add support for the listener config variable (replaces the interface variable) - Add support for the bind_address config variable. - Change the port config variable behaviour to match that of rsmb (applies to the default listener only, can be given just once). - Fix QoS 2 protocol compliance - stop sending duplicate messages and handle timeouts correctly. Fixes bug #598290. - Set retain flag correctly for outgoing messages. It should only be set for messages sent in response to a subscribe command (ie. stale data). - Fix bug in returning correct CONNACK result to on_connect client callback. - Don't send client will if it is disconnected for exceeding its keepalive timer. - Fix client library unsubscribe function incorrectly sending a SUBSCRIBE command when it should be UNSUBSCRIBE. - Fix max_inflight_messages and max_queued_messages operation. These parameters now apply only to QoS 1 and 2 messages and are used regardless of the client connection state. - mosquitto.conf now installed to /etc/mosquitto/mosquitto.conf instead of /etc/mosquitto.conf. The /etc/mosquitto/ directory will be used for password and access control files in the future. - Give the compile time option of using 32-bit integers for the database IDs instead of 64-bit integers. This is useful where htobe64()/be64toh() are not available or for embedded systems for example. - The DUP bit is now set correctly when resending PUBREL messages. - A port to Windows native has been partially completed. This currently drops a number of features, including the ability to change configuration parameters and persistent storage. 0.8.3 - 20101004 ================ - Fix QoS 2 protocol compliance - stop sending duplicate messages and handle timeouts correctly. Fixes bug #598290. (backported from future 0.9 code) 0.8.2 - 20100815 ================ - Fix default loop() timeout value in mosquitto.py. Previous value was 0, causing high cpu load. - Fix message handling problem in client library when more than one message was in the client queue. - Fix the logic used to determine whether a QoS>0 message needs to be retried. - Fix the Python sub.py example so that it quits on error. 0.8.1 - 20100812 ================ - Improve python interface - Fix incorrect return value from message delete function - Use logging function to print error messages in clients. - Fix python installation script DESTDIR. - Fix library destination path for 64-bit machines. 0.8 - 20100807 ============== - Topics starting with a / are treated as distinct to those not starting with a /. For example, /topic/path is different to topic/path. This matches the behaviour of rsmb. - Correctly calculate the will QoS on a new client connection (bug #597451). - Add "addresses" configuration file variable as an alias of "address", for better rsmb compatibility. - Bridge clean_session setting is now false, to give more sensible behaviour and be more compatible with rsmb. - Add cleansession variable for configuring bridges. - Add keepalive_interval variable for bridges. - Remove default topic subscription for mosquitto_sub because the old behaviour was too confusing. - Added a C client library, which the pub and sub clients now use. - Added a C++ client library (bound to the C library). - Added a Python client library (bound to the C library). - Added CMake build scripts to allow the library and clients (not the broker) to be compiled natively on Windows. 0.7 - 20100615 ============== - mosquitto_pub can now send null (zero length) messages. - Don't store QoS=0 messages for disconnected clients with subscriptions of QoS>0. - accept() all available sockets when new clients are connecting, rather than just one. - Add option to print debug messages in pub and sub clients. - hg revision is now exported via $SYS/broker/changeset - Send Will when client exceeds keepalive timer and is disconnected. - Check to see if a client has a will before sending it. - Correctly deal with clients connecting with the same id multiple times. - Add compile time option to disable heap memory tracking. - Use poll() instead of select() to allow >1024 clients. - Implement max_connections. - Run VACUUM on in-memory database on receiving SIGUSR2. - Fix bridge keepalive timeouts and reconnects. - Don't attempt to drop root privileges when running on Windows as this isn't well supported (bug #586231). 0.6.1 - 20100506 ================ - Fix DB auto upgrade for messages table. 0.6 - 20100505 ============== - Basic support for connecting multiple MQTT brokers together (bridging). - mosquitto_sub can now subscribe to multiple topics (limited to a global QoS). - mosquitto_pub can now send a file as a message. - mosquitto_pub can now read all of stdin and send it as a message. - mosquitto_pub can now read stdin and send each line as a message. - mosquitto will now correctly run VACUUM on the persistent database on exit. - Implement a more efficient database design, so that only one copy of each message is held in the database, rather than one per subscribed client. - Add the store_cleanup_interval config option for dealing with the internal message store. - Add support for disabling "clean session" for the sub client. - Add support for automatic upgrading of the mosquitto DB from v1 to v2. - Add persistence_file config option to allow changing the filename of the persistence database. This allows multiple mosquitto DBs to be stored in the same location whilst keeping persistence_location compatible with rsmb. - Don't store QoS=0 messages for disconnected clients. Fixes bug #572608. This wasn't correctly fixed in version 0.5. - Don't disconnect clients if they send a PUBLISH with zero length payload (bug #573610). - If a retained message is received with a zero length payload, the retained message for that topic is deleted. - Send through zero length messages. - Produce a warning on unsupported rsmb options instead of quitting. - Describe clean session flag in the mqtt man page. - Implement the max_inflight_messages and max_queued_messages features in the broker. 0.5.4 - 20100311 ================ - Fix memory allocation in mqtt3_fix_sub_topic() (bug #531861). - Remove accidental limit of 100 client connections. - Fix mosquitto_pub handling of messages with QoS>0 (bug #537061). 0.5.3 - 20100303 ================ - Will messages are now only sent when a client disconnects unexpectedly. - Fix all incoming topics/subscriptions that start with a / or contain multiple / in a row (//). - Do actually disconnect client when it sends an empty subscription/topic string. - Add missing $SYS/broker/clients/total to man page. 0.5.2 - 20100302 ================ - Always update last backup time, so that the backup doesn't run every time through the main loop once autosave_interval has been reached. - Report $SYS/broker/uptime in the same format as rsmb. - Make mandatory options obvious in usage output and man page of mosquitto_pub. Fixes bug #529990. - Treat subscriptions with a trailing slash correctly. This should fix bugs #530369 and #530099. 0.5.1 - 20100227 ================ - Must daemonise before pid file is written. 0.5 - 20100227 ============== - No longer store QoS=0 messages for disconnected clients that do not have clean start set. - Rename msg_timeout option to retry_interval for better rsmb compatibility. - Change persistence behaviour. The database is now stored in memory even if persistence is enabled. It is written to disk when mosquitto exits and also at periodic intervals as defined by the new autosave_interval option. - The writing of the persistence database may be forced by sending mosquitto the SIGUSR1 signal. - Clients that do not send CONNECT as their first command are now disconnected. - Boolean configuration values may now be specified with true/false as well as 1/0. - Log message on CONNECT with invalid protocol or protocol version. - Default sqlite3-pcre path on Linux is now /usr/lib/sqlite3/pcre.so to match future sqlite3-pcre packages. - Add mosquitto_sub and mosquitto_pub, simple clients for subscribe/publish. - Add man pages for clients. - Add general man page on mqtt. - Root privileges are now dropped only after attempting to write a pid file (if configured). This means that the pid file can be written to /var/run/ directly and should fix bug #523183. 0.4.2 - 20100203 ================ - Fix segfault on client connect with invalid protocol name/version. 0.4.1 - 20100112 =============== - Fix regex used for finding retained messages to send on new subscription. 0.4 - 20100105 ============== - Added support for wildcard subscriptions using + and #. - All network operations are now non-blocking and can cope with partial packets, meaning that networking should be a lot more reliable. - Total messsages/bytes sent/received are now available in $SYS. - Improved logging information - use client ip address and id instead of socket number. - Broker build timestamp is available in $SYS. - Keepalive==0 is now correctly treated as "never disconnect". - Fixed manpage installation. - Fixed incorrect $SYS hierarchy locations in documentation and code. - Debug type log messages are no longer sent to "topics". - Default logging destination no longer includes "topics" to prevent possible error logging to the db before it is initialised. - Periodic $SYS messages can now be disabled. - stdout and stderr are flushed when logging to them to give more timely updates. - dup is now set correctly when resending messages. - Database format bumped due to topic column naming fix. 0.3 - 20091217 ============== - The port option in the configuration file and --port command line argument may now be given any number of times to make mosquitto listen on multiple sockets. - Add new config file and command line option "interface" to specify an interface to listen on, rather than all interfaces. - Added host access control through tcp-wrappers support. - Set SO_REUSEADDR on the listening socket so restart is much quicker. - Added support for tracking current heap memory usage - this is published on the topic "$SYS/broker/heap/current size" - Added code for logging to stderr, stdout, syslog and topics. - Added logging to numerous places - still plenty of scope for more. 0.2 - 20091204 ============== - Replaced the command line option --foreground with --daemon, swapping the default behaviour. - Added the command line option --config-file, to specify a config file to load. If this is not given, no config file is load and the default options are used. - Added the command line option --port for specifying the port to listen on. This overrides values in the config file. - Don't use persistence by default. - Default behaviour is now more sane when run by a normal user with no command line options (combination of above changes). - Added option user to config file, defaulting to a value of mosquitto. If this value isn't blank and mosquitto is started by root, then it will drop privileges by changing to the user and its primary group. This replaces the current behaviour of refusing to start if run by root. - Fix non-persistent mode, which would never work in the previous release. - Added information on default values of msg_timeout and sys_interval to the mosquitto.conf man page. (closes bug #492045). mosquitto-2.0.18/LICENSE.txt000066400000000000000000000002331450213760600154730ustar00rootroot00000000000000This project is dual licensed under the Eclipse Public License 2.0 and the Eclipse Distribution License 1.0 as described in the epl-v20 and edl-v10 files. mosquitto-2.0.18/Makefile000066400000000000000000000064031450213760600153150ustar00rootroot00000000000000include config.mk DIRS=lib apps client plugins src DOCDIRS=man DISTDIRS=man DISTFILES= \ apps/ \ client/ \ cmake/ \ deps/ \ examples/ \ include/ \ installer/ \ lib/ \ logo/ \ man/ \ misc/ \ plugins/ \ security/ \ service/ \ snap/ \ src/ \ test/ \ \ CMakeLists.txt \ CONTRIBUTING.md \ ChangeLog.txt \ LICENSE.txt \ Makefile \ about.html \ aclfile.example \ config.h \ config.mk \ edl-v10 \ epl-v20 \ libmosquitto.pc.in \ libmosquittopp.pc.in \ mosquitto.conf \ NOTICE.md \ pskfile.example \ pwfile.example \ README-compiling.md \ README-letsencrypt.md \ README-windows.txt \ README.md .PHONY : all mosquitto api docs binary check clean reallyclean test install uninstall dist sign copy localdocker all : $(MAKE_ALL) api : mkdir -p api p naturaldocs -o HTML api -i lib -p p rm -rf p docs : set -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d}; done binary : mosquitto mosquitto : ifeq ($(UNAME),Darwin) $(error Please compile using CMake on Mac OS X) endif set -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done clean : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} clean; done set -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} clean; done $(MAKE) -C test clean reallyclean : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} reallyclean; done set -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} reallyclean; done $(MAKE) -C test reallyclean -rm -f *.orig check : test test : mosquitto $(MAKE) -C test test ptest : mosquitto $(MAKE) -C test ptest utest : mosquitto $(MAKE) -C test utest install : all set -e; for d in ${DIRS}; do $(MAKE) -C $${d} install; done ifeq ($(WITH_DOCS),yes) set -e; for d in ${DOCDIRS}; do $(MAKE) -C $${d} install; done endif $(INSTALL) -d "${DESTDIR}/etc/mosquitto" $(INSTALL) -m 644 mosquitto.conf "${DESTDIR}/etc/mosquitto/mosquitto.conf.example" $(INSTALL) -m 644 aclfile.example "${DESTDIR}/etc/mosquitto/aclfile.example" $(INSTALL) -m 644 pwfile.example "${DESTDIR}/etc/mosquitto/pwfile.example" $(INSTALL) -m 644 pskfile.example "${DESTDIR}/etc/mosquitto/pskfile.example" uninstall : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} uninstall; done rm -f "${DESTDIR}/etc/mosquitto/mosquitto.conf.example" rm -f "${DESTDIR}/etc/mosquitto/aclfile.example" rm -f "${DESTDIR}/etc/mosquitto/pwfile.example" rm -f "${DESTDIR}/etc/mosquitto/pskfile.example" dist : reallyclean set -e; for d in ${DISTDIRS}; do $(MAKE) -C $${d} dist; done mkdir -p dist/mosquitto-${VERSION} cp -r ${DISTFILES} dist/mosquitto-${VERSION}/ cd dist; tar -zcf mosquitto-${VERSION}.tar.gz mosquitto-${VERSION}/ sign : dist cd dist; gpg --detach-sign -a mosquitto-${VERSION}.tar.gz copy : sign cd dist; scp mosquitto-${VERSION}.tar.gz mosquitto-${VERSION}.tar.gz.asc mosquitto:site/mosquitto.org/files/source/ scp ChangeLog.txt mosquitto:site/mosquitto.org/ coverage : lcov --capture --directory . --output-file coverage.info genhtml coverage.info --output-directory out localdocker : reallyclean set -e; for d in ${DISTDIRS}; do $(MAKE) -C $${d} dist; done rm -rf dockertmp/ mkdir -p dockertmp/mosquitto-${VERSION} cp -r ${DISTFILES} dockertmp/mosquitto-${VERSION}/ cd dockertmp/; tar -zcf mosq.tar.gz mosquitto-${VERSION}/ cp dockertmp/mosq.tar.gz docker/local rm -rf dockertmp/ cd docker/local && docker build . -t eclipse-mosquitto:local mosquitto-2.0.18/NOTICE.md000066400000000000000000000035361450213760600151640ustar00rootroot00000000000000# Notices for Mosquitto This content is produced and maintained by the Eclipse Mosquitto project. * Project home: https://projects.eclipse.org/projects/iot.mosquitto ## Trademarks Eclipse Mosquitto trademarks of the Eclipse Foundation. Eclipse, and the Eclipse Logo are registered trademarks of the Eclipse Foundation. ## Copyright All content is the property of the respective authors or their employers. For more information regarding authorship of content, please consult the listed source code repository logs. ## Declared Project Licenses This program and the accompanying materials are made available under the terms of the Eclipse Public License v2.0 which is available at http://www.eclipse.org/legal/epl-v10.html, or the BSD 3 Clause license. SPDX-License-Identifier: EPL-2.0 or BSD-3-Clause ## Source Code The project maintains the following source code repositories: * https://github.com/eclipse/mosquitto ## Third-party Content This project makes use of the follow third party projects. cJSON (1.7.x) * License: MIT * Project: https://github.com/DaveGamble/cJSON libwebsockets (4.x) * License: MIT * Project: https://github.com/warmcat/libwebsockets openssl (1.1.1) * License: OpenSSL License and SSLeay License * Project: https://openssl.org * Source: https://github.com/openssl/openssl uthash (2.1.0) * License: BSD revised (https://troydhanson.github.io/uthash/license.html) * Project: https://github.com/troydhanson/uthash ## Cryptography Content may contain encryption software. The country in which you are currently may have restrictions on the import, possession, and use, and/or re-export to another country, of encryption software. BEFORE using any encryption software, please check the country's laws, regulations and policies concerning the import, possession, or use, and re-export of encryption software, to see if this is permitted. mosquitto-2.0.18/README-compiling.md000066400000000000000000000021331450213760600171070ustar00rootroot00000000000000The following packages can be used to add features to mosquitto. All of them are optional. * openssl * c-ares (for DNS-SRV support, disabled by default) * tcp-wrappers (optional, package name libwrap0-dev) * libwebsockets (optional, disabled by default, version 2.4 and above) * cJSON (optional but recommended, for dynamic-security plugin support, and JSON output from mosquitto_sub/mosquitto_rr) * libsystemd-dev (optional, if building with systemd support on Linux) * On Windows, a pthreads library is required if threading support is to be included. * xsltproc (only if building from git) * docbook-xsl (only if building from git) To compile, run "make", but also see the file config.mk for more details on the various options that can be compiled in. Where possible use the Makefiles to compile. This is particularly relevant for the client libraries as symbol information will be included. Use cmake to compile on Windows or Mac. If you have any questions, problems or suggestions (particularly related to installing on a more unusual device) then please get in touch using the details in README.md. mosquitto-2.0.18/README-letsencrypt.md000066400000000000000000000016531450213760600175100ustar00rootroot00000000000000# Using Lets Encrypt with Mosquitto On Unix like operating systems, Mosquitto will attempt to drop root access as soon as it has loaded its configuration file, but before it has activated any of that configuration. This means that if you are using Lets Encrypt TLS certificates, it will be unable to access the certificates and private keys typically located in /etc/letsencrypt/live/ To help with this problem there is an example `deploy` renewal hook script in `misc/letsencrypt/mosquitto-copy.sh` which shows how the certificate and private key for a mosquitto broker can be copied to /etc/mosquitto/certs/ and given the correct ownership and permissions so the broker can access them, but no other user can. It then signals Mosquitto to reload the certificates. Use of this script allows you to happily use Lets Encrypt certificates with Mosquitto without needing root access for Mosquitto, and without having to restart Mosquitto. mosquitto-2.0.18/README-tests.md000066400000000000000000000012001450213760600162620ustar00rootroot00000000000000# Tests ## Running The Mosquitto test suite can be invoked using either of ``` make test make check ``` The tests run in series and due to the nature of some of the tests being made can take a while. ## Parallel Tests To run the tests with some parallelism, use ``` make ptest ``` This runs up to 20 tests in parallel at once, yielding much faster overall run time at the expense of having up to 20 instances of Python running at once. This is not a particularly CPU intensive option, but does require more memory and may be unsuitable on e.g. a Raspberry Pi. ## Dependencies The tests require Python 3 and CUnit to be installed. mosquitto-2.0.18/README-windows.txt000066400000000000000000000046251450213760600170470ustar00rootroot00000000000000Mosquitto for Windows ===================== Mosquitto for Windows comes in 64-bit and 32-bit flavours. All dependencies are provided in the installer. Installing ---------- Running the installer will present the normal type of graphical installer. If you want to install without starting the graphical part of the installer, you can do so by running it from a cmd prompt with the `/S` switch: ``` mosquitto-2.0.12-install-windows-x64.exe /S ``` You can override the installation directory with the `/D` switch: ``` mosquitto-2.0.12-install-windows-x64.exe /S /D=:\mosquitto ``` Capabilities ------------ Some versions of Windows have limitations on the number of concurrent connections due to the Windows API being used. In modern versions of Windows, e.g. Windows 10 or Windows Server 2019, this is approximately 8192 connections. In earlier versions of Windows, t his limit is 2048 connections. Websockets ---------- The broker executables provided in the installers have Websockets support through a statically compiled version of libwebsockets and is being distributed under the Static Linking Exception (Section 2) of the License. As a result, the content is not subject to the LGPL 2.1. Library Thread Support ---------------------- libmosquitto on Windows is currently compiled without thread support, so neither of mosquitto_loop_start() nor "mosquitto_pub -l" are available. A better solution that the old pthreads-win32 is being looked into, so support will return in the future. If you need thread support, the code still supports it just fine. Support has been dropped to simplify installation. Windows Service --------------- If you wish, mosquitto can be installed as a Windows service so you can start/stop it from the control panel as well as running it as a normal executable. When running as a service, the configuration file used is mosquitto.conf in the directory that you installed to. If you want to install/uninstall mosquitto as a Windows service run from the command line as follows: C:\Program Files\mosquitto\mosquitto install C:\Program Files\mosquitto\mosquitto uninstall Logging ------- If you use `log_dest file ...` in your configuration, the log file will be created with security permissions for the current user only. If running as a service, this means the SYSTEM user. You will only be able to view the log file if you add permissions for yourself or whatever user you wish to view the logs. mosquitto-2.0.18/README.md000066400000000000000000000064151450213760600151370ustar00rootroot00000000000000Eclipse Mosquitto ================= Mosquitto is an open source implementation of a server for version 5.0, 3.1.1, and 3.1 of the MQTT protocol. It also includes a C and C++ client library, and the `mosquitto_pub` and `mosquitto_sub` utilities for publishing and subscribing. ## Links See the following links for more information on MQTT: - Community page: - MQTT v3.1.1 standard: - MQTT v5.0 standard: Mosquitto project information is available at the following locations: - Main homepage: - Find existing bugs or submit a new bug: - Source code repository: There is also a public test server available at ## Installing See for details on installing binaries for various platforms. ## Quick start If you have installed a binary package the broker should have been started automatically. If not, it can be started with a basic configuration: mosquitto Then use `mosquitto_sub` to subscribe to a topic: mosquitto_sub -t 'test/topic' -v And to publish a message: mosquitto_pub -t 'test/topic' -m 'hello world' ## Documentation Documentation for the broker, clients and client library API can be found in the man pages, which are available online at . There are also pages with an introduction to the features of MQTT, the `mosquitto_passwd` utility for dealing with username/passwords, and a description of the configuration file options available for the broker. Detailed client library API documentation can be found at ## Building from source To build from source the recommended route for end users is to download the archive from . On Windows and Mac, use `cmake` to build. On other platforms, just run `make` to build. For Windows, see also `README-windows.md`. If you are building from the git repository then the documentation will not already be built. Use `make binary` to skip building the man pages, or install `docbook-xsl` on Debian/Ubuntu systems. ### Build Dependencies * c-ares (libc-ares-dev on Debian based systems) - only when compiled with `make WITH_SRV=yes` * cJSON - for client JSON output support. Disable with `make WITH_CJSON=no` Auto detected with CMake. * libwebsockets (libwebsockets-dev) - enable with `make WITH_WEBSOCKETS=yes` * openssl (libssl-dev on Debian based systems) - disable with `make WITH_TLS=no` * pthreads - for client library thread support. This is required to support the `mosquitto_loop_start()` and `mosquitto_loop_stop()` functions. If compiled without pthread support, the library isn't guaranteed to be thread safe. * uthash / utlist - bundled versions of these headers are provided, disable their use with `make WITH_BUNDLED_DEPS=no` * xsltproc (xsltproc and docbook-xsl on Debian based systems) - only needed when building from git sources - disable with `make WITH_DOCS=no` Equivalent options for enabling/disabling features are available when using the CMake build. ## Credits Mosquitto was written by Roger Light mosquitto-2.0.18/SECURITY.md000066400000000000000000000003301450213760600154370ustar00rootroot00000000000000# Security Policy ## Reporting a Vulnerability If 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. mosquitto-2.0.18/THANKS.txt000066400000000000000000000027251450213760600154110ustar00rootroot00000000000000These people have reported bugs / provided patches / done something else to aid the mosquitto project. Thanks to you all! If you think I've missed you off the list, please rest assured that it wasn't intentional and get in touch and I'll fix it. Adam Rudd Alexandre Zia Andrew Elwell Andy Piper Andy Stanford-Clark Anton Prokofiev Axel Busch Bart Van Der Meerssche BD Walker Ben Tobin Bob Blackrock Brad Stancel Brett Francis Chris Willing Christian Sampaio Christoph Krey Craig Hollabaugh Cristian Manuel Vertiz Fernandez Dan Anderson Daniel Deadwyler Daniel O'Conner Dariusz Suchojad Darren Oliver David Huang David Monro Dirk O. Kaar Dominik Obermaier Dominik Zajac Ed Morris Fabian Ruff Fang Chong Frank Hansen Gary Koh Gianfranco Costamagna Guido Hinderberger Hiram van Paassen Jan-Piet Mens Joan Zapata Joe McIlvain Karl Palsson Larry Lendo Martin Assarsson Martin Rauscher Martin van Wingerden Marty Lee Matt Daubney Michael C Michael Frisch Michael Hekel Michael Laing Michael Rushton Mike Bush Milan Tucic Neil Bothwick Nicholas Humfrey Nicholas O'Leary Nithin Kumar Patrick Geary Paul Diston Peter Magnusson Pryce Jones Peter George Richard Eagland Rob Pridham Robin Gingras Roland de Boo Sebastian Kroll Sergio Rubio Sharon Ben-Asher sskaje Stefan Hudelmaier Stefano Costa Stephen Owens Stephen Woods Steven Lougheed Surendra Reddy Szymon Kochanski Tammo Besemann Thomas Hilbig Tobias Assarsson Toby Jaffey Tomas Novotny Vicente Ruiz Wayne Ingram Yun Wu Yuvraaj Kelkar 賴 冠廷 mosquitto-2.0.18/about.html000066400000000000000000000047001450213760600156530ustar00rootroot00000000000000 About

About This Content

May 8, 2014

License

The Eclipse Foundation makes available all content in this plug-in ("Content"). Unless otherwise indicated below, the Content is provided to you under the terms and conditions of the Eclipse Public License Version 2.0 ("EPL") and Eclipse Distribution License Version 1.0 ("EDL"). A copy of the EPL is available at https://www.eclipse.org/legal/epl-2.0/ and a copy of the EDL is available at http://www.eclipse.org/org/documents/edl-v10.php. For purposes of the EPL, "Program" will mean the Content.

If you did not receive this Content directly from the Eclipse Foundation, the Content is being redistributed by another party ("Redistributor") and different terms and conditions may apply to your use of any object code in the Content. Check the Redistributor's license that was provided with the Content. If no such license exists, contact the Redistributor. Unless otherwise indicated below, the terms and conditions of the EPL still apply to any source code in the Content and such source code may be obtained at http://www.eclipse.org.

Third Party Content

The Content includes items that have been sourced from third parties as set out below. If you did not receive this Content directly from the Eclipse Foundation, the following is provided for informational purposes only, and you should look to the Redistributor's license for terms and conditions of use.

libwebsockets 2.4.2

This project makes use of the libwebsockets library.

The use of libwebsockets is based on the terms and conditions of the LGPL 2.1 with some specific exceptions. https://github.com/warmcat/libwebsockets/blob/v2.4.2/LICENSE

When libwebsockets is distributed with the project, it is being used subject to the Static Linking Exception (Section 2) of the License. As a result, the content is not subject to the LGPL 2.1.

mosquitto-2.0.18/aclfile.example000066400000000000000000000003461450213760600166310ustar00rootroot00000000000000# This affects access control for clients with no username. topic read $SYS/# # This only affects clients with username "roger". user roger topic foo/bar # This affects all clients. pattern write $SYS/broker/connection/%c/state mosquitto-2.0.18/apps/000077500000000000000000000000001450213760600146155ustar00rootroot00000000000000mosquitto-2.0.18/apps/CMakeLists.txt000066400000000000000000000001041450213760600173500ustar00rootroot00000000000000add_subdirectory(mosquitto_ctrl) add_subdirectory(mosquitto_passwd) mosquitto-2.0.18/apps/Makefile000066400000000000000000000011311450213760600162510ustar00rootroot00000000000000DIRS= \ db_dump \ mosquitto_ctrl \ mosquitto_passwd .PHONY : all binary check clean reallyclean test install uninstall all : set -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done binary : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done clean : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done reallyclean : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done check : test test : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done install : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done uninstall : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done mosquitto-2.0.18/apps/db_dump/000077500000000000000000000000001450213760600162275ustar00rootroot00000000000000mosquitto-2.0.18/apps/db_dump/Makefile000066400000000000000000000050011450213760600176630ustar00rootroot00000000000000include ../../config.mk CFLAGS_FINAL=${CFLAGS} -I../../include -I../../ -I../../lib -I../../src -I../../deps -DWITH_BROKER -DWITH_PERSISTENCE OBJS = \ db_dump.o \ print.o \ \ memory_mosq.o \ memory_public.o \ packet_datatypes.o \ packet_mosq.o \ persist_read.o \ persist_read_v234.o \ persist_read_v5.o \ property_mosq.o \ send_disconnect.o \ stubs.o \ time_mosq.o \ topic_tok.o \ utf8_mosq.o .PHONY: all clean reallyclean all : mosquitto_db_dump mosquitto_db_dump : ${OBJS} ${CROSS_COMPILE}${CC} $^ -o $@ ${LDFLAGS} ${LIBS} db_dump.o : db_dump.c db_dump.h ../../src/persist.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ print.o : print.c db_dump.h ../../src/persist.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ memory_mosq.o : ../../lib/memory_mosq.c ../../lib/memory_mosq.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ memory_public.o : ../../src/memory_public.c ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ net_mosq.o : ../../lib/net_mosq.c ../../lib/net_mosq.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ packet_datatypes.o : ../../lib/packet_datatypes.c ../../lib/packet_mosq.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ packet_mosq.o : ../../lib/packet_mosq.c ../../lib/packet_mosq.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ persist_read.o : ../../src/persist_read.c ../../src/persist.h ../../src/mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ persist_read_v234.o : ../../src/persist_read_v234.c ../../src/persist.h ../../src/mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ persist_read_v5.o : ../../src/persist_read_v5.c ../../src/persist.h ../../src/mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ property_mosq.o : ../../lib/property_mosq.c ../../lib/property_mosq.h ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ read_handle.o : ../../src/read_handle.c ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ stubs.o : stubs.c ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ send_disconnect.o : ../../lib/send_disconnect.c ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ time_mosq.o : ../../lib/time_mosq.c ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ topic_tok.o : ../../src/topic_tok.c ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ utf8_mosq.o : ../../lib/utf8_mosq.c ${CROSS_COMPILE}${CC} $(CFLAGS_FINAL) -c $< -o $@ reallyclean: clean clean : -rm -f *.o mosquitto_db_dump install: uninstall: mosquitto-2.0.18/apps/db_dump/db_dump.c000066400000000000000000000267561450213760600200250ustar00rootroot00000000000000/* Copyright (c) 2010-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include #include #include #include #include #include #include #include #include #include "db_dump.h" #include #include #include #define mosquitto__malloc(A) malloc((A)) #define mosquitto__free(A) free((A)) #define _mosquitto_malloc(A) malloc((A)) #define _mosquitto_free(A) free((A)) #include #include "db_dump.h" struct client_data { UT_hash_handle hh_id; char *id; uint32_t subscriptions; uint32_t subscription_size; int messages; long message_size; }; struct msg_store_chunk { UT_hash_handle hh; dbid_t store_id; uint32_t length; }; struct mosquitto_db db; extern uint32_t db_version; static int stats = 0; static int client_stats = 0; static int do_print = 1; /* Counts */ static long cfg_count = 0; static long client_count = 0; static long client_msg_count = 0; static long msg_store_count = 0; static long retain_count = 0; static long sub_count = 0; /* ====== */ struct client_data *clients_by_id = NULL; struct msg_store_chunk *msgs_by_id = NULL; static void free__sub(struct P_sub *chunk) { free(chunk->client_id); free(chunk->topic); } static void free__client(struct P_client *chunk) { free(chunk->client_id); } static void free__client_msg(struct P_client_msg *chunk) { free(chunk->client_id); mosquitto_property_free_all(&chunk->properties); } static void free__msg_store(struct P_msg_store *chunk) { free(chunk->topic); free(chunk->payload); mosquitto_property_free_all(&chunk->properties); } static int dump__cfg_chunk_process(FILE *db_fd, uint32_t length) { struct PF_cfg chunk; int rc; cfg_count++; memset(&chunk, 0, sizeof(struct PF_cfg)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_cfg_read_v56(db_fd, &chunk); }else{ rc = persist__chunk_cfg_read_v234(db_fd, &chunk); } if(rc){ fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); return rc; } if(do_print) printf("DB_CHUNK_CFG:\n"); if(do_print) printf("\tLength: %d\n", length); if(do_print) printf("\tShutdown: %d\n", chunk.shutdown); if(do_print) printf("\tDB ID size: %d\n", chunk.dbid_size); if(chunk.dbid_size != sizeof(dbid_t)){ fprintf(stderr, "Error: Incompatible database configuration (dbid size is %d bytes, expected %zu)", chunk.dbid_size, sizeof(dbid_t)); fclose(db_fd); return 1; } if(do_print) printf("\tLast DB ID: %" PRIu64 "\n", chunk.last_db_id); return 0; } static int dump__client_chunk_process(FILE *db_fd, uint32_t length) { struct P_client chunk; int rc = 0; struct client_data *cc; client_count++; memset(&chunk, 0, sizeof(struct P_client)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_client_read_v56(db_fd, &chunk, db_version); }else{ rc = persist__chunk_client_read_v234(db_fd, &chunk, db_version); } if(rc){ fprintf(stderr, "Error: Corrupt persistent database."); return rc; } if(client_stats){ cc = calloc(1, sizeof(struct client_data)); if(!cc){ fprintf(stderr, "Error: Out of memory.\n"); fclose(db_fd); free(chunk.client_id); return 1; } cc->id = strdup(chunk.client_id); HASH_ADD_KEYPTR(hh_id, clients_by_id, cc->id, strlen(cc->id), cc); } if(do_print) { print__client(&chunk, length); } free__client(&chunk); return 0; } static int dump__client_msg_chunk_process(FILE *db_fd, uint32_t length) { struct P_client_msg chunk; struct client_data *cc; struct msg_store_chunk *msc; int rc; client_msg_count++; memset(&chunk, 0, sizeof(struct P_client_msg)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_client_msg_read_v56(db_fd, &chunk, length); }else{ rc = persist__chunk_client_msg_read_v234(db_fd, &chunk); } if(rc){ fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); return rc; } if(client_stats){ HASH_FIND(hh_id, clients_by_id, chunk.client_id, strlen(chunk.client_id), cc); if(cc){ cc->messages++; cc->message_size += length; HASH_FIND(hh, msgs_by_id, &chunk.F.store_id, sizeof(dbid_t), msc); if(msc){ cc->message_size += msc->length; } } } if(do_print) { print__client_msg(&chunk, length); } free__client_msg(&chunk); return 0; } static int dump__msg_store_chunk_process(FILE *db_fptr, uint32_t length) { struct P_msg_store chunk; struct mosquitto_msg_store *stored = NULL; struct mosquitto_msg_store_load *load; int64_t message_expiry_interval64; uint32_t message_expiry_interval; int rc = 0; struct msg_store_chunk *mcs; msg_store_count++; memset(&chunk, 0, sizeof(struct P_msg_store)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_msg_store_read_v56(db_fptr, &chunk, length); }else{ rc = persist__chunk_msg_store_read_v234(db_fptr, &chunk, db_version); } if(rc){ fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fptr); return rc; } load = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store_load)); if(!load){ fclose(db_fptr); mosquitto__free(chunk.source.id); mosquitto__free(chunk.source.username); mosquitto__free(chunk.topic); mosquitto__free(chunk.payload); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } if(chunk.F.expiry_time > 0){ message_expiry_interval64 = chunk.F.expiry_time - time(NULL); if(message_expiry_interval64 < 0 || message_expiry_interval64 > UINT32_MAX){ message_expiry_interval = 0; }else{ message_expiry_interval = (uint32_t)message_expiry_interval64; } }else{ message_expiry_interval = 0; } stored = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store)); if(stored == NULL){ mosquitto__free(load); fclose(db_fptr); mosquitto__free(chunk.source.id); mosquitto__free(chunk.source.username); mosquitto__free(chunk.topic); mosquitto__free(chunk.payload); return MOSQ_ERR_NOMEM; } stored->source_mid = chunk.F.source_mid; stored->topic = chunk.topic; stored->qos = chunk.F.qos; stored->retain = chunk.F.retain; stored->payloadlen = chunk.F.payloadlen; stored->payload = chunk.payload; stored->properties = chunk.properties; rc = db__message_store(&chunk.source, stored, message_expiry_interval, chunk.F.store_id, mosq_mo_client); mosquitto__free(chunk.source.id); mosquitto__free(chunk.source.username); chunk.source.id = NULL; chunk.source.username = NULL; if(rc == MOSQ_ERR_SUCCESS){ stored->source_listener = chunk.source.listener; load->db_id = stored->db_id; load->store = stored; HASH_ADD(hh, db.msg_store_load, db_id, sizeof(dbid_t), load); }else{ mosquitto__free(load); fclose(db_fptr); return rc; } if(client_stats){ mcs = calloc(1, sizeof(struct msg_store_chunk)); if(!mcs){ errno = ENOMEM; return 1; } mcs->store_id = chunk.F.store_id; mcs->length = length; HASH_ADD(hh, msgs_by_id, store_id, sizeof(dbid_t), mcs); } if(do_print){ print__msg_store(&chunk, length); } free__msg_store(&chunk); return 0; } static int dump__retain_chunk_process(FILE *db_fd, uint32_t length) { struct P_retain chunk; int rc; retain_count++; if(do_print) printf("DB_CHUNK_RETAIN:\n"); if(do_print) printf("\tLength: %d\n", length); if(db_version == 6 || db_version == 5){ rc = persist__chunk_retain_read_v56(db_fd, &chunk); }else{ rc = persist__chunk_retain_read_v234(db_fd, &chunk); } if(rc){ fclose(db_fd); return rc; } if(do_print) printf("\tStore ID: %" PRIu64 "\n", chunk.F.store_id); return 0; } static int dump__sub_chunk_process(FILE *db_fd, uint32_t length) { int rc = 0; struct P_sub chunk; struct client_data *cc; sub_count++; memset(&chunk, 0, sizeof(struct P_sub)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_sub_read_v56(db_fd, &chunk); }else{ rc = persist__chunk_sub_read_v234(db_fd, &chunk); } if(rc){ fprintf(stderr, "Error: Corrupt persistent database."); fclose(db_fd); return rc; } if(client_stats){ HASH_FIND(hh_id, clients_by_id, chunk.client_id, strlen(chunk.client_id), cc); if(cc){ cc->subscriptions++; cc->subscription_size += length; } } if(do_print) { print__sub(&chunk, length); } free__sub(&chunk); return 0; } int main(int argc, char *argv[]) { FILE *fd; char header[15]; int rc = 0; uint32_t crc; uint32_t i32temp; uint32_t length; uint32_t chunk; char *filename; struct client_data *cc, *cc_tmp; if(argc == 2){ filename = argv[1]; }else if(argc == 3 && !strcmp(argv[1], "--stats")){ stats = 1; do_print = 0; filename = argv[2]; }else if(argc == 3 && !strcmp(argv[1], "--client-stats")){ client_stats = 1; do_print = 0; filename = argv[2]; }else{ fprintf(stderr, "Usage: db_dump [--stats | --client-stats] \n"); return 1; } memset(&db, 0, sizeof(struct mosquitto_db)); fd = fopen(filename, "rb"); if(!fd){ fprintf(stderr, "Error: Unable to open %s\n", filename); return 0; } read_e(fd, &header, 15); if(!memcmp(header, magic, 15)){ if(do_print) printf("Mosquitto DB dump\n"); /* Restore DB as normal */ read_e(fd, &crc, sizeof(uint32_t)); if(do_print) printf("CRC: %d\n", crc); read_e(fd, &i32temp, sizeof(uint32_t)); db_version = ntohl(i32temp); if(do_print) printf("DB version: %d\n", db_version); if(db_version > MOSQ_DB_VERSION){ if(do_print) printf("Warning: mosquitto_db_dump does not support this DB version, continuing but expecting errors.\n"); } while(persist__chunk_header_read(fd, &chunk, &length) == MOSQ_ERR_SUCCESS){ switch(chunk){ case DB_CHUNK_CFG: if(dump__cfg_chunk_process(fd, length)) return 1; break; case DB_CHUNK_MSG_STORE: if(dump__msg_store_chunk_process(fd, length)) return 1; break; case DB_CHUNK_CLIENT_MSG: if(dump__client_msg_chunk_process(fd, length)) return 1; break; case DB_CHUNK_RETAIN: if(dump__retain_chunk_process(fd, length)) return 1; break; case DB_CHUNK_SUB: if(dump__sub_chunk_process(fd, length)) return 1; break; case DB_CHUNK_CLIENT: if(dump__client_chunk_process(fd, length)) return 1; break; default: fprintf(stderr, "Warning: Unsupported chunk \"%d\" in persistent database file. Ignoring.\n", chunk); if(fseek(fd, length, SEEK_CUR) < 0){ fprintf(stderr, "Error seeking in file.\n"); return 1; } break; } } }else{ fprintf(stderr, "Error: Unrecognised file format."); rc = 1; } fclose(fd); if(stats){ printf("DB_CHUNK_CFG: %ld\n", cfg_count); printf("DB_CHUNK_MSG_STORE: %ld\n", msg_store_count); printf("DB_CHUNK_CLIENT_MSG: %ld\n", client_msg_count); printf("DB_CHUNK_RETAIN: %ld\n", retain_count); printf("DB_CHUNK_SUB: %ld\n", sub_count); printf("DB_CHUNK_CLIENT: %ld\n", client_count); } if(client_stats){ HASH_ITER(hh_id, clients_by_id, cc, cc_tmp){ printf("SC: %d SS: %d MC: %d MS: %ld ", cc->subscriptions, cc->subscription_size, cc->messages, cc->message_size); printf("%s\n", cc->id); free(cc->id); } } return rc; error: fprintf(stderr, "Error: %s.", strerror(errno)); if(fd) fclose(fd); return 1; } mosquitto-2.0.18/apps/db_dump/db_dump.h000066400000000000000000000016071450213760600200160ustar00rootroot00000000000000#ifndef DB_DUMP_H #define DB_DUMP_H /* Copyright (c) 2010-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include void print__client(struct P_client *chunk, uint32_t length); void print__client_msg(struct P_client_msg *chunk, uint32_t length); void print__msg_store(struct P_msg_store *chunk, uint32_t length); void print__sub(struct P_sub *chunk, uint32_t length); #endif mosquitto-2.0.18/apps/db_dump/print.c000066400000000000000000000155331450213760600175360ustar00rootroot00000000000000/* Copyright (c) 2010-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include #include #include "db_dump.h" #include #include #include #include #include static void print__properties(mosquitto_property *properties) { int i; if(properties == NULL) return; printf("\tProperties:\n"); while(properties){ switch(properties->identifier){ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: printf("\t\tPayload format indicator: %d\n", properties->value.i8); break; case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: printf("\t\tRequest problem information: %d\n", properties->value.i8); break; case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: printf("\t\tRequest response information: %d\n", properties->value.i8); break; case MQTT_PROP_MAXIMUM_QOS: printf("\t\tMaximum QoS: %d\n", properties->value.i8); break; case MQTT_PROP_RETAIN_AVAILABLE: printf("\t\tRetain available: %d\n", properties->value.i8); break; case MQTT_PROP_WILDCARD_SUB_AVAILABLE: printf("\t\tWildcard sub available: %d\n", properties->value.i8); break; case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: printf("\t\tSubscription ID available: %d\n", properties->value.i8); break; case MQTT_PROP_SHARED_SUB_AVAILABLE: printf("\t\tShared subscription available: %d\n", properties->value.i8); break; case MQTT_PROP_SERVER_KEEP_ALIVE: printf("\t\tServer keep alive: %d\n", properties->value.i16); break; case MQTT_PROP_RECEIVE_MAXIMUM: printf("\t\tReceive maximum: %d\n", properties->value.i16); break; case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: printf("\t\tTopic alias maximum: %d\n", properties->value.i16); break; case MQTT_PROP_TOPIC_ALIAS: printf("\t\tTopic alias: %d\n", properties->value.i16); break; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: printf("\t\tMessage expiry interval: %d\n", properties->value.i32); break; case MQTT_PROP_SESSION_EXPIRY_INTERVAL: printf("\t\tSession expiry interval: %d\n", properties->value.i32); break; case MQTT_PROP_WILL_DELAY_INTERVAL: printf("\t\tWill delay interval: %d\n", properties->value.i32); break; case MQTT_PROP_MAXIMUM_PACKET_SIZE: printf("\t\tMaximum packet size: %d\n", properties->value.i32); break; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: printf("\t\tSubscription identifier: %d\n", properties->value.varint); break; case MQTT_PROP_CONTENT_TYPE: printf("\t\tContent type: %s\n", properties->value.s.v); break; case MQTT_PROP_RESPONSE_TOPIC: printf("\t\tResponse topic: %s\n", properties->value.s.v); break; case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: printf("\t\tAssigned client identifier: %s\n", properties->value.s.v); break; case MQTT_PROP_AUTHENTICATION_METHOD: printf("\t\tAuthentication method: %s\n", properties->value.s.v); break; case MQTT_PROP_RESPONSE_INFORMATION: printf("\t\tResponse information: %s\n", properties->value.s.v); break; case MQTT_PROP_SERVER_REFERENCE: printf("\t\tServer reference: %s\n", properties->value.s.v); break; case MQTT_PROP_REASON_STRING: printf("\t\tReason string: %s\n", properties->value.s.v); break; case MQTT_PROP_AUTHENTICATION_DATA: printf("\t\tAuthentication data: "); for(i=0; ivalue.bin.len; i++){ printf("%02X", properties->value.bin.v[i]); } printf("\n"); break; case MQTT_PROP_CORRELATION_DATA: printf("\t\tCorrelation data: "); for(i=0; ivalue.bin.len; i++){ printf("%02X", properties->value.bin.v[i]); } printf("\n"); break; case MQTT_PROP_USER_PROPERTY: printf("\t\tUser property: %s , %s\n", properties->name.v, properties->value.s.v); break; default: printf("\t\tInvalid property type: %d\n", properties->identifier); break; } properties = properties->next; } } void print__client(struct P_client *chunk, uint32_t length) { printf("DB_CHUNK_CLIENT:\n"); printf("\tLength: %d\n", length); printf("\tClient ID: %s\n", chunk->client_id); if(chunk->username){ printf("\tUsername: %s\n", chunk->username); } if(chunk->F.listener_port > 0){ printf("\tListener port: %u\n", chunk->F.listener_port); } printf("\tLast MID: %d\n", chunk->F.last_mid); printf("\tSession expiry time: %" PRIu64 "\n", chunk->F.session_expiry_time); printf("\tSession expiry interval: %u\n", chunk->F.session_expiry_interval); } void print__client_msg(struct P_client_msg *chunk, uint32_t length) { printf("DB_CHUNK_CLIENT_MSG:\n"); printf("\tLength: %d\n", length); printf("\tClient ID: %s\n", chunk->client_id); printf("\tStore ID: %" PRIu64 "\n", chunk->F.store_id); printf("\tMID: %d\n", chunk->F.mid); printf("\tQoS: %d\n", chunk->F.qos); printf("\tRetain: %d\n", (chunk->F.retain_dup&0xF0)>>4); printf("\tDirection: %d\n", chunk->F.direction); printf("\tState: %d\n", chunk->F.state); printf("\tDup: %d\n", chunk->F.retain_dup&0x0F); print__properties(chunk->properties); } void print__msg_store(struct P_msg_store *chunk, uint32_t length) { uint8_t *payload; printf("DB_CHUNK_MSG_STORE:\n"); printf("\tLength: %d\n", length); printf("\tStore ID: %" PRIu64 "\n", chunk->F.store_id); /* printf("\tSource ID: %s\n", chunk->source_id); */ /* printf("\tSource Username: %s\n", chunk->source_username); */ printf("\tSource Port: %d\n", chunk->F.source_port); printf("\tSource MID: %d\n", chunk->F.source_mid); printf("\tTopic: %s\n", chunk->topic); printf("\tQoS: %d\n", chunk->F.qos); printf("\tRetain: %d\n", chunk->F.retain); printf("\tPayload Length: %d\n", chunk->F.payloadlen); printf("\tExpiry Time: %" PRIu64 "\n", chunk->F.expiry_time); payload = chunk->payload; if(chunk->F.payloadlen < 256){ /* Print payloads with UTF-8 data below an arbitrary limit of 256 bytes */ if(mosquitto_validate_utf8((char *)payload, (uint16_t)chunk->F.payloadlen) == MOSQ_ERR_SUCCESS){ printf("\tPayload: %s\n", payload); } } print__properties(chunk->properties); } void print__sub(struct P_sub *chunk, uint32_t length) { printf("DB_CHUNK_SUB:\n"); printf("\tLength: %u\n", length); printf("\tClient ID: %s\n", chunk->client_id); printf("\tTopic: %s\n", chunk->topic); printf("\tQoS: %d\n", chunk->F.qos); printf("\tSubscription ID: %d\n", chunk->F.identifier); printf("\tOptions: 0x%02X\n", chunk->F.options); } mosquitto-2.0.18/apps/db_dump/stubs.c000066400000000000000000000053761450213760600175460ustar00rootroot00000000000000#include #include #include "misc_mosq.h" #include "mosquitto_broker_internal.h" #include "mosquitto_internal.h" #include "util_mosq.h" #ifndef UNUSED # define UNUSED(A) (void)(A) #endif struct mosquitto *context__init(mosq_sock_t sock) { UNUSED(sock); return NULL; } void context__add_to_by_id(struct mosquitto *context) { UNUSED(context); } int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store *stored, uint32_t message_expiry_interval, dbid_t store_id, enum mosquitto_msg_origin origin) { UNUSED(source); UNUSED(stored); UNUSED(message_expiry_interval); UNUSED(store_id); UNUSED(origin); return 0; } void db__msg_store_ref_inc(struct mosquitto_msg_store *store) { UNUSED(store); } int handle__packet(struct mosquitto *context) { UNUSED(context); return 0; } int log__printf(struct mosquitto *mosq, unsigned int level, const char *fmt, ...) { UNUSED(mosq); UNUSED(level); UNUSED(fmt); return 0; } FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) { UNUSED(path); UNUSED(mode); UNUSED(restrict_read); return NULL; } enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq) { UNUSED(mosq); return mosq_cs_new; } int mux__add_out(struct mosquitto *mosq) { UNUSED(mosq); return 0; } int mux__remove_out(struct mosquitto *mosq) { UNUSED(mosq); return 0; } ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count) { UNUSED(mosq); UNUSED(buf); UNUSED(count); return 0; } ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count) { UNUSED(mosq); UNUSED(buf); UNUSED(count); return 0; } int retain__store(const char *topic, struct mosquitto_msg_store *stored, char **split_topics) { UNUSED(topic); UNUSED(stored); UNUSED(split_topics); return 0; } int sub__add(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier **root) { UNUSED(context); UNUSED(sub); UNUSED(qos); UNUSED(identifier); UNUSED(options); UNUSED(root); return 0; } int sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store **stored) { UNUSED(source_id); UNUSED(topic); UNUSED(qos); UNUSED(retain); UNUSED(stored); return 0; } int keepalive__update(struct mosquitto *context) { UNUSED(context); return 0; } void db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { UNUSED(msg_data); UNUSED(msg); } void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { UNUSED(msg_data); UNUSED(msg); } int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time) { UNUSED(context); UNUSED(expiry_time); return 0; } mosquitto-2.0.18/apps/mosquitto_ctrl/000077500000000000000000000000001450213760600177055ustar00rootroot00000000000000mosquitto-2.0.18/apps/mosquitto_ctrl/CMakeLists.txt000066400000000000000000000026021450213760600224450ustar00rootroot00000000000000if (WITH_TLS AND CJSON_FOUND) add_definitions("-DWITH_CJSON") include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include ${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/src ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${CJSON_INCLUDE_DIRS} ${mosquitto_SOURCE_DIR}/apps/mosquitto_passwd) link_directories(${CJSON_DIR}) add_executable(mosquitto_ctrl mosquitto_ctrl.c mosquitto_ctrl.h client.c dynsec.c dynsec_client.c dynsec_group.c dynsec_role.c ../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h ../../lib/memory_mosq.c ../../lib/memory_mosq.h ../../lib/misc_mosq.c ../../lib/misc_mosq.h ../../src/memory_public.c options.c ../../src/password_mosq.c ../../src/password_mosq.h ) if (WITH_STATIC_LIBRARIES) target_link_libraries(mosquitto_ctrl libmosquitto_static) else() target_link_libraries(mosquitto_ctrl libmosquitto) endif() if (UNIX) if (APPLE) target_link_libraries(mosquitto_ctrl dl) elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") # elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD") # elseif(QNX) # else(APPLE) target_link_libraries(mosquitto_ctrl dl) endif (APPLE) endif (UNIX) target_link_libraries(mosquitto_ctrl ${OPENSSL_LIBRARIES} ${CJSON_LIBRARIES}) install(TARGETS mosquitto_ctrl RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif (WITH_TLS AND CJSON_FOUND) mosquitto-2.0.18/apps/mosquitto_ctrl/Makefile000066400000000000000000000061701450213760600213510ustar00rootroot00000000000000include ../../config.mk .PHONY: all install uninstall clean reallyclean ifeq ($(WITH_SHARED_LIBRARIES),yes) LIBMOSQ:=../../lib/libmosquitto.so.${SOVERSION} else ifeq ($(WITH_THREADING),yes) LIBMOSQ:=../../lib/libmosquitto.a -lpthread -lssl -lcrypto else LIBMOSQ:=../../lib/libmosquitto.a endif endif LOCAL_CPPFLAGS:=-I../mosquitto_passwd -DWITH_CJSON OBJS= mosquitto_ctrl.o \ client.o \ dynsec.o \ dynsec_client.o \ dynsec_group.o \ dynsec_role.o \ get_password.o \ memory_mosq.o \ memory_public.o \ misc_mosq.o \ options.o \ password_mosq.o EXAMPLE_OBJS= example.o ifeq ($(WITH_TLS),yes) ifeq ($(WITH_CJSON),yes) TARGET:=mosquitto_ctrl mosquitto_ctrl_example.so else TARGET:= endif else TARGET:= endif all : ${TARGET} mosquitto_ctrl : ${OBJS} ${LIBMOSQ} ${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD) $(LOCAL_LDFLAGS) $(LIBMOSQ) -lcjson -ldl mosquitto_ctrl_example.so : ${EXAMPLE_OBJS} $(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) -shared $< -o $@ mosquitto_ctrl.o : mosquitto_ctrl.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ client.o : client.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ dynsec.o : dynsec.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ dynsec_client.o : dynsec_client.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ dynsec_group.o : dynsec_group.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ dynsec_role.o : dynsec_role.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ example.o : example.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ get_password.o : ../mosquitto_passwd/get_password.c ../mosquitto_passwd/get_password.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ memory_mosq.o : ../../lib/memory_mosq.c ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ memory_public.o : ../../src/memory_public.c ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ options.o : options.c mosquitto_ctrl.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ misc_mosq.o : ../../lib/misc_mosq.c ../../lib/misc_mosq.h ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ password_mosq.o : ../../src/password_mosq.c ../../src/password_mosq.h ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ ../../lib/libmosquitto.so.${SOVERSION} : $(MAKE) -C ../../lib ../../lib/libmosquitto.a : $(MAKE) -C ../../lib libmosquitto.a install : all ifeq ($(WITH_TLS),yes) ifeq ($(WITH_CJSON),yes) $(INSTALL) -d "${DESTDIR}$(prefix)/bin" $(INSTALL) ${STRIP_OPTS} mosquitto_ctrl "${DESTDIR}${prefix}/bin/mosquitto_ctrl" endif endif uninstall : -rm -f "${DESTDIR}${prefix}/bin/mosquitto_ctrl" clean : -rm -f *.o mosquitto_ctrl *.gcda *.gcno *.so reallyclean : clean -rm -rf *.orig *.db mosquitto-2.0.18/apps/mosquitto_ctrl/client.c000066400000000000000000000102261450213760600213300ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #include #include #include #include "mosquitto_ctrl.h" static int run = 1; static void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties) { struct mosq_ctrl *ctrl = obj; UNUSED(properties); if(ctrl->payload_callback){ ctrl->payload_callback(ctrl, msg->payloadlen, msg->payload); } mosquitto_disconnect_v5(mosq, 0, NULL); run = 0; } static void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { UNUSED(obj); UNUSED(mid); UNUSED(properties); if(reason_code > 127){ fprintf(stderr, "Publish error: %s\n", mosquitto_reason_string(reason_code)); run = 0; mosquitto_disconnect_v5(mosq, 0, NULL); } } static void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos, const mosquitto_property *properties) { struct mosq_ctrl *ctrl = obj; UNUSED(mid); UNUSED(properties); if(qos_count == 1){ if(granted_qos[0] < 128){ /* Success */ mosquitto_publish(mosq, NULL, ctrl->request_topic, (int)strlen(ctrl->payload), ctrl->payload, ctrl->cfg.qos, 0); free(ctrl->request_topic); ctrl->request_topic = NULL; free(ctrl->payload); ctrl->payload = NULL; }else{ if(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){ fprintf(stderr, "Subscribe error: %s\n", mosquitto_reason_string(granted_qos[0])); }else{ fprintf(stderr, "Subscribe error: Subscription refused.\n"); } run = 0; mosquitto_disconnect_v5(mosq, 0, NULL); } }else{ run = 0; mosquitto_disconnect_v5(mosq, 0, NULL); } } static void on_connect(struct mosquitto *mosq, void *obj, int reason_code, int flags, const mosquitto_property *properties) { struct mosq_ctrl *ctrl = obj; UNUSED(flags); UNUSED(properties); if(reason_code == 0){ if(ctrl->response_topic){ mosquitto_subscribe(mosq, NULL, ctrl->response_topic, ctrl->cfg.qos); free(ctrl->response_topic); ctrl->response_topic = NULL; } }else{ if(ctrl->cfg.protocol_version == MQTT_PROTOCOL_V5){ if(reason_code == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){ fprintf(stderr, "Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\n", mosquitto_reason_string(reason_code)); }else{ fprintf(stderr, "Connection error: %s\n", mosquitto_reason_string(reason_code)); } }else{ fprintf(stderr, "Connection error: %s\n", mosquitto_connack_string(reason_code)); } run = 0; mosquitto_disconnect_v5(mosq, 0, NULL); } } int client_request_response(struct mosq_ctrl *ctrl) { struct mosquitto *mosq; int rc; time_t start; if(ctrl->cfg.cafile == NULL && ctrl->cfg.capath == NULL){ fprintf(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"); } mosquitto_lib_init(); mosq = mosquitto_new(ctrl->cfg.id, true, ctrl); rc = client_opts_set(mosq, &ctrl->cfg); if(rc) goto cleanup; mosquitto_connect_v5_callback_set(mosq, on_connect); mosquitto_subscribe_v5_callback_set(mosq, on_subscribe); mosquitto_publish_v5_callback_set(mosq, on_publish); mosquitto_message_v5_callback_set(mosq, on_message); rc = client_connect(mosq, &ctrl->cfg); if(rc) goto cleanup; start = time(NULL); while(run && start+10 > time(NULL)){ mosquitto_loop(mosq, -1, 1); } cleanup: mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return rc; } mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec.c000066400000000000000000000666771450213760600213640ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #ifndef WIN32 # include # include # include #endif #include "mosquitto_ctrl.h" #include "mosquitto.h" #include "password_mosq.h" #include "get_password.h" #include "misc_mosq.h" void dynsec__print_usage(void) { printf("\nDynamic Security module\n"); printf("=======================\n"); printf("\nInitialisation\n--------------\n"); printf("Create a new configuration file with an admin user:\n"); printf(" mosquitto_ctrl dynsec init [admin-password]\n"); printf("\nGeneral\n-------\n"); printf("Get ACL default access: getDefaultACLAccess\n"); printf("Set ACL default access: setDefaultACLAccess allow|deny\n"); printf("Get group for anonymous clients: getAnonymousGroup\n"); printf("Set group for anonymous clients: setAnonymousGroup \n"); printf("\nClients\n-------\n"); printf("Create a new client: createClient [-c clientid] [-p password]\n"); printf("Delete a client: deleteClient \n"); printf("Set a client password: setClientPassword [password]\n"); printf("Set a client id: setClientId [clientid]\n"); printf("Add a role to a client: addClientRole [priority]\n"); printf(" Higher priority (larger numerical value) roles are evaluated first.\n"); printf("Remove role from a client: removeClientRole \n"); printf("Get client information: getClient \n"); printf("List all clients: listClients [count [offset]]\n"); printf("Enable client: enableClient \n"); printf("Disable client: disableClient \n"); printf("\nGroups\n------\n"); printf("Create a new group: createGroup \n"); printf("Delete a group: deleteGroup \n"); printf("Add a role to a group: addGroupRole [priority]\n"); printf(" Higher priority (larger numerical value) roles are evaluated first.\n"); printf("Remove role from a group: removeGroupRole \n"); printf("Add client to a group: addGroupClient [priority]\n"); printf(" Priority sets the group priority for the given client only.\n"); printf(" Higher priority (larger numerical value) groups are evaluated first.\n"); printf("Remove client from a group: removeGroupClient \n"); printf("Get group information: getGroup \n"); printf("List all groups: listGroups [count [offset]]\n"); printf("\nRoles\n------\n"); printf("Create a new role: createRole \n"); printf("Delete a role: deleteRole \n"); printf("Add an ACL to a role: addRoleACL [priority]\n"); printf(" Higher priority (larger numerical value) ACLs are evaluated first.\n"); printf("Remove ACL from a role: removeRoleACL \n"); printf("Get role information: getRole \n"); printf("List all roles: listRoles [count [offset]]\n"); printf("\naclspec: allow|deny\n"); printf("acltype: publishClientSend|publishClientReceive\n"); printf(" |subscribeLiteral|subscribePattern\n"); printf(" |unsubscribeLiteral|unsubscribePattern\n"); printf("\nFor more information see:\n"); printf(" https://mosquitto.org/documentation/dynamic-security/\n\n"); } cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number) { char buf[30]; snprintf(buf, sizeof(buf), "%d", number); return cJSON_AddRawToObject(object, name, buf); } /* ################################################################ * # * # Payload callback * # * ################################################################ */ static void print_list(cJSON *j_response, const char *arrayname, const char *keyname) { cJSON *j_data, *j_array, *j_elem, *j_name; j_data = cJSON_GetObjectItem(j_response, "data"); if(j_data == NULL){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } j_array = cJSON_GetObjectItem(j_data, arrayname); if(j_array == NULL || !cJSON_IsArray(j_array)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } cJSON_ArrayForEach(j_elem, j_array){ if(cJSON_IsObject(j_elem)){ j_name = cJSON_GetObjectItem(j_elem, keyname); if(j_name && cJSON_IsString(j_name)){ printf("%s\n", j_name->valuestring); } }else if(cJSON_IsString(j_elem)){ printf("%s\n", j_elem->valuestring); } } } static void print_roles(cJSON *j_roles, size_t slen) { bool first; cJSON *j_elem, *jtmp; if(j_roles && cJSON_IsArray(j_roles)){ first = true; cJSON_ArrayForEach(j_elem, j_roles){ jtmp = cJSON_GetObjectItem(j_elem, "rolename"); if(jtmp && cJSON_IsString(jtmp)){ if(first){ first = false; printf("%-*s %s", (int)slen, "Roles:", jtmp->valuestring); }else{ printf("%-*s %s", (int)slen, "", jtmp->valuestring); } jtmp = cJSON_GetObjectItem(j_elem, "priority"); if(jtmp && cJSON_IsNumber(jtmp)){ printf(" (priority: %d)", (int)jtmp->valuedouble); }else{ printf(" (priority: -1)"); } printf("\n"); } } }else{ printf("Roles:\n"); } } static void print_client(cJSON *j_response) { cJSON *j_data, *j_client, *j_array, *j_elem, *jtmp; bool first; j_data = cJSON_GetObjectItem(j_response, "data"); if(j_data == NULL || !cJSON_IsObject(j_data)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } j_client = cJSON_GetObjectItem(j_data, "client"); if(j_client == NULL || !cJSON_IsObject(j_client)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } jtmp = cJSON_GetObjectItem(j_client, "username"); if(jtmp == NULL || !cJSON_IsString(jtmp)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } printf("Username: %s\n", jtmp->valuestring); jtmp = cJSON_GetObjectItem(j_client, "clientid"); if(jtmp && cJSON_IsString(jtmp)){ printf("Clientid: %s\n", jtmp->valuestring); }else{ printf("Clientid:\n"); } jtmp = cJSON_GetObjectItem(j_client, "disabled"); if(jtmp && cJSON_IsBool(jtmp)){ printf("Disabled: %s\n", cJSON_IsTrue(jtmp)?"true":"false"); } j_array = cJSON_GetObjectItem(j_client, "roles"); print_roles(j_array, strlen("Username:")); j_array = cJSON_GetObjectItem(j_client, "groups"); if(j_array && cJSON_IsArray(j_array)){ first = true; cJSON_ArrayForEach(j_elem, j_array){ jtmp = cJSON_GetObjectItem(j_elem, "groupname"); if(jtmp && cJSON_IsString(jtmp)){ if(first){ printf("Groups: %s", jtmp->valuestring); first = false; }else{ printf(" %s", jtmp->valuestring); } jtmp = cJSON_GetObjectItem(j_elem, "priority"); if(jtmp && cJSON_IsNumber(jtmp)){ printf(" (priority: %d)", (int)jtmp->valuedouble); }else{ printf(" (priority: -1)"); } printf("\n"); } } }else{ printf("Groups:\n"); } } static void print_group(cJSON *j_response) { cJSON *j_data, *j_group, *j_array, *j_elem, *jtmp; bool first; j_data = cJSON_GetObjectItem(j_response, "data"); if(j_data == NULL || !cJSON_IsObject(j_data)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } j_group = cJSON_GetObjectItem(j_data, "group"); if(j_group == NULL || !cJSON_IsObject(j_group)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } jtmp = cJSON_GetObjectItem(j_group, "groupname"); if(jtmp == NULL || !cJSON_IsString(jtmp)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } printf("Groupname: %s\n", jtmp->valuestring); j_array = cJSON_GetObjectItem(j_group, "roles"); print_roles(j_array, strlen("Groupname:")); j_array = cJSON_GetObjectItem(j_group, "clients"); if(j_array && cJSON_IsArray(j_array)){ first = true; cJSON_ArrayForEach(j_elem, j_array){ jtmp = cJSON_GetObjectItem(j_elem, "username"); if(jtmp && cJSON_IsString(jtmp)){ if(first){ first = false; printf("Clients: %s\n", jtmp->valuestring); }else{ printf(" %s\n", jtmp->valuestring); } } } } } static void print_role(cJSON *j_response) { cJSON *j_data, *j_role, *j_array, *j_elem, *jtmp; bool first; j_data = cJSON_GetObjectItem(j_response, "data"); if(j_data == NULL || !cJSON_IsObject(j_data)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } j_role = cJSON_GetObjectItem(j_data, "role"); if(j_role == NULL || !cJSON_IsObject(j_role)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } jtmp = cJSON_GetObjectItem(j_role, "rolename"); if(jtmp == NULL || !cJSON_IsString(jtmp)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } printf("Rolename: %s\n", jtmp->valuestring); j_array = cJSON_GetObjectItem(j_role, "acls"); if(j_array && cJSON_IsArray(j_array)){ first = true; cJSON_ArrayForEach(j_elem, j_array){ jtmp = cJSON_GetObjectItem(j_elem, "acltype"); if(jtmp && cJSON_IsString(jtmp)){ if(first){ first = false; printf("ACLs: %-20s", jtmp->valuestring); }else{ printf(" %-20s", jtmp->valuestring); } jtmp = cJSON_GetObjectItem(j_elem, "allow"); if(jtmp && cJSON_IsBool(jtmp)){ printf(" : %s", cJSON_IsTrue(jtmp)?"allow":"deny "); } jtmp = cJSON_GetObjectItem(j_elem, "topic"); if(jtmp && cJSON_IsString(jtmp)){ printf(" : %s", jtmp->valuestring); } jtmp = cJSON_GetObjectItem(j_elem, "priority"); if(jtmp && cJSON_IsNumber(jtmp)){ printf(" (priority: %d)", (int)jtmp->valuedouble); }else{ printf(" (priority: -1)"); } printf("\n"); } } } } static void print_anonymous_group(cJSON *j_response) { cJSON *j_data, *j_group, *j_groupname; j_data = cJSON_GetObjectItem(j_response, "data"); if(j_data == NULL || !cJSON_IsObject(j_data)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } j_group = cJSON_GetObjectItem(j_data, "group"); if(j_group == NULL || !cJSON_IsObject(j_group)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } j_groupname = cJSON_GetObjectItem(j_group, "groupname"); if(j_groupname == NULL || !cJSON_IsString(j_groupname)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } printf("%s\n", j_groupname->valuestring); } static void print_default_acl_access(cJSON *j_response) { cJSON *j_data, *j_acls, *j_acl, *j_acltype, *j_allow; j_data = cJSON_GetObjectItem(j_response, "data"); if(j_data == NULL || !cJSON_IsObject(j_data)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } j_acls = cJSON_GetObjectItem(j_data, "acls"); if(j_acls == NULL || !cJSON_IsArray(j_acls)){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } cJSON_ArrayForEach(j_acl, j_acls){ j_acltype = cJSON_GetObjectItem(j_acl, "acltype"); j_allow = cJSON_GetObjectItem(j_acl, "allow"); if(j_acltype == NULL || !cJSON_IsString(j_acltype) || j_allow == NULL || !cJSON_IsBool(j_allow) ){ fprintf(stderr, "Error: Invalid response from server.\n"); return; } printf("%-20s : %s\n", j_acltype->valuestring, cJSON_IsTrue(j_allow)?"allow":"deny"); } } static void dynsec__payload_callback(struct mosq_ctrl *ctrl, long payloadlen, const void *payload) { cJSON *tree, *j_responses, *j_response, *j_command, *j_error; UNUSED(ctrl); #if CJSON_VERSION_FULL < 1007013 UNUSED(payloadlen); tree = cJSON_Parse(payload); #else tree = cJSON_ParseWithLength(payload, (size_t)payloadlen); #endif if(tree == NULL){ fprintf(stderr, "Error: Payload not JSON.\n"); return; } j_responses = cJSON_GetObjectItem(tree, "responses"); if(j_responses == NULL || !cJSON_IsArray(j_responses)){ fprintf(stderr, "Error: Payload missing data.\n"); cJSON_Delete(tree); return; } j_response = cJSON_GetArrayItem(j_responses, 0); if(j_response == NULL){ fprintf(stderr, "Error: Payload missing data.\n"); cJSON_Delete(tree); return; } j_command = cJSON_GetObjectItem(j_response, "command"); if(j_command == NULL){ fprintf(stderr, "Error: Payload missing data.\n"); cJSON_Delete(tree); return; } j_error = cJSON_GetObjectItem(j_response, "error"); if(j_error){ fprintf(stderr, "%s: Error: %s\n", j_command->valuestring, j_error->valuestring); }else{ if(!strcasecmp(j_command->valuestring, "listClients")){ print_list(j_response, "clients", "username"); }else if(!strcasecmp(j_command->valuestring, "listGroups")){ print_list(j_response, "groups", "groupname"); }else if(!strcasecmp(j_command->valuestring, "listRoles")){ print_list(j_response, "roles", "rolename"); }else if(!strcasecmp(j_command->valuestring, "getClient")){ print_client(j_response); }else if(!strcasecmp(j_command->valuestring, "getGroup")){ print_group(j_response); }else if(!strcasecmp(j_command->valuestring, "getRole")){ print_role(j_response); }else if(!strcasecmp(j_command->valuestring, "getDefaultACLAccess")){ print_default_acl_access(j_response); }else if(!strcasecmp(j_command->valuestring, "getAnonymousGroup")){ print_anonymous_group(j_response); }else{ /* fprintf(stderr, "%s: Success\n", j_command->valuestring); */ } } cJSON_Delete(tree); } /* ################################################################ * # * # Default ACL access * # * ################################################################ */ static int dynsec__set_default_acl_access(int argc, char *argv[], cJSON *j_command) { char *acltype, *access; bool b_access; cJSON *j_acls, *j_acl; if(argc == 2){ acltype = argv[0]; access = argv[1]; }else{ return MOSQ_ERR_INVAL; } if(strcasecmp(acltype, "publishClientSend") && strcasecmp(acltype, "publishClientReceive") && strcasecmp(acltype, "subscribe") && strcasecmp(acltype, "unsubscribe")){ return MOSQ_ERR_INVAL; } if(!strcasecmp(access, "allow")){ b_access = true; }else if(!strcasecmp(access, "deny")){ b_access = false; }else{ fprintf(stderr, "Error: access must be \"allow\" or \"deny\".\n"); return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "setDefaultACLAccess") == NULL || (j_acls = cJSON_AddArrayToObject(j_command, "acls")) == NULL ){ return MOSQ_ERR_NOMEM; } j_acl = cJSON_CreateObject(); if(j_acl == NULL){ return MOSQ_ERR_NOMEM; } cJSON_AddItemToArray(j_acls, j_acl); if(cJSON_AddStringToObject(j_acl, "acltype", acltype) == NULL || cJSON_AddBoolToObject(j_acl, "allow", b_access) == NULL ){ return MOSQ_ERR_NOMEM; } return MOSQ_ERR_SUCCESS; } static int dynsec__get_default_acl_access(int argc, char *argv[], cJSON *j_command) { UNUSED(argc); UNUSED(argv); if(cJSON_AddStringToObject(j_command, "command", "getDefaultACLAccess") == NULL ){ return MOSQ_ERR_NOMEM; } return MOSQ_ERR_SUCCESS; } /* ################################################################ * # * # Init * # * ################################################################ */ static cJSON *init_add_acl_to_role(cJSON *j_acls, const char *type, const char *topic) { cJSON *j_acl; j_acl = cJSON_CreateObject(); if(j_acl == NULL) return NULL; if(cJSON_AddStringToObject(j_acl, "acltype", type) == NULL || cJSON_AddStringToObject(j_acl, "topic", topic) == NULL || cJSON_AddBoolToObject(j_acl, "allow", true) == NULL ){ cJSON_Delete(j_acl); return NULL; } cJSON_AddItemToArray(j_acls, j_acl); return j_acl; } static cJSON *init_add_role(const char *rolename) { cJSON *j_role, *j_acls; j_role = cJSON_CreateObject(); if(j_role == NULL){ return NULL; } if(cJSON_AddStringToObject(j_role, "rolename", rolename) == NULL){ cJSON_Delete(j_role); return NULL; } j_acls = cJSON_CreateArray(); if(j_acls == NULL){ cJSON_Delete(j_role); return NULL; } cJSON_AddItemToObject(j_role, "acls", j_acls); if(init_add_acl_to_role(j_acls, "publishClientSend", "$CONTROL/dynamic-security/#") == NULL || init_add_acl_to_role(j_acls, "publishClientReceive", "$CONTROL/dynamic-security/#") == NULL || init_add_acl_to_role(j_acls, "subscribePattern", "$CONTROL/dynamic-security/#") == NULL || init_add_acl_to_role(j_acls, "publishClientReceive", "$SYS/#") == NULL || init_add_acl_to_role(j_acls, "subscribePattern", "$SYS/#") == NULL || init_add_acl_to_role(j_acls, "publishClientReceive", "#") == NULL || init_add_acl_to_role(j_acls, "subscribePattern", "#") == NULL || init_add_acl_to_role(j_acls, "unsubscribePattern", "#") == NULL ){ cJSON_Delete(j_role); return NULL; } return j_role; } static cJSON *init_add_client(const char *username, const char *password, const char *rolename) { cJSON *j_client, *j_roles, *j_role; struct mosquitto_pw pw; char *salt64 = NULL, *hash64 = NULL; char buf[10]; memset(&pw, 0, sizeof(pw)); pw.hashtype = pw_sha512_pbkdf2; if(pw__hash(password, &pw, true, PW_DEFAULT_ITERATIONS) != 0){ return NULL; } if(base64__encode(pw.salt, sizeof(pw.salt), &salt64) || base64__encode(pw.password_hash, sizeof(pw.password_hash), &hash64) ){ fprintf(stderr, "dynsec init: Internal error while encoding password.\n"); free(salt64); free(hash64); return NULL; } j_client = cJSON_CreateObject(); if(j_client == NULL){ free(salt64); free(hash64); return NULL; } snprintf(buf, sizeof(buf), "%d", PW_DEFAULT_ITERATIONS); if(cJSON_AddStringToObject(j_client, "username", username) == NULL || cJSON_AddStringToObject(j_client, "textName", "Dynsec admin user") == NULL || cJSON_AddStringToObject(j_client, "password", hash64) == NULL || cJSON_AddStringToObject(j_client, "salt", salt64) == NULL || cJSON_AddRawToObject(j_client, "iterations", buf) == NULL ){ free(salt64); free(hash64); cJSON_Delete(j_client); return NULL; } free(salt64); free(hash64); j_roles = cJSON_CreateArray(); if(j_roles == NULL){ cJSON_Delete(j_client); return NULL; } cJSON_AddItemToObject(j_client, "roles", j_roles); j_role = cJSON_CreateObject(); if(j_role == NULL){ cJSON_Delete(j_client); return NULL; } cJSON_AddItemToArray(j_roles, j_role); if(cJSON_AddStringToObject(j_role, "rolename", rolename) == NULL){ cJSON_Delete(j_client); return NULL; } return j_client; } static cJSON *init_create(const char *username, const char *password, const char *rolename) { cJSON *tree, *j_clients, *j_client, *j_roles, *j_role; cJSON *j_default_access; tree = cJSON_CreateObject(); if(tree == NULL) return NULL; if((j_clients = cJSON_AddArrayToObject(tree, "clients")) == NULL || (j_roles = cJSON_AddArrayToObject(tree, "roles")) == NULL || (j_default_access = cJSON_AddObjectToObject(tree, "defaultACLAccess")) == NULL ){ cJSON_Delete(tree); return NULL; } /* Set default behaviour: * * Client can not publish to the broker by default. * * Broker *CAN* publish to the client by default. * * Client con not subscribe to topics by default. * * Client *CAN* unsubscribe from topics by default. */ if(cJSON_AddBoolToObject(j_default_access, "publishClientSend", false) == NULL || cJSON_AddBoolToObject(j_default_access, "publishClientReceive", true) == NULL || cJSON_AddBoolToObject(j_default_access, "subscribe", false) == NULL || cJSON_AddBoolToObject(j_default_access, "unsubscribe", true) == NULL ){ cJSON_Delete(tree); return NULL; } j_client = init_add_client(username, password, rolename); if(j_client == NULL){ cJSON_Delete(tree); return NULL; } cJSON_AddItemToArray(j_clients, j_client); j_role = init_add_role(rolename); if(j_role == NULL){ cJSON_Delete(tree); return NULL; } cJSON_AddItemToArray(j_roles, j_role); return tree; } /* mosquitto_ctrl dynsec init [role-name] */ static int dynsec_init(int argc, char *argv[]) { char *filename; char *admin_user; char *admin_password; char *json_str; cJSON *tree; FILE *fptr; char prompt[200], verify_prompt[200]; char password[200]; int rc; if(argc < 2){ fprintf(stderr, "dynsec init: Not enough arguments - filename, or admin-user missing.\n"); return MOSQ_ERR_INVAL; } if(argc > 3){ fprintf(stderr, "dynsec init: Too many arguments.\n"); return MOSQ_ERR_INVAL; } filename = argv[0]; admin_user = argv[1]; if(argc == 3){ admin_password = argv[2]; }else{ snprintf(prompt, sizeof(prompt), "New password for %s: ", admin_user); snprintf(verify_prompt, sizeof(verify_prompt), "Reenter password for %s: ", admin_user); rc = get_password(prompt, verify_prompt, false, password, sizeof(password)); if(rc){ mosquitto_lib_cleanup(); return -1; } admin_password = password; } tree = init_create(admin_user, admin_password, "admin"); if(tree == NULL){ fprintf(stderr, "dynsec init: Out of memory.\n"); return MOSQ_ERR_NOMEM; } json_str = cJSON_Print(tree); cJSON_Delete(tree); #ifdef WIN32 fptr = mosquitto__fopen(filename, "wb", true); #else int fd = open(filename, O_CREAT | O_EXCL | O_WRONLY, 0640); if(fd < 0){ free(json_str); fprintf(stderr, "dynsec init: Unable to open '%s' for writing (%s).\n", filename, strerror(errno)); return -1; } fptr = fdopen(fd, "wb"); #endif if(fptr){ fprintf(fptr, "%s", json_str); free(json_str); fclose(fptr); }else{ free(json_str); fprintf(stderr, "dynsec init: Unable to open '%s' for writing.\n", filename); return -1; } printf("The client '%s' has been created in the file '%s'.\n", admin_user, filename); printf("This client is configured to allow you to administer the dynamic security plugin only.\n"); printf("It does not have access to publish messages to normal topics.\n"); printf("You should create your application clients to do that, for example:\n"); printf(" mosquitto_ctrl dynsec createClient \n"); printf(" mosquitto_ctrl dynsec createRole \n"); printf(" mosquitto_ctrl dynsec addRoleACL publishClientSend my/topic [priority]\n"); printf(" mosquitto_ctrl dynsec addClientRole [priority]\n"); printf("See https://mosquitto.org/documentation/dynamic-security/ for details of all commands.\n"); return -1; /* Suppress client connection */ } /* ################################################################ * # * # Main * # * ################################################################ */ int dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl) { int rc = -1; cJSON *j_tree; cJSON *j_commands, *j_command; if(!strcasecmp(argv[0], "help")){ dynsec__print_usage(); return -1; }else if(!strcasecmp(argv[0], "init")){ return dynsec_init(argc-1, &argv[1]); } /* The remaining commands need a network connection and JSON command. */ ctrl->payload_callback = dynsec__payload_callback; ctrl->request_topic = strdup("$CONTROL/dynamic-security/v1"); ctrl->response_topic = strdup("$CONTROL/dynamic-security/v1/response"); if(ctrl->request_topic == NULL || ctrl->response_topic == NULL){ return MOSQ_ERR_NOMEM; } j_tree = cJSON_CreateObject(); if(j_tree == NULL) return MOSQ_ERR_NOMEM; j_commands = cJSON_AddArrayToObject(j_tree, "commands"); if(j_commands == NULL){ cJSON_Delete(j_tree); j_tree = NULL; return MOSQ_ERR_NOMEM; } j_command = cJSON_CreateObject(); if(j_command == NULL){ cJSON_Delete(j_tree); j_tree = NULL; return MOSQ_ERR_NOMEM; } cJSON_AddItemToArray(j_commands, j_command); if(!strcasecmp(argv[0], "setDefaultACLAccess")){ rc = dynsec__set_default_acl_access(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "getDefaultACLAccess")){ rc = dynsec__get_default_acl_access(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "createClient")){ rc = dynsec_client__create(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "deleteClient")){ rc = dynsec_client__delete(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "getClient")){ rc = dynsec_client__get(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "listClients")){ rc = dynsec_client__list_all(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "setClientId")){ rc = dynsec_client__set_id(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "setClientPassword")){ rc = dynsec_client__set_password(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "addClientRole")){ rc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "removeClientRole")){ rc = dynsec_client__add_remove_role(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "enableClient")){ rc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "disableClient")){ rc = dynsec_client__enable_disable(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "createGroup")){ rc = dynsec_group__create(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "deleteGroup")){ rc = dynsec_group__delete(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "getGroup")){ rc = dynsec_group__get(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "listGroups")){ rc = dynsec_group__list_all(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "addGroupRole")){ rc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "removeGroupRole")){ rc = dynsec_group__add_remove_role(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "addGroupClient")){ rc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "removeGroupClient")){ rc = dynsec_group__add_remove_client(argc-1, &argv[1], j_command, argv[0]); }else if(!strcasecmp(argv[0], "setAnonymousGroup")){ rc = dynsec_group__set_anonymous(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "getAnonymousGroup")){ rc = dynsec_group__get_anonymous(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "createRole")){ rc = dynsec_role__create(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "deleteRole")){ rc = dynsec_role__delete(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "getRole")){ rc = dynsec_role__get(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "listRoles")){ rc = dynsec_role__list_all(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "addRoleACL")){ rc = dynsec_role__add_acl(argc-1, &argv[1], j_command); }else if(!strcasecmp(argv[0], "removeRoleACL")){ rc = dynsec_role__remove_acl(argc-1, &argv[1], j_command); }else{ fprintf(stderr, "Command '%s' not recognised.\n", argv[0]); return MOSQ_ERR_UNKNOWN; } if(rc == MOSQ_ERR_SUCCESS){ ctrl->payload = cJSON_PrintUnformatted(j_tree); cJSON_Delete(j_tree); if(ctrl->payload == NULL){ fprintf(stderr, "Error: Out of memory.\n"); return MOSQ_ERR_NOMEM; } } return rc; } mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec_client.c000066400000000000000000000145141450213760600227010ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include #include #include #include #include "mosquitto.h" #include "mosquitto_ctrl.h" #include "get_password.h" #include "password_mosq.h" int dynsec_client__create(int argc, char *argv[], cJSON *j_command) { char *username = NULL, *password = NULL, *clientid = NULL; char prompt[200], verify_prompt[200]; char password_buf[200]; int rc; int i; bool request_password = true; if(argc == 0){ return MOSQ_ERR_INVAL; } username = argv[0]; for(i=1; i 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL) || (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL) ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec_group.c000066400000000000000000000110251450213760600225510ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include "mosquitto.h" #include "mosquitto_ctrl.h" #include "password_mosq.h" int dynsec_group__create(int argc, char *argv[], cJSON *j_command) { char *groupname = NULL; if(argc == 1){ groupname = argv[0]; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "createGroup") == NULL || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_group__delete(int argc, char *argv[], cJSON *j_command) { char *groupname = NULL; if(argc == 1){ groupname = argv[0]; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "deleteGroup") == NULL || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command) { UNUSED(argc); UNUSED(argv); if(cJSON_AddStringToObject(j_command, "command", "getAnonymousGroup") == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command) { char *groupname = NULL; if(argc == 1){ groupname = argv[0]; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "setAnonymousGroup") == NULL || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_group__get(int argc, char *argv[], cJSON *j_command) { char *groupname = NULL; if(argc == 1){ groupname = argv[0]; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "getGroup") == NULL || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command) { char *groupname = NULL, *rolename = NULL; int priority = -1; if(argc == 2){ groupname = argv[0]; rolename = argv[1]; }else if(argc == 3){ groupname = argv[0]; rolename = argv[1]; priority = atoi(argv[2]); }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", command) == NULL || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL || (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL) ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_group__list_all(int argc, char *argv[], cJSON *j_command) { int count = -1, offset = -1; if(argc == 0){ /* All groups */ }else if(argc == 1){ count = atoi(argv[0]); }else if(argc == 2){ count = atoi(argv[0]); offset = atoi(argv[1]); }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "listGroups") == NULL || (count > 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL) || (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL) ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command) { char *username, *groupname; int priority = -1; if(argc == 2){ groupname = argv[0]; username = argv[1]; }else if(argc == 3){ groupname = argv[0]; username = argv[1]; priority = atoi(argv[2]); }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", command) == NULL || cJSON_AddStringToObject(j_command, "username", username) == NULL || cJSON_AddStringToObject(j_command, "groupname", groupname) == NULL || (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL) ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } mosquitto-2.0.18/apps/mosquitto_ctrl/dynsec_role.c000066400000000000000000000113141450213760600223570ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #ifndef WIN32 # include #endif #include "mosquitto.h" #include "mosquitto_ctrl.h" #include "password_mosq.h" int dynsec_role__create(int argc, char *argv[], cJSON *j_command) { char *rolename = NULL; if(argc == 1){ rolename = argv[0]; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "createRole") == NULL || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_role__delete(int argc, char *argv[], cJSON *j_command) { char *rolename = NULL; if(argc == 1){ rolename = argv[0]; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "deleteRole") == NULL || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_role__get(int argc, char *argv[], cJSON *j_command) { char *rolename = NULL; if(argc == 1){ rolename = argv[0]; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "getRole") == NULL || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_role__list_all(int argc, char *argv[], cJSON *j_command) { int count = -1, offset = -1; if(argc == 0){ /* All roles */ }else if(argc == 1){ count = atoi(argv[0]); }else if(argc == 2){ count = atoi(argv[0]); offset = atoi(argv[1]); }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "listRoles") == NULL || (count > 0 && cJSON_AddIntToObject(j_command, "count", count) == NULL) || (offset > 0 && cJSON_AddIntToObject(j_command, "offset", offset) == NULL) ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command) { char *rolename, *acltype, *topic, *action; bool allow; int priority = -1; if(argc == 5){ rolename = argv[0]; acltype = argv[1]; topic = argv[2]; action = argv[3]; priority = atoi(argv[4]); }else if(argc == 4){ rolename = argv[0]; acltype = argv[1]; topic = argv[2]; action = argv[3]; }else{ return MOSQ_ERR_INVAL; } if(strcasecmp(acltype, "publishClientSend") && strcasecmp(acltype, "publishClientReceive") && strcasecmp(acltype, "subscribeLiteral") && strcasecmp(acltype, "subscribePattern") && strcasecmp(acltype, "unsubscribeLiteral") && strcasecmp(acltype, "unsubscribePattern")){ return MOSQ_ERR_INVAL; } if(!strcasecmp(action, "allow")){ allow = true; }else if(!strcasecmp(action, "deny")){ allow = false; }else{ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "addRoleACL") == NULL || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL || cJSON_AddStringToObject(j_command, "acltype", acltype) == NULL || cJSON_AddStringToObject(j_command, "topic", topic) == NULL || cJSON_AddBoolToObject(j_command, "allow", allow) == NULL || (priority != -1 && cJSON_AddIntToObject(j_command, "priority", priority) == NULL) ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } int dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command) { char *rolename, *acltype, *topic; if(argc == 3){ rolename = argv[0]; acltype = argv[1]; topic = argv[2]; }else{ return MOSQ_ERR_INVAL; } if(strcasecmp(acltype, "publishClientSend") && strcasecmp(acltype, "publishClientReceive") && strcasecmp(acltype, "subscribeLiteral") && strcasecmp(acltype, "subscribePattern") && strcasecmp(acltype, "unsubscribeLiteral") && strcasecmp(acltype, "unsubscribePattern")){ return MOSQ_ERR_INVAL; } if(cJSON_AddStringToObject(j_command, "command", "removeRoleACL") == NULL || cJSON_AddStringToObject(j_command, "rolename", rolename) == NULL || cJSON_AddStringToObject(j_command, "acltype", acltype) == NULL || cJSON_AddStringToObject(j_command, "topic", topic) == NULL ){ return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_SUCCESS; } } mosquitto-2.0.18/apps/mosquitto_ctrl/example.c000066400000000000000000000020771450213760600215120ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #ifndef WIN32 # include #endif #include "mosquitto_ctrl.h" void ctrl_help(void) { printf("\nExample module\n"); printf("==============\n"); printf(" mosquitto_ctrl example help\n"); } int ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl) { UNUSED(argc); UNUSED(ctrl); if(!strcasecmp(argv[0], "help")){ ctrl_help(); return -1; }else{ return MOSQ_ERR_INVAL; } } mosquitto-2.0.18/apps/mosquitto_ctrl/mosquitto_ctrl.c000066400000000000000000000052621450213760600231460ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #ifndef WIN32 # include #endif #include "lib_load.h" #include "mosquitto.h" #include "mosquitto_ctrl.h" static void print_version(void) { int major, minor, revision; mosquitto_lib_version(&major, &minor, &revision); printf("mosquitto_ctrl version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision); } static void print_usage(void) { printf("mosquitto_ctrl is a tool for administering certain Mosquitto features.\n"); print_version(); printf("\nGeneral usage: mosquitto_ctrl \n"); printf("For module specific help use: mosquitto_ctrl help\n"); printf("\nModules available: dynsec\n"); printf("\nFor more information see:\n"); printf(" https://mosquitto.org/man/mosquitto_ctrl-1.html\n\n"); } int main(int argc, char *argv[]) { struct mosq_ctrl ctrl; int rc = MOSQ_ERR_SUCCESS; FUNC_ctrl_main l_ctrl_main = NULL; void *lib = NULL; char lib_name[200]; if(argc == 1){ print_usage(); return 1; } memset(&ctrl, 0, sizeof(ctrl)); init_config(&ctrl.cfg); /* Shift program name out of args */ argc--; argv++; ctrl_config_parse(&ctrl.cfg, &argc, &argv); if(argc < 2){ print_usage(); return 1; } /* In built modules */ if(!strcasecmp(argv[0], "dynsec")){ l_ctrl_main = dynsec__main; }else{ /* Attempt external module */ snprintf(lib_name, sizeof(lib_name), "mosquitto_ctrl_%s.so", argv[0]); lib = LIB_LOAD(lib_name); if(lib){ l_ctrl_main = (FUNC_ctrl_main)LIB_SYM(lib, "ctrl_main"); } } if(l_ctrl_main == NULL){ fprintf(stderr, "Error: Module '%s' not supported.\n", argv[0]); rc = MOSQ_ERR_NOT_SUPPORTED; } if(l_ctrl_main){ rc = l_ctrl_main(argc-1, &argv[1], &ctrl); if(rc < 0){ /* Usage print */ rc = 0; }else if(rc == MOSQ_ERR_SUCCESS){ rc = client_request_response(&ctrl); }else if(rc == MOSQ_ERR_UNKNOWN){ /* Message printed already */ }else{ fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); } } client_config_cleanup(&ctrl.cfg); return rc; } mosquitto-2.0.18/apps/mosquitto_ctrl/mosquitto_ctrl.h000066400000000000000000000077621450213760600231620ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MOSQUITTO_CTRL_H #define MOSQUITTO_CTRL_H #include #include #include "mosquitto.h" #define PORT_UNDEFINED -1 #define PORT_UNIX 0 struct mosq_config { char *id; int protocol_version; int keepalive; char *host; int port; int qos; char *bind_address; bool debug; bool quiet; char *username; char *password; char *options_file; #ifdef WITH_TLS char *cafile; char *capath; char *certfile; char *keyfile; char *ciphers; bool insecure; char *tls_alpn; char *tls_version; char *tls_engine; char *tls_engine_kpass_sha1; char *keyform; # ifdef FINAL_WITH_TLS_PSK char *psk; char *psk_identity; # endif #endif bool verbose; /* sub */ unsigned int timeout; /* sub */ #ifdef WITH_SOCKS char *socks5_host; int socks5_port; char *socks5_username; char *socks5_password; #endif }; struct mosq_ctrl { struct mosq_config cfg; char *request_topic; char *response_topic; char *payload; void (*payload_callback)(struct mosq_ctrl *, long , const void *); void *userdata; }; typedef int (*FUNC_ctrl_main)(int argc, char *argv[], struct mosq_ctrl *ctrl); void init_config(struct mosq_config *cfg); int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[]); int client_config_load(struct mosq_config *cfg); void client_config_cleanup(struct mosq_config *cfg); int client_request_response(struct mosq_ctrl *ctrl); int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg); int client_connect(struct mosquitto *mosq, struct mosq_config *cfg); cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number); void dynsec__print_usage(void); int dynsec__main(int argc, char *argv[], struct mosq_ctrl *ctrl); int dynsec_client__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command); int dynsec_client__create(int argc, char *argv[], cJSON *j_command); int dynsec_client__delete(int argc, char *argv[], cJSON *j_command); int dynsec_client__enable_disable(int argc, char *argv[], cJSON *j_command, const char *command); int dynsec_client__get(int argc, char *argv[], cJSON *j_command); int dynsec_client__list_all(int argc, char *argv[], cJSON *j_command); int dynsec_client__set_id(int argc, char *argv[], cJSON *j_command); int dynsec_client__set_password(int argc, char *argv[], cJSON *j_command); int dynsec_group__add_remove_client(int argc, char *argv[], cJSON *j_command, const char *command); int dynsec_group__add_remove_role(int argc, char *argv[], cJSON *j_command, const char *command); int dynsec_group__create(int argc, char *argv[], cJSON *j_command); int dynsec_group__delete(int argc, char *argv[], cJSON *j_command); int dynsec_group__get(int argc, char *argv[], cJSON *j_command); int dynsec_group__list_all(int argc, char *argv[], cJSON *j_command); int dynsec_group__set_anonymous(int argc, char *argv[], cJSON *j_command); int dynsec_group__get_anonymous(int argc, char *argv[], cJSON *j_command); int dynsec_role__create(int argc, char *argv[], cJSON *j_command); int dynsec_role__delete(int argc, char *argv[], cJSON *j_command); int dynsec_role__get(int argc, char *argv[], cJSON *j_command); int dynsec_role__list_all(int argc, char *argv[], cJSON *j_command); int dynsec_role__add_acl(int argc, char *argv[], cJSON *j_command); int dynsec_role__remove_acl(int argc, char *argv[], cJSON *j_command); /* Functions to implement as an external module: */ void ctrl_help(void); int ctrl_main(int argc, char *argv[], struct mosq_ctrl *ctrl); #endif mosquitto-2.0.18/apps/mosquitto_ctrl/options.c000066400000000000000000000544431450213760600215560ustar00rootroot00000000000000/* Copyright (c) 2014-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #ifndef WIN32 #include #include #else #include #include #define snprintf sprintf_s #define strncasecmp _strnicmp #endif #include #include #include "mosquitto_ctrl.h" #include "get_password.h" #ifdef WITH_SOCKS static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url); #endif static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[]); void init_config(struct mosq_config *cfg) { cfg->qos = 1; cfg->port = PORT_UNDEFINED; cfg->protocol_version = MQTT_PROTOCOL_V5; } void client_config_cleanup(struct mosq_config *cfg) { free(cfg->id); free(cfg->host); free(cfg->bind_address); free(cfg->username); free(cfg->password); free(cfg->options_file); #ifdef WITH_TLS free(cfg->cafile); free(cfg->capath); free(cfg->certfile); free(cfg->keyfile); free(cfg->ciphers); free(cfg->tls_alpn); free(cfg->tls_version); free(cfg->tls_engine); free(cfg->tls_engine_kpass_sha1); free(cfg->keyform); # ifdef FINAL_WITH_TLS_PSK free(cfg->psk); free(cfg->psk_identity); # endif #endif #ifdef WITH_SOCKS free(cfg->socks5_host); free(cfg->socks5_username); free(cfg->socks5_password); #endif } int ctrl_config_parse(struct mosq_config *cfg, int *argc, char **argv[]) { int rc; init_config(cfg); /* Deal with real argc/argv */ rc = client_config_line_proc(cfg, argc, argv); if(rc) return rc; /* Load options from config file - this must be after `-o` has been processed */ rc = client_config_load(cfg); if(rc) return rc; #ifdef WITH_TLS if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){ fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n"); return 1; } if((cfg->keyform && !cfg->keyfile)){ fprintf(stderr, "Error: If keyform is set, keyfile must be also specified.\n"); return 1; } if((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){ fprintf(stderr, "Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\n"); return 1; } #endif #ifdef FINAL_WITH_TLS_PSK if((cfg->cafile || cfg->capath) && cfg->psk){ fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n"); return 1; } if(cfg->psk && !cfg->psk_identity){ fprintf(stderr, "Error: --psk-identity required if --psk used.\n"); return 1; } #endif if(!cfg->host){ cfg->host = strdup("localhost"); if(!cfg->host){ fprintf(stderr, "Error: Out of memory.\n"); return 1; } } return MOSQ_ERR_SUCCESS; } /* Process a tokenised single line from a file or set of real argc/argv */ static int client_config_line_proc(struct mosq_config *cfg, int *argc, char **argvp[]) { char **argv = *argvp; while((*argc) && argv[0][0] == '-'){ if(!strcmp(argv[0], "-A")){ if((*argc) == 1){ fprintf(stderr, "Error: -A argument given but no address specified.\n\n"); return 1; }else{ cfg->bind_address = strdup(argv[1]); } argv++; (*argc)--; #ifdef WITH_TLS }else if(!strcmp(argv[0], "--cafile")){ if((*argc) == 1){ fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n"); return 1; }else{ cfg->cafile = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--capath")){ if((*argc) == 1){ fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n"); return 1; }else{ cfg->capath = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--cert")){ if((*argc) == 1){ fprintf(stderr, "Error: --cert argument given but no file specified.\n\n"); return 1; }else{ cfg->certfile = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--ciphers")){ if((*argc) == 1){ fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n"); return 1; }else{ cfg->ciphers = strdup(argv[1]); } argv++; (*argc)--; #endif }else if(!strcmp(argv[0], "-d") || !strcmp(argv[0], "--debug")){ cfg->debug = true; }else if(!strcmp(argv[0], "--help")){ return 2; }else if(!strcmp(argv[0], "-h") || !strcmp(argv[0], "--host")){ if((*argc) == 1){ fprintf(stderr, "Error: -h argument given but no host specified.\n\n"); return 1; }else{ cfg->host = strdup(argv[1]); } argv++; (*argc)--; #ifdef WITH_TLS }else if(!strcmp(argv[0], "--insecure")){ cfg->insecure = true; #endif }else if(!strcmp(argv[0], "-i") || !strcmp(argv[0], "--id")){ if((*argc) == 1){ fprintf(stderr, "Error: -i argument given but no id specified.\n\n"); return 1; }else{ cfg->id = strdup(argv[1]); } argv++; (*argc)--; #ifdef WITH_TLS }else if(!strcmp(argv[0], "--key")){ if((*argc) == 1){ fprintf(stderr, "Error: --key argument given but no file specified.\n\n"); return 1; }else{ cfg->keyfile = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--keyform")){ if((*argc) == 1){ fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n"); return 1; }else{ cfg->keyform = strdup(argv[1]); } argv++; (*argc)--; #endif }else if(!strcmp(argv[0], "-L") || !strcmp(argv[0], "--url")){ if((*argc) == 1){ fprintf(stderr, "Error: -L argument given but no URL specified.\n\n"); return 1; } else { char *url = argv[1]; char *topic; char *tmp; if(!strncasecmp(url, "mqtt://", 7)) { url += 7; cfg->port = 1883; } else if(!strncasecmp(url, "mqtts://", 8)) { url += 8; cfg->port = 8883; } else { fprintf(stderr, "Error: unsupported URL scheme.\n\n"); return 1; } topic = strchr(url, '/'); if(!topic){ fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n"); return 1; } *topic++ = 0; tmp = strchr(url, '@'); if(tmp) { *tmp++ = 0; char *colon = strchr(url, ':'); if(colon) { *colon = 0; cfg->password = strdup(colon + 1); } cfg->username = strdup(url); url = tmp; } cfg->host = url; tmp = strchr(url, ':'); if(tmp) { *tmp++ = 0; cfg->port = atoi(tmp); } /* Now we've removed the port, time to get the host on the heap */ cfg->host = strdup(cfg->host); } argv++; (*argc)--; }else if(!strcmp(argv[0], "-o")){ if((*argc) == 1){ fprintf(stderr, "Error: -o argument given but no options file specified.\n\n"); return 1; }else{ cfg->options_file = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "-p") || !strcmp(argv[0], "--port")){ if((*argc) == 1){ fprintf(stderr, "Error: -p argument given but no port specified.\n\n"); return 1; }else{ cfg->port = atoi(argv[1]); if(cfg->port<0 || cfg->port>65535){ fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port); return 1; } } argv++; (*argc)--; }else if(!strcmp(argv[0], "-P") || !strcmp(argv[0], "--pw")){ if((*argc) == 1){ fprintf(stderr, "Error: -P argument given but no password specified.\n\n"); return 1; }else{ cfg->password = strdup(argv[1]); } argv++; (*argc)--; #ifdef WITH_SOCKS }else if(!strcmp(argv[0], "--proxy")){ if((*argc) == 1){ fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n"); return 1; }else{ if(mosquitto__parse_socks_url(cfg, argv[1])){ return 1; } } argv++; (*argc)--; #endif #ifdef FINAL_WITH_TLS_PSK }else if(!strcmp(argv[0], "--psk")){ if((*argc) == 1){ fprintf(stderr, "Error: --psk argument given but no key specified.\n\n"); return 1; }else{ cfg->psk = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--psk-identity")){ if((*argc) == 1){ fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n"); return 1; }else{ cfg->psk_identity = strdup(argv[1]); } argv++; (*argc)--; #endif }else if(!strcmp(argv[0], "-q") || !strcmp(argv[0], "--qos")){ if((*argc) == 1){ fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n"); return 1; }else{ cfg->qos = atoi(argv[1]); if(cfg->qos<0 || cfg->qos>2){ fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos); return 1; } } argv++; (*argc)--; }else if(!strcmp(argv[0], "--quiet")){ cfg->quiet = true; #ifdef WITH_TLS }else if(!strcmp(argv[0], "--tls-alpn")){ if((*argc) == 1){ fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n"); return 1; }else{ cfg->tls_alpn = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--tls-engine")){ if((*argc) == 1){ fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n"); return 1; }else{ cfg->tls_engine = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--tls-engine-kpass-sha1")){ if((*argc) == 1){ fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n"); return 1; }else{ cfg->tls_engine_kpass_sha1 = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--tls-version")){ if((*argc) == 1){ fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n"); return 1; }else{ cfg->tls_version = strdup(argv[1]); } argv++; (*argc)--; #endif }else if(!strcmp(argv[0], "-u") || !strcmp(argv[0], "--username")){ if((*argc) == 1){ fprintf(stderr, "Error: -u argument given but no username specified.\n\n"); return 1; }else{ cfg->username = strdup(argv[1]); } argv++; (*argc)--; }else if(!strcmp(argv[0], "--unix")){ if((*argc) == 1){ fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n"); return 1; }else{ cfg->host = strdup(argv[1]); cfg->port = 0; } argv++; (*argc)--; }else if(!strcmp(argv[0], "-V") || !strcmp(argv[0], "--protocol-version")){ if((*argc) == 1){ fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n"); return 1; }else{ if(!strcmp(argv[1], "mqttv31") || !strcmp(argv[1], "31")){ cfg->protocol_version = MQTT_PROTOCOL_V31; }else if(!strcmp(argv[1], "mqttv311") || !strcmp(argv[1], "311")){ cfg->protocol_version = MQTT_PROTOCOL_V311; }else if(!strcmp(argv[1], "mqttv5") || !strcmp(argv[1], "5")){ cfg->protocol_version = MQTT_PROTOCOL_V5; }else{ fprintf(stderr, "Error: Invalid protocol version argument given.\n\n"); return 1; } } argv++; (*argc)--; }else if(!strcmp(argv[0], "-v") || !strcmp(argv[0], "--verbose")){ cfg->verbose = 1; }else if(!strcmp(argv[0], "--version")){ return 3; }else{ goto unknown_option; } argv++; (*argc)--; } *argvp = argv; return MOSQ_ERR_SUCCESS; unknown_option: fprintf(stderr, "Error: Unknown option '%s'.\n",argv[0]); return 1; } static char *get_default_cfg_location(void) { char *loc = NULL; size_t len; #ifndef WIN32 char *env; #else char env[1024]; int rc; #endif #ifndef WIN32 env = getenv("XDG_CONFIG_HOME"); if(env){ len = strlen(env) + strlen("/mosquitto_ctrl") + 1; loc = malloc(len); if(!loc){ fprintf(stderr, "Error: Out of memory.\n"); return NULL; } snprintf(loc, len, "%s/mosquitto_ctrl", env); loc[len-1] = '\0'; }else{ env = getenv("HOME"); if(env){ len = strlen(env) + strlen("/.config/mosquitto_ctrl") + 1; loc = malloc(len); if(!loc){ fprintf(stderr, "Error: Out of memory.\n"); return NULL; } snprintf(loc, len, "%s/.config/mosquitto_ctrl", env); loc[len-1] = '\0'; } } #else rc = GetEnvironmentVariable("USERPROFILE", env, 1024); if(rc > 0 && rc < 1024){ len = strlen(env) + strlen("\\mosquitto_ctrl.conf") + 1; loc = malloc(len); if(!loc){ fprintf(stderr, "Error: Out of memory.\n"); return NULL; } snprintf(loc, len, "%s\\mosquitto_ctrl.conf", env); loc[len-1] = '\0'; } #endif return loc; } int client_config_load(struct mosq_config *cfg) { int rc; FILE *fptr = NULL; char line[1024]; int count; char **local_args, **args; char *default_cfg; if(cfg->options_file){ fptr = fopen(cfg->options_file, "rt"); }else{ default_cfg = get_default_cfg_location(); if(default_cfg){ fptr = fopen(default_cfg, "rt"); free(default_cfg); } } if(fptr){ local_args = malloc(3*sizeof(char *)); if(local_args == NULL){ fprintf(stderr, "Error: Out of memory.\n"); fclose(fptr); return 1; } while(fgets(line, sizeof(line), fptr)){ if(line[0] == '#') continue; /* Comments */ while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){ line[strlen(line)-1] = 0; } local_args[0] = strtok(line, " "); if(local_args[0]){ local_args[1] = strtok(NULL, " "); if(local_args[1]){ count = 2; }else{ count = 1; } args = local_args; rc = client_config_line_proc(cfg, &count, &args); if(rc){ fclose(fptr); free(local_args); return rc; } } } fclose(fptr); free(local_args); } return 0; } int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) { int rc; char prompt[1000]; char password[1000]; mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version); if(cfg->username && cfg->password == NULL){ /* Ask for password */ snprintf(prompt, sizeof(prompt), "Password for %s: ", cfg->username); rc = get_password(prompt, NULL, false, password, sizeof(password)); if(rc){ fprintf(stderr, "Error getting password.\n"); mosquitto_lib_cleanup(); return 1; } cfg->password = strdup(password); if(cfg->password == NULL){ fprintf(stderr, "Error: Out of memory.\n"); mosquitto_lib_cleanup(); return 1; } } if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){ fprintf(stderr, "Error: Problem setting username and/or password.\n"); mosquitto_lib_cleanup(); return 1; } #ifdef WITH_TLS if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){ fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->cafile || cfg->capath){ rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL); if(rc){ if(rc == MOSQ_ERR_INVAL){ fprintf(stderr, "Error: Problem setting TLS options: File not found.\n"); }else{ fprintf(stderr, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc)); } mosquitto_lib_cleanup(); return 1; } } if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){ fprintf(stderr, "Error: Problem setting TLS insecure option.\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){ fprintf(stderr, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){ fprintf(stderr, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){ fprintf(stderr, "Error: Problem setting TLS ALPN protocol.\n"); mosquitto_lib_cleanup(); return 1; } # ifdef FINAL_WITH_TLS_PSK if(cfg->psk && mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){ fprintf(stderr, "Error: Problem setting TLS-PSK options.\n"); mosquitto_lib_cleanup(); return 1; } # endif if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){ fprintf(stderr, "Error: Problem setting TLS options, check the options are valid.\n"); mosquitto_lib_cleanup(); return 1; } #endif #ifdef WITH_SOCKS if(cfg->socks5_host){ rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password); if(rc){ mosquitto_lib_cleanup(); return rc; } } #endif return MOSQ_ERR_SUCCESS; } int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) { #ifndef WIN32 char *err; #else char err[1024]; #endif int rc; int port; if(cfg->port == PORT_UNDEFINED){ #ifdef WITH_TLS if(cfg->cafile || cfg->capath # ifdef FINAL_WITH_TLS_PSK || cfg->psk # endif ){ port = 8883; }else #endif { port = 1883; } }else{ port = cfg->port; } rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, 60, cfg->bind_address, NULL); if(rc>0){ if(rc == MOSQ_ERR_ERRNO){ #ifndef WIN32 err = strerror(errno); #else FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL); #endif fprintf(stderr, "Error: %s\n", err); }else{ fprintf(stderr, "Unable to connect (%s).\n", mosquitto_strerror(rc)); } mosquitto_lib_cleanup(); return rc; } return MOSQ_ERR_SUCCESS; } #ifdef WITH_SOCKS /* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */ static int mosquitto__urldecode(char *str) { int i, j; size_t len; if(!str) return 0; if(!strchr(str, '%')) return 0; len = strlen(str); for(i=0; i= len){ return 1; } if(str[i+1] == '2' && str[i+2] == '5'){ str[i] = '%'; len -= 2; for(j=i+1; j start){ len = i-start; if(host){ /* Have already seen a @ , so this must be of form * socks5h://username[:password]@host:port */ port = malloc(len + 1); if(!port){ fprintf(stderr, "Error: Out of memory.\n"); goto cleanup; } memcpy(port, &(str[start]), len); port[len] = '\0'; }else if(username_or_host){ /* Haven't seen a @ before, so must be of form * socks5h://host:port */ host = username_or_host; username_or_host = NULL; port = malloc(len + 1); if(!port){ fprintf(stderr, "Error: Out of memory.\n"); goto cleanup; } memcpy(port, &(str[start]), len); port[len] = '\0'; }else{ host = malloc(len + 1); if(!host){ fprintf(stderr, "Error: Out of memory.\n"); goto cleanup; } memcpy(host, &(str[start]), len); host[len] = '\0'; } } if(!host){ fprintf(stderr, "Error: Invalid proxy.\n"); goto cleanup; } if(mosquitto__urldecode(username)){ goto cleanup; } if(mosquitto__urldecode(password)){ goto cleanup; } if(port){ port_int = atoi(port); if(port_int < 1 || port_int > 65535){ fprintf(stderr, "Error: Invalid proxy port %d\n", port_int); goto cleanup; } free(port); }else{ port_int = 1080; } cfg->socks5_username = username; cfg->socks5_password = password; cfg->socks5_host = host; cfg->socks5_port = port_int; return 0; cleanup: free(username_or_host); free(username); free(password); free(host); free(port); return 1; } #endif mosquitto-2.0.18/apps/mosquitto_passwd/000077500000000000000000000000001450213760600202425ustar00rootroot00000000000000mosquitto-2.0.18/apps/mosquitto_passwd/CMakeLists.txt000066400000000000000000000011521450213760600230010ustar00rootroot00000000000000include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include ${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/src ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH}) if (WITH_TLS) add_executable(mosquitto_passwd mosquitto_passwd.c get_password.c get_password.h ../../lib/memory_mosq.c ../../lib/memory_mosq.h ../../src/memory_public.c ../../lib/misc_mosq.c ../../src/password_mosq.c ../../src/password_mosq.h ) target_link_libraries(mosquitto_passwd ${OPENSSL_LIBRARIES}) install(TARGETS mosquitto_passwd RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") endif (WITH_TLS) mosquitto-2.0.18/apps/mosquitto_passwd/Makefile000066400000000000000000000025121450213760600217020ustar00rootroot00000000000000include ../../config.mk .PHONY: all install uninstall clean reallyclean OBJS= mosquitto_passwd.o \ get_password.o \ memory_mosq.o \ memory_public.o \ misc_mosq.o \ password_mosq.o ifeq ($(WITH_TLS),yes) all: mosquitto_passwd else all: endif mosquitto_passwd : ${OBJS} ${CROSS_COMPILE}${CC} ${APP_LDFLAGS} $^ -o $@ $(PASSWD_LDADD) mosquitto_passwd.o : mosquitto_passwd.c ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ get_password.o : get_password.c ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ memory_mosq.o : ../../lib/memory_mosq.c ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ memory_public.o : ../../src/memory_public.c ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ misc_mosq.o : ../../lib/misc_mosq.c ../../lib/misc_mosq.h ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ password_mosq.o : ../../src/password_mosq.c ../../src/password_mosq.h ${CROSS_COMPILE}${CC} $(APP_CPPFLAGS) $(APP_CFLAGS) -c $< -o $@ install : all ifeq ($(WITH_TLS),yes) $(INSTALL) -d "${DESTDIR}$(prefix)/bin" $(INSTALL) ${STRIP_OPTS} mosquitto_passwd "${DESTDIR}${prefix}/bin/mosquitto_passwd" endif uninstall : -rm -f "${DESTDIR}${prefix}/bin/mosquitto_passwd" clean : -rm -f *.o mosquitto_passwd *.gcda *.gcno reallyclean : clean -rm -rf *.orig *.db mosquitto-2.0.18/apps/mosquitto_passwd/get_password.c000066400000000000000000000056041450213760600231140ustar00rootroot00000000000000/* Copyright (c) 2012-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #ifdef WIN32 # include # include # define snprintf sprintf_s # include # include #else # include # include # include #endif #include "get_password.h" #define MAX_BUFFER_LEN 65500 #define SALT_LEN 12 void get_password__reset_term(void) { #ifndef WIN32 struct termios ts; tcgetattr(0, &ts); ts.c_lflag |= ECHO | ICANON; tcsetattr(0, TCSANOW, &ts); #endif } static int gets_quiet(char *s, int len) { #ifdef WIN32 HANDLE h; DWORD con_orig, con_quiet = 0; DWORD read_len = 0; memset(s, 0, len); h = GetStdHandle(STD_INPUT_HANDLE); GetConsoleMode(h, &con_orig); con_quiet = con_orig; con_quiet &= ~ENABLE_ECHO_INPUT; con_quiet |= ENABLE_LINE_INPUT; SetConsoleMode(h, con_quiet); if(!ReadConsole(h, s, len, &read_len, NULL)){ SetConsoleMode(h, con_orig); return 1; } while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){ s[strlen(s)-1] = 0; } if(strlen(s) == 0){ return 1; } SetConsoleMode(h, con_orig); return 0; #else struct termios ts_quiet, ts_orig; char *rs; memset(s, 0, (size_t)len); tcgetattr(0, &ts_orig); ts_quiet = ts_orig; ts_quiet.c_lflag &= (unsigned int)(~(ECHO | ICANON)); tcsetattr(0, TCSANOW, &ts_quiet); rs = fgets(s, len, stdin); tcsetattr(0, TCSANOW, &ts_orig); if(!rs){ return 1; }else{ while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){ s[strlen(s)-1] = 0; } if(strlen(s) == 0){ return 1; } } return 0; #endif } int get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len) { char pw1[MAX_BUFFER_LEN], pw2[MAX_BUFFER_LEN]; size_t minLen; minLen = len < MAX_BUFFER_LEN ? len : MAX_BUFFER_LEN; printf("%s", prompt); fflush(stdout); if(gets_quiet(pw1, (int)minLen)){ if(!quiet){ fprintf(stderr, "Error: Empty password.\n"); } return 1; } printf("\n"); if(verify_prompt){ printf("%s", verify_prompt); fflush(stdout); if(gets_quiet(pw2, (int)minLen)){ if(!quiet){ fprintf(stderr, "Error: Empty password.\n"); } return 1; } printf("\n"); if(strcmp(pw1, pw2)){ if(!quiet){ fprintf(stderr, "Error: Passwords do not match.\n"); } return 2; } } strncpy(password, pw1, minLen); return 0; } mosquitto-2.0.18/apps/mosquitto_passwd/get_password.h000066400000000000000000000014431450213760600231160ustar00rootroot00000000000000#ifndef GET_PASSWORD_H #define GET_PASSWORD_H /* Copyright (c) 2012-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include void get_password__reset_term(void); int get_password(const char *prompt, const char *verify_prompt, bool quiet, char *password, size_t len); #endif mosquitto-2.0.18/apps/mosquitto_passwd/mosquitto_passwd.c000066400000000000000000000416511450213760600240420ustar00rootroot00000000000000/* Copyright (c) 2012-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #include #include #include "get_password.h" #include "password_mosq.h" #ifdef WIN32 # include # include # ifndef __cplusplus # if defined(_MSC_VER) && _MSC_VER < 1900 # define bool char # define true 1 # define false 0 # else # include # endif # endif # define snprintf sprintf_s # include # include #else # include # include # include # include #endif #define MAX_BUFFER_LEN 65500 #define SALT_LEN 12 #include "misc_mosq.h" struct cb_helper { const char *line; const char *username; const char *password; int iterations; bool found; }; static enum mosquitto_pwhash_type hashtype = pw_sha512_pbkdf2; #ifdef WIN32 static FILE *mpw_tmpfile(void) { return tmpfile(); } #else static char unsigned alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static unsigned char tmpfile_path[36]; static FILE *mpw_tmpfile(void) { int fd; size_t i; if(RAND_bytes(tmpfile_path, sizeof(tmpfile_path)) != 1){ return NULL; } strcpy((char *)tmpfile_path, "/tmp/"); for(i=strlen((char *)tmpfile_path); iusername)){ /* If this isn't the username to delete, write it to the new file */ fprintf(ftmp, "%s", line); }else{ /* Don't write the matching username to the file. */ helper->found = true; } return 0; } static int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username) { struct cb_helper helper; int rc; memset(&helper, 0, sizeof(helper)); helper.username = username; rc = pwfile_iterate(fptr, ftmp, delete_pwuser_cb, &helper); if(helper.found == false){ fprintf(stderr, "Warning: User %s not found in password file.\n", username); return 1; } return rc; } /* ====================================================================== * Update a plain text password file to use hashes * ====================================================================== */ static int update_file_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) { UNUSED(fptr); UNUSED(line); if(helper){ return output_new_password(ftmp, username, password, helper->iterations); }else{ return output_new_password(ftmp, username, password, PW_DEFAULT_ITERATIONS); } } static int update_file(FILE *fptr, FILE *ftmp) { return pwfile_iterate(fptr, ftmp, update_file_cb, NULL); } /* ====================================================================== * Update an existing user password / create a new password * ====================================================================== */ static int update_pwuser_cb(FILE *fptr, FILE *ftmp, const char *username, const char *password, const char *line, struct cb_helper *helper) { int rc = 0; UNUSED(fptr); UNUSED(password); if(strcmp(username, helper->username)){ /* If this isn't the matching user, then writing out the exiting line */ fprintf(ftmp, "%s", line); }else{ /* Write out a new line for our matching username */ helper->found = true; rc = output_new_password(ftmp, username, helper->password, helper->iterations); } return rc; } static int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password, int iterations) { struct cb_helper helper; int rc; memset(&helper, 0, sizeof(helper)); helper.username = username; helper.password = password; helper.iterations = iterations; rc = pwfile_iterate(fptr, ftmp, update_pwuser_cb, &helper); if(helper.found){ return rc; }else{ return output_new_password(ftmp, username, password, iterations); } } static int copy_contents(FILE *src, FILE *dest) { char buf[MAX_BUFFER_LEN]; size_t len; rewind(src); rewind(dest); #ifdef WIN32 _chsize(fileno(dest), 0); #else if(ftruncate(fileno(dest), 0)) return 1; #endif while(!feof(src)){ len = fread(buf, 1, MAX_BUFFER_LEN, src); if(len > 0){ if(fwrite(buf, 1, len, dest) != len){ return 1; } }else{ return !feof(src); } } return 0; } static int create_backup(char *backup_file, FILE *fptr) { FILE *fbackup; #ifdef WIN32 fbackup = mosquitto__fopen(backup_file, "wt", true); #else int fd; umask(077); fd = mkstemp(backup_file); if(fd < 0){ fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file); return 1; } fbackup = fdopen(fd, "wt"); #endif if(!fbackup){ fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file); return 1; } if(copy_contents(fptr, fbackup)){ fprintf(stderr, "Error copying data to backup password file \"%s\", not continuing.\n", backup_file); fclose(fbackup); return 1; } fclose(fbackup); rewind(fptr); return 0; } static void handle_sigint(int signal) { get_password__reset_term(); UNUSED(signal); exit(0); } static bool is_username_valid(const char *username) { size_t i; size_t slen; if(username){ slen = strlen(username); if(slen > 65535){ fprintf(stderr, "Error: Username must be less than 65536 characters long.\n"); return false; } for(i=0; i 0.\n"); return 1; } }else if(!strcmp(argv[idx], "-U")){ do_update_file = true; }else{ break; } } if(create_new && delete_user){ fprintf(stderr, "Error: -c and -D cannot be used together.\n"); return 1; } if(create_new && do_update_file){ fprintf(stderr, "Error: -c and -U cannot be used together.\n"); return 1; } if(delete_user && do_update_file){ fprintf(stderr, "Error: -D and -U cannot be used together.\n"); return 1; } if(delete_user && batch_mode){ fprintf(stderr, "Error: -b and -D cannot be used together.\n"); return 1; } if(create_new){ if(batch_mode){ if(idx+2 >= argc){ fprintf(stderr, "Error: -c argument given but password file, username, or password missing.\n"); return 1; }else{ password_file_tmp = argv[idx]; username = argv[idx+1]; password_cmd = argv[idx+2]; } }else{ if(idx+1 >= argc){ fprintf(stderr, "Error: -c argument given but password file or username missing.\n"); return 1; }else{ password_file_tmp = argv[idx]; username = argv[idx+1]; } } }else if(delete_user){ if(idx+1 >= argc){ fprintf(stderr, "Error: -D argument given but password file or username missing.\n"); return 1; }else{ password_file_tmp = argv[idx]; username = argv[idx+1]; } }else if(do_update_file){ if(idx+1 != argc){ fprintf(stderr, "Error: -U argument given but password file missing.\n"); return 1; }else{ password_file_tmp = argv[idx]; } }else if(batch_mode == true && idx+3 == argc){ password_file_tmp = argv[idx]; username = argv[idx+1]; password_cmd = argv[idx+2]; }else if(batch_mode == false && idx+2 == argc){ password_file_tmp = argv[idx]; username = argv[idx+1]; }else{ print_usage(); return 1; } if(!is_username_valid(username)){ return 1; } if(password_cmd && strlen(password_cmd) > 65535){ fprintf(stderr, "Error: Password must be less than 65536 characters long.\n"); return 1; } #ifdef WIN32 password_file = _fullpath(NULL, password_file_tmp, 0); if(!password_file){ fprintf(stderr, "Error getting full path for password file.\n"); return 1; } #else password_file = realpath(password_file_tmp, NULL); if(!password_file){ if(errno == ENOENT){ password_file = strdup(password_file_tmp); if(!password_file){ fprintf(stderr, "Error: Out of memory.\n"); return 1; } }else{ fprintf(stderr, "Error reading password file: %s\n", strerror(errno)); return 1; } } #endif if(create_new){ if(batch_mode == false){ rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN); if(rc){ free(password_file); return rc; } password_cmd = password; } fptr = mosquitto__fopen(password_file, "wt", true); if(!fptr){ fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno)); free(password_file); return 1; } free(password_file); rc = output_new_password(fptr, username, password_cmd, iterations); fclose(fptr); return rc; }else{ fptr = mosquitto__fopen(password_file, "r+t", true); if(!fptr){ fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno)); free(password_file); return 1; } size_t len = strlen(password_file) + strlen(".backup.XXXXXX") + 1; backup_file = malloc(len); if(!backup_file){ fprintf(stderr, "Error: Out of memory.\n"); free(password_file); return 1; } snprintf(backup_file, len, "%s.backup.XXXXXX", password_file); free(password_file); password_file = NULL; if(create_backup(backup_file, fptr)){ fclose(fptr); free(backup_file); return 1; } ftmp = mpw_tmpfile(); if(!ftmp){ fprintf(stderr, "Error: Unable to open temporary file. %s.\n", strerror(errno)); fclose(fptr); free(backup_file); return 1; } if(delete_user){ rc = delete_pwuser(fptr, ftmp, username); }else if(do_update_file){ rc = update_file(fptr, ftmp); }else{ if(batch_mode){ /* Update password for individual user */ rc = update_pwuser(fptr, ftmp, username, password_cmd, iterations); }else{ rc = get_password("Password: ", "Reenter password: ", false, password, MAX_BUFFER_LEN); if(rc){ fclose(fptr); fclose(ftmp); unlink(backup_file); free(backup_file); return rc; } /* Update password for individual user */ rc = update_pwuser(fptr, ftmp, username, password, iterations); } } if(rc){ fclose(fptr); fclose(ftmp); unlink(backup_file); free(backup_file); return rc; } if(copy_contents(ftmp, fptr)){ fclose(fptr); fclose(ftmp); fprintf(stderr, "Error occurred updating password file.\n"); fprintf(stderr, "Password file may be corrupt, check the backup file: %s.\n", backup_file); free(backup_file); return 1; } fclose(fptr); fclose(ftmp); /* Everything was ok so backup no longer needed. May contain old * passwords so shouldn't be kept around. */ unlink(backup_file); free(backup_file); } return 0; } mosquitto-2.0.18/appveyor.yml000066400000000000000000000014031450213760600162400ustar00rootroot00000000000000os: Visual Studio 2015 environment: CMAKE_ARGS: -DCMAKE_BUILD_TYPE=Release configuration: - Release install: build: build_script: - md build - cd build - cmake -DCMAKE_BUILD_TYPE=Release -DWITH_THREADING=no .. - cmake --build . --config Release - cd .. after_build: - cd installer - '"%PROGRAMFILES(x86)%\NSIS\makensis" mosquitto.nsi' artifacts: - name: Installer path: 'installer/mosquitto-*-install-windows-x86.exe' #- path: build\src\Release\mosquitto.exe #- path: build\src\Release\mosquitto_passwd.exe #- path: build\lib\Release\mosquitto.dll #- path: build\lib\Release\mosquitto.lib #- path: build\client\Release\mosquitto_pub.exe #- path: build\client\Release\mosquitto_sub.exe #- path: build\src\Release\mosquitto.exe mosquitto-2.0.18/buildtest.py000077500000000000000000000026371450213760600162360ustar00rootroot00000000000000#!/usr/bin/python3 build_variants = [ 'WITH_ADNS', 'WITH_BRIDGE', 'WITH_CJSON', 'WITH_DOCS', 'WITH_EC', 'WITH_EPOLL', 'WITH_MEMORY_TRACKING', 'WITH_PERSISTENCE', 'WITH_SHARED_LIBRARIES', 'WITH_SOCKS', 'WITH_SRV', 'WITH_STATIC_LIBRARIES', 'WITH_STRIP', 'WITH_SYSTEMD', 'WITH_SYS_TREE', 'WITH_THREADING', 'WITH_TLS', 'WITH_TLS_PSK', 'WITH_UNIX_SOCKETS', 'WITH_WEBSOCKETS', 'WITH_WRAP', 'WITH_XTREPORT', ] special_variants = [ 'WITH_BUNDLED_DEPS', 'WITH_COVERAGE', ] import os import random import subprocess def run_test(msg, opts): subprocess.run(["make", "clean"], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) print("%s: %s" % (msg, str(opts))) args = ["make", "-j%d" % (os.cpu_count())] + opts proc = subprocess.run(args, stdout=subprocess.DEVNULL) if proc.returncode != 0: raise RuntimeError("BUILD FAILED: %s" % (' '.join(args))) def simple_tests(): for bv in build_variants: for enabled in ["yes", "no"]: opts = "%s=%s" % (bv, enabled) run_test("SIMPLE BUILD", [opts]) def random_tests(count=10): for i in range(1, count): opts = [] for bv in build_variants: opts.append("%s=%s" % (bv, random.choice(["yes", "no"]))) run_test("RANDOM BUILD", opts) if __name__ == "__main__": simple_tests() random_tests(100) mosquitto-2.0.18/client/000077500000000000000000000000001450213760600151305ustar00rootroot00000000000000mosquitto-2.0.18/client/CMakeLists.txt000066400000000000000000000033571450213760600177000ustar00rootroot00000000000000set(shared_src client_shared.c client_shared.h client_props.c) if (WITH_SRV) add_definitions("-DWITH_SRV") endif (WITH_SRV) set( CLIENT_INC ${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) set( CLIENT_DIR ${mosquitto_BINARY_DIR}/lib) if (CJSON_FOUND) add_definitions("-DWITH_CJSON") set( CLIENT_DIR "${CLIENT_DIR};${CJSON_DIR}" ) set( CLIENT_INC "${CLIENT_INC};${CJSON_INCLUDE_DIRS}" ) endif() include_directories(${CLIENT_INC}) link_directories(${CLIENT_DIR}) add_executable(mosquitto_pub pub_client.c pub_shared.c ${shared_src}) add_executable(mosquitto_sub sub_client.c sub_client_output.c ${shared_src}) add_executable(mosquitto_rr rr_client.c pub_shared.c sub_client_output.c ${shared_src}) if (CJSON_FOUND) target_link_libraries(mosquitto_pub ${CJSON_LIBRARIES}) target_link_libraries(mosquitto_sub ${CJSON_LIBRARIES}) target_link_libraries(mosquitto_rr ${CJSON_LIBRARIES}) endif() if (WITH_STATIC_LIBRARIES) target_link_libraries(mosquitto_pub libmosquitto_static) target_link_libraries(mosquitto_sub libmosquitto_static) target_link_libraries(mosquitto_rr libmosquitto_static) else() target_link_libraries(mosquitto_pub libmosquitto) target_link_libraries(mosquitto_sub libmosquitto) target_link_libraries(mosquitto_rr libmosquitto) endif() if (QNX) target_link_libraries(mosquitto_pub socket) target_link_libraries(mosquitto_sub socket) target_link_libraries(mosquitto_rr socket) endif() install(TARGETS mosquitto_pub RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") install(TARGETS mosquitto_sub RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") install(TARGETS mosquitto_rr RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}") mosquitto-2.0.18/client/Makefile000066400000000000000000000065771450213760600166070ustar00rootroot00000000000000include ../config.mk .PHONY: all install uninstall reallyclean clean static static_pub static_sub static_rr ifeq ($(WITH_SHARED_LIBRARIES),yes) SHARED_DEP:=../lib/libmosquitto.so.${SOVERSION} endif ifeq ($(WITH_SHARED_LIBRARIES),yes) ALL_DEPS:= mosquitto_pub mosquitto_sub mosquitto_rr else ifeq ($(WITH_STATIC_LIBRARIES),yes) ALL_DEPS:= static_pub static_sub static_rr endif endif all : ${ALL_DEPS} static : static_pub static_sub static_rr # This makes mosquitto_pub/sub/rr versions that are statically linked with # libmosquitto only. static_pub : pub_client.o pub_shared.o client_props.o client_shared.o ../lib/libmosquitto.a ${CROSS_COMPILE}${CC} $^ -o mosquitto_pub ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD} static_sub : sub_client.o sub_client_output.o client_props.o client_shared.o ../lib/libmosquitto.a ${CROSS_COMPILE}${CC} $^ -o mosquitto_sub ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD} static_rr : rr_client.o client_props.o client_shared.o pub_shared.o sub_client_output.o ../lib/libmosquitto.a ${CROSS_COMPILE}${CC} $^ -o mosquitto_rr ${CLIENT_LDFLAGS} ${STATIC_LIB_DEPS} ${CLIENT_STATIC_LDADD} mosquitto_pub : pub_client.o pub_shared.o client_shared.o client_props.o ${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD) mosquitto_sub : sub_client.o sub_client_output.o client_shared.o client_props.o ${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD) mosquitto_rr : rr_client.o client_shared.o client_props.o pub_shared.o sub_client_output.o ${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD) pub_client.o : pub_client.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ pub_shared.o : pub_shared.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ sub_client.o : sub_client.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ sub_client_output.o : sub_client_output.c sub_client_output.h ${SHARED_DEP} ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ rr_client.o : rr_client.c ${SHARED_DEP} ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ client_shared.o : client_shared.c client_shared.h ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ client_props.o : client_props.c client_shared.h ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ # The "testing" target is intended to make it easy to compile a quick client # for testing purposes. testing.c should not be committed as a file. testing : testing.o ${CROSS_COMPILE}${CC} $(CLIENT_LDFLAGS) $^ -o $@ $(CLIENT_LDADD) $(CLIENT_LDFLAGS) testing.o : testing.c ${CROSS_COMPILE}${CC} $(CLIENT_CPPFLAGS) $(CLIENT_CFLAGS) -c $< -o $@ ../lib/libmosquitto.so.${SOVERSION} : $(MAKE) -C ../lib ../lib/libmosquitto.a : $(MAKE) -C ../lib libmosquitto.a install : all $(INSTALL) -d "${DESTDIR}$(prefix)/bin" $(INSTALL) ${STRIP_OPTS} mosquitto_pub "${DESTDIR}${prefix}/bin/mosquitto_pub" $(INSTALL) ${STRIP_OPTS} mosquitto_sub "${DESTDIR}${prefix}/bin/mosquitto_sub" $(INSTALL) ${STRIP_OPTS} mosquitto_rr "${DESTDIR}${prefix}/bin/mosquitto_rr" uninstall : -rm -f "${DESTDIR}${prefix}/bin/mosquitto_pub" -rm -f "${DESTDIR}${prefix}/bin/mosquitto_sub" -rm -f "${DESTDIR}${prefix}/bin/mosquitto_rr" reallyclean : clean clean : -rm -f *.o mosquitto_pub mosquitto_sub mosquitto_rr *.gcda *.gcno mosquitto-2.0.18/client/args.txt000066400000000000000000000022551450213760600166310ustar00rootroot00000000000000A - PUB,RR,SUB (bind to address) a B b C - SUB (message count) c - PUB,RR,SUB (clean session) D - PUB,RR,SUB (properties) d - PUB,RR,SUB (debug log) E - SUB (exit after subscribe) e - RR (response topic) F - RR,SUB (output format) f - PUB,RR (file input) G g H h - PUB,RR,SUB (host) I - PUB,RR,SUB (client id prefix) i - PUB,RR,SUB (client id) J j K k - PUB,RR,SUB (keepalive) L - PUB,RR,SUB (connect url) l - PUB (stdin input) M - PUB,RR,SUB (max inflight) m - PUB,RR (message input) N - RR,SUB (no end of line) n - PUB,RR (null message) O o - CTRL (options file) P - PUB,RR,SUB (password) p - PUB,RR,SUB (port) Q q - PUB,RR,SUB (qos) R - RR,SUB (don't show retained) r - PUB,RR (retain) S - PUB,RR,SUB (SRV lookups) s - PUB,RR (stdin input) T - SUB (filter out topic) t - PUB,RR,SUB (topic) U - SUB (unsubscribe) u - PUB,RR,SUB (username) V - PUB,RR,SUB (version output) v - RR,SUB (verbose) W - SUB (timeout) w X x - PUB,RR,SUB (session-expiry-interval) Y y Z z mosquitto-2.0.18/client/client_props.c000066400000000000000000000137171450213760600200060ustar00rootroot00000000000000/* Copyright (c) 2018-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #ifndef WIN32 #include #include #else #include #include #define snprintf sprintf_s #define strncasecmp _strnicmp #endif #include "mosquitto.h" #include "mqtt_protocol.h" #include "client_shared.h" enum prop_type { PROP_TYPE_BYTE, PROP_TYPE_INT16, PROP_TYPE_INT32, PROP_TYPE_BINARY, PROP_TYPE_STRING, PROP_TYPE_STRING_PAIR }; /* This parses property inputs. It should work for any command type, but is limited at the moment. * * Format: * * command property value * command property key value * * Example: * * publish message-expiry-interval 32 * connect user-property key value */ int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx) { char *cmdname = NULL, *propname = NULL; char *key = NULL, *value = NULL; int cmd, identifier, type; mosquitto_property **proplist; int rc; long tmpl; size_t szt; /* idx now points to "command" */ if((*idx)+2 > argc-1){ /* Not enough args */ fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n"); return MOSQ_ERR_INVAL; } cmdname = argv[*idx]; if(mosquitto_string_to_command(cmdname, &cmd)){ fprintf(stderr, "Error: Invalid command given in --property argument.\n\n"); return MOSQ_ERR_INVAL; } propname = argv[(*idx)+1]; if(mosquitto_string_to_property_info(propname, &identifier, &type)){ fprintf(stderr, "Error: Invalid property name given in --property argument.\n\n"); return MOSQ_ERR_INVAL; } if(mosquitto_property_check_command(cmd, identifier)){ fprintf(stderr, "Error: %s property not allowed for %s in --property argument.\n\n", propname, cmdname); return MOSQ_ERR_INVAL; } if(identifier == MQTT_PROP_USER_PROPERTY){ if((*idx)+3 > argc-1){ /* Not enough args */ fprintf(stderr, "Error: --property argument given but not enough arguments specified.\n\n"); return MOSQ_ERR_INVAL; } key = argv[(*idx)+2]; value = argv[(*idx)+3]; (*idx) += 3; }else{ value = argv[(*idx)+2]; (*idx) += 2; } switch(cmd){ case CMD_CONNECT: proplist = &cfg->connect_props; break; case CMD_PUBLISH: if(identifier == MQTT_PROP_TOPIC_ALIAS){ cfg->have_topic_alias = true; } if(identifier == MQTT_PROP_SUBSCRIPTION_IDENTIFIER){ fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); return MOSQ_ERR_INVAL; } proplist = &cfg->publish_props; break; case CMD_SUBSCRIBE: if(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER && identifier != MQTT_PROP_USER_PROPERTY){ fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); return MOSQ_ERR_NOT_SUPPORTED; } proplist = &cfg->subscribe_props; break; case CMD_UNSUBSCRIBE: proplist = &cfg->unsubscribe_props; break; case CMD_DISCONNECT: proplist = &cfg->disconnect_props; break; case CMD_AUTH: fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); return MOSQ_ERR_NOT_SUPPORTED; case CMD_WILL: proplist = &cfg->will_props; break; case CMD_PUBACK: case CMD_PUBREC: case CMD_PUBREL: case CMD_PUBCOMP: case CMD_SUBACK: case CMD_UNSUBACK: fprintf(stderr, "Error: %s property not supported for %s in --property argument.\n\n", propname, cmdname); return MOSQ_ERR_NOT_SUPPORTED; default: return MOSQ_ERR_INVAL; } switch(type){ case MQTT_PROP_TYPE_BYTE: tmpl = atol(value); if(tmpl < 0 || tmpl > UINT8_MAX){ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname); return MOSQ_ERR_INVAL; } rc = mosquitto_property_add_byte(proplist, identifier, (uint8_t )tmpl); break; case MQTT_PROP_TYPE_INT16: tmpl = atol(value); if(tmpl < 0 || tmpl > UINT16_MAX){ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname); return MOSQ_ERR_INVAL; } rc = mosquitto_property_add_int16(proplist, identifier, (uint16_t )tmpl); break; case MQTT_PROP_TYPE_INT32: tmpl = atol(value); if(tmpl < 0 || tmpl > UINT32_MAX){ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname); return MOSQ_ERR_INVAL; } rc = mosquitto_property_add_int32(proplist, identifier, (uint32_t )tmpl); break; case MQTT_PROP_TYPE_VARINT: tmpl = atol(value); if(tmpl < 0 || tmpl > UINT32_MAX){ fprintf(stderr, "Error: Property value (%ld) out of range for property %s.\n\n", tmpl, propname); return MOSQ_ERR_INVAL; } rc = mosquitto_property_add_varint(proplist, identifier, (uint32_t )tmpl); break; case MQTT_PROP_TYPE_BINARY: szt = strlen(value); if(szt > UINT16_MAX){ fprintf(stderr, "Error: Property value too long for property %s.\n\n", propname); return MOSQ_ERR_INVAL; } rc = mosquitto_property_add_binary(proplist, identifier, value, (uint16_t )szt); break; case MQTT_PROP_TYPE_STRING: rc = mosquitto_property_add_string(proplist, identifier, value); break; case MQTT_PROP_TYPE_STRING_PAIR: rc = mosquitto_property_add_string_pair(proplist, identifier, key, value); break; default: return MOSQ_ERR_INVAL; } if(rc){ fprintf(stderr, "Error adding property %s %d\n", propname, type); return rc; } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/client/client_shared.c000066400000000000000000001272331450213760600201100ustar00rootroot00000000000000/* Copyright (c) 2014-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #ifndef WIN32 #include #include #else #include #include #define snprintf sprintf_s #define strncasecmp _strnicmp #endif #include #include #include "client_shared.h" #ifdef WITH_SOCKS static int mosquitto__parse_socks_url(struct mosq_config *cfg, char *url); #endif static int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]); static int check_format(const char *str) { size_t i; size_t len; len = strlen(str); for(i=0; i= '0' && str[i+1] <= '9'){ i++; if(i == len-1){ /* error */ fprintf(stderr, "Error: Incomplete format specifier.\n"); return 1; } } if(str[i+1] == '.'){ /* Precision specifier */ i++; if(i == len-1){ /* error */ fprintf(stderr, "Error: Incomplete format specifier.\n"); return 1; } /* Precision */ while(str[i+1] >= '0' && str[i+1] <= '9'){ i++; if(i == len-1){ /* error */ fprintf(stderr, "Error: Incomplete format specifier.\n"); return 1; } } } if(str[i+1] == '%'){ /* Print %, ignore */ }else if(str[i+1] == 'A'){ /* MQTT v5 property topic-alias */ }else if(str[i+1] == 'C'){ /* MQTT v5 property content-type */ }else if(str[i+1] == 'D'){ /* MQTT v5 property correlation-data */ }else if(str[i+1] == 'E'){ /* MQTT v5 property message-expiry-interval */ }else if(str[i+1] == 'F'){ /* MQTT v5 property payload-format-indicator */ }else if(str[i+1] == 'I'){ /* ISO 8601 date+time */ }else if(str[i+1] == 'l'){ /* payload length */ }else if(str[i+1] == 'm'){ /* mid */ }else if(str[i+1] == 'P'){ /* MQTT v5 property user-property */ }else if(str[i+1] == 'p'){ /* payload */ }else if(str[i+1] == 'q'){ /* qos */ }else if(str[i+1] == 'R'){ /* MQTT v5 property response-topic */ }else if(str[i+1] == 'S'){ /* MQTT v5 property subscription-identifier */ }else if(str[i+1] == 'r'){ /* retain */ }else if(str[i+1] == 't'){ /* topic */ }else if(str[i+1] == 'j'){ /* JSON output, escaped payload */ }else if(str[i+1] == 'J'){ /* JSON output, assuming JSON payload */ }else if(str[i+1] == 'U'){ /* Unix time+nanoseconds */ #ifdef WIN32 fprintf(stderr, "Error: The %%U format option is not supported on Windows.\n"); return 1; #endif }else if(str[i+1] == 'x' || str[i+1] == 'X'){ /* payload in hex */ }else{ fprintf(stderr, "Error: Invalid format specifier '%c'.\n", str[i+1]); return 1; } i++; } }else if(str[i] == '@'){ if(i == len-1){ /* error */ fprintf(stderr, "Error: Incomplete format specifier.\n"); return 1; } i++; }else if(str[i] == '\\'){ if(i == len-1){ /* error */ fprintf(stderr, "Error: Incomplete escape specifier.\n"); return 1; }else{ switch(str[i+1]){ case '\\': /* '\' */ case '0': /* 0 (NULL) */ case 'a': /* alert */ case 'e': /* escape */ case 'n': /* new line */ case 'r': /* carriage return */ case 't': /* horizontal tab */ case 'v': /* vertical tab */ break; default: fprintf(stderr, "Error: Invalid escape specifier '%c'.\n", str[i+1]); return 1; } i++; } } } return 0; } static void init_config(struct mosq_config *cfg, int pub_or_sub) { memset(cfg, 0, sizeof(*cfg)); cfg->port = PORT_UNDEFINED; cfg->max_inflight = 20; cfg->keepalive = 60; cfg->clean_session = true; cfg->eol = true; cfg->repeat_count = 1; cfg->repeat_delay.tv_sec = 0; cfg->repeat_delay.tv_usec = 0; cfg->random_filter = 10000; if(pub_or_sub == CLIENT_RR){ cfg->protocol_version = MQTT_PROTOCOL_V5; cfg->msg_count = 1; }else{ cfg->protocol_version = MQTT_PROTOCOL_V311; } cfg->session_expiry_interval = -1; /* -1 means unset here, the user can't set it to -1. */ } void client_config_cleanup(struct mosq_config *cfg) { int i; free(cfg->id); free(cfg->id_prefix); free(cfg->host); free(cfg->file_input); free(cfg->message); free(cfg->topic); free(cfg->bind_address); free(cfg->username); free(cfg->password); free(cfg->will_topic); free(cfg->will_payload); free(cfg->format); free(cfg->response_topic); #ifdef WITH_TLS free(cfg->cafile); free(cfg->capath); free(cfg->certfile); free(cfg->keyfile); free(cfg->ciphers); free(cfg->tls_alpn); free(cfg->tls_version); free(cfg->tls_engine); free(cfg->tls_engine_kpass_sha1); free(cfg->keyform); # ifdef FINAL_WITH_TLS_PSK free(cfg->psk); free(cfg->psk_identity); # endif #endif if(cfg->topics){ for(i=0; itopic_count; i++){ free(cfg->topics[i]); } free(cfg->topics); } if(cfg->filter_outs){ for(i=0; ifilter_out_count; i++){ free(cfg->filter_outs[i]); } free(cfg->filter_outs); } if(cfg->unsub_topics){ for(i=0; iunsub_topic_count; i++){ free(cfg->unsub_topics[i]); } free(cfg->unsub_topics); } #ifdef WITH_SOCKS free(cfg->socks5_host); free(cfg->socks5_username); free(cfg->socks5_password); #endif mosquitto_property_free_all(&cfg->connect_props); mosquitto_property_free_all(&cfg->publish_props); mosquitto_property_free_all(&cfg->subscribe_props); mosquitto_property_free_all(&cfg->unsubscribe_props); mosquitto_property_free_all(&cfg->disconnect_props); mosquitto_property_free_all(&cfg->will_props); } int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]) { int rc; FILE *fptr; char line[1024]; int count; char *loc = NULL; size_t len; char *args[3]; #ifndef WIN32 char *env; #else char env[1024]; #endif args[0] = NULL; init_config(cfg, pub_or_sub); /* Default config file */ #ifndef WIN32 env = getenv("XDG_CONFIG_HOME"); if(env){ len = strlen(env) + strlen("/mosquitto_pub") + 1; loc = malloc(len); if(!loc){ err_printf(cfg, "Error: Out of memory.\n"); return 1; } if(pub_or_sub == CLIENT_PUB){ snprintf(loc, len, "%s/mosquitto_pub", env); }else if(pub_or_sub == CLIENT_SUB){ snprintf(loc, len, "%s/mosquitto_sub", env); }else{ snprintf(loc, len, "%s/mosquitto_rr", env); } loc[len-1] = '\0'; }else{ env = getenv("HOME"); if(env){ len = strlen(env) + strlen("/.config/mosquitto_pub") + 1; loc = malloc(len); if(!loc){ err_printf(cfg, "Error: Out of memory.\n"); return 1; } if(pub_or_sub == CLIENT_PUB){ snprintf(loc, len, "%s/.config/mosquitto_pub", env); }else if(pub_or_sub == CLIENT_SUB){ snprintf(loc, len, "%s/.config/mosquitto_sub", env); }else{ snprintf(loc, len, "%s/.config/mosquitto_rr", env); } loc[len-1] = '\0'; } } #else rc = GetEnvironmentVariable("USERPROFILE", env, 1024); if(rc > 0 && rc < 1024){ len = strlen(env) + strlen("\\mosquitto_pub.conf") + 1; loc = malloc(len); if(!loc){ err_printf(cfg, "Error: Out of memory.\n"); return 1; } if(pub_or_sub == CLIENT_PUB){ snprintf(loc, len, "%s\\mosquitto_pub.conf", env); }else if(pub_or_sub == CLIENT_SUB){ snprintf(loc, len, "%s\\mosquitto_sub.conf", env); }else{ snprintf(loc, len, "%s\\mosquitto_rr.conf", env); } loc[len-1] = '\0'; } #endif if(loc){ fptr = fopen(loc, "rt"); if(fptr){ while(fgets(line, 1024, fptr)){ if(line[0] == '#') continue; /* Comments */ while(line[strlen(line)-1] == 10 || line[strlen(line)-1] == 13){ line[strlen(line)-1] = 0; } /* All offset by one "args" here, because real argc/argv has * program name as the first entry. */ args[1] = strtok(line, " "); if(args[1]){ args[2] = strtok(NULL, ""); if(args[2]){ count = 3; }else{ count = 2; } rc = client_config_line_proc(cfg, pub_or_sub, count, args); if(rc){ fclose(fptr); free(loc); return rc; } } } fclose(fptr); } free(loc); } /* Deal with real argc/argv */ rc = client_config_line_proc(cfg, pub_or_sub, argc, argv); if(rc) return rc; if(cfg->will_payload && !cfg->will_topic){ fprintf(stderr, "Error: Will payload given, but no will topic given.\n"); return 1; } if(cfg->will_retain && !cfg->will_topic){ fprintf(stderr, "Error: Will retain given, but no will topic given.\n"); return 1; } #ifdef WITH_TLS if((cfg->certfile && !cfg->keyfile) || (cfg->keyfile && !cfg->certfile)){ fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is set.\n"); return 1; } if((cfg->keyform && !cfg->keyfile)){ fprintf(stderr, "Error: If keyform is set, keyfile must be also specified.\n"); return 1; } if((cfg->tls_engine_kpass_sha1 && (!cfg->keyform || !cfg->tls_engine))){ fprintf(stderr, "Error: when using tls-engine-kpass-sha1, both tls-engine and keyform must also be provided.\n"); return 1; } #endif #ifdef FINAL_WITH_TLS_PSK if((cfg->cafile || cfg->capath) && cfg->psk){ fprintf(stderr, "Error: Only one of --psk or --cafile/--capath may be used at once.\n"); return 1; } if(cfg->psk && !cfg->psk_identity){ fprintf(stderr, "Error: --psk-identity required if --psk used.\n"); return 1; } #endif if(cfg->protocol_version == 5){ if(cfg->clean_session == false && cfg->session_expiry_interval == -1){ /* User hasn't set session-expiry-interval, but has cleared clean * session so default to persistent session. */ cfg->session_expiry_interval = UINT32_MAX; } if(cfg->session_expiry_interval > 0){ if(cfg->session_expiry_interval == UINT32_MAX && (cfg->id_prefix || !cfg->id)){ fprintf(stderr, "Error: You must provide a client id if you are using an infinite session expiry interval.\n"); return 1; } rc = mosquitto_property_add_int32(&cfg->connect_props, MQTT_PROP_SESSION_EXPIRY_INTERVAL, (uint32_t )cfg->session_expiry_interval); if(rc){ fprintf(stderr, "Error adding property session-expiry-interval\n"); } } }else{ if(cfg->clean_session == false && (cfg->id_prefix || !cfg->id)){ fprintf(stderr, "Error: You must provide a client id if you are using the -c option.\n"); return 1; } } if(pub_or_sub == CLIENT_SUB){ if(cfg->topic_count == 0){ fprintf(stderr, "Error: You must specify a topic to subscribe to.\n"); return 1; } } if(!cfg->host){ cfg->host = strdup("localhost"); if(!cfg->host){ err_printf(cfg, "Error: Out of memory.\n"); return 1; } } rc = mosquitto_property_check_all(CMD_CONNECT, cfg->connect_props); if(rc){ err_printf(cfg, "Error in CONNECT properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_PUBLISH, cfg->publish_props); if(rc){ err_printf(cfg, "Error in PUBLISH properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_SUBSCRIBE, cfg->subscribe_props); if(rc){ err_printf(cfg, "Error in SUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, cfg->unsubscribe_props); if(rc){ err_printf(cfg, "Error in UNSUBSCRIBE properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_DISCONNECT, cfg->disconnect_props); if(rc){ err_printf(cfg, "Error in DISCONNECT properties: %s\n", mosquitto_strerror(rc)); return 1; } rc = mosquitto_property_check_all(CMD_WILL, cfg->will_props); if(rc){ err_printf(cfg, "Error in Will properties: %s\n", mosquitto_strerror(rc)); return 1; } return MOSQ_ERR_SUCCESS; } static int cfg_add_topic(struct mosq_config *cfg, int type, char *topic, const char *arg) { if(mosquitto_validate_utf8(topic, (int )strlen(topic))){ fprintf(stderr, "Error: Malformed UTF-8 in %s argument.\n\n", arg); return 1; } if(type == CLIENT_PUB || type == CLIENT_RR){ if(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){ fprintf(stderr, "Error: Invalid publish topic '%s', does it contain '+' or '#'?\n", topic); return 1; } cfg->topic = strdup(topic); }else if(type == CLIENT_RESPONSE_TOPIC){ if(mosquitto_pub_topic_check(topic) == MOSQ_ERR_INVAL){ fprintf(stderr, "Error: Invalid response topic '%s', does it contain '+' or '#'?\n", topic); return 1; } cfg->response_topic = strdup(topic); }else{ if(mosquitto_sub_topic_check(topic) == MOSQ_ERR_INVAL){ fprintf(stderr, "Error: Invalid subscription topic '%s', are all '+' and '#' wildcards correct?\n", topic); return 1; } cfg->topic_count++; cfg->topics = realloc(cfg->topics, (size_t )cfg->topic_count*sizeof(char *)); if(!cfg->topics){ err_printf(cfg, "Error: Out of memory.\n"); return 1; } cfg->topics[cfg->topic_count-1] = strdup(topic); } return 0; } /* Process a tokenised single line from a file or set of real argc/argv */ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, char *argv[]) { int i; int tmpi; float f; size_t szt; for(i=1; ibind_address = strdup(argv[i+1]); } i++; #ifdef WITH_TLS }else if(!strcmp(argv[i], "--cafile")){ if(i==argc-1){ fprintf(stderr, "Error: --cafile argument given but no file specified.\n\n"); return 1; }else{ cfg->cafile = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--capath")){ if(i==argc-1){ fprintf(stderr, "Error: --capath argument given but no directory specified.\n\n"); return 1; }else{ cfg->capath = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--cert")){ if(i==argc-1){ fprintf(stderr, "Error: --cert argument given but no file specified.\n\n"); return 1; }else{ cfg->certfile = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--ciphers")){ if(i==argc-1){ fprintf(stderr, "Error: --ciphers argument given but no ciphers specified.\n\n"); return 1; }else{ cfg->ciphers = strdup(argv[i+1]); } i++; #endif }else if(!strcmp(argv[i], "-C")){ if(pub_or_sub != CLIENT_SUB){ goto unknown_option; }else{ if(i==argc-1){ fprintf(stderr, "Error: -C argument given but no count specified.\n\n"); return 1; }else{ cfg->msg_count = atoi(argv[i+1]); if(cfg->msg_count < 1){ fprintf(stderr, "Error: Invalid message count \"%d\".\n\n", cfg->msg_count); return 1; } } i++; } }else if(!strcmp(argv[i], "-c") || !strcmp(argv[i], "--disable-clean-session")){ cfg->clean_session = false; }else if(!strcmp(argv[i], "-d") || !strcmp(argv[i], "--debug")){ cfg->debug = true; }else if(!strcmp(argv[i], "-D") || !strcmp(argv[i], "--property")){ i++; if(cfg_parse_property(cfg, argc, argv, &i)){ return 1; } cfg->protocol_version = MQTT_PROTOCOL_V5; }else if(!strcmp(argv[i], "-e")){ if(pub_or_sub != CLIENT_RR){ goto unknown_option; } if(i==argc-1){ fprintf(stderr, "Error: -e argument given but no response topic specified.\n\n"); return 1; }else{ if(cfg_add_topic(cfg, CLIENT_RESPONSE_TOPIC, argv[i+1], "-e")){ return 1; } } i++; }else if(!strcmp(argv[i], "-E")){ if(pub_or_sub != CLIENT_SUB){ goto unknown_option; } cfg->exit_after_sub = true; }else if(!strcmp(argv[i], "-f") || !strcmp(argv[i], "--file")){ if(pub_or_sub == CLIENT_SUB){ goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE){ fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; }else if(i==argc-1){ fprintf(stderr, "Error: -f argument given but no file specified.\n\n"); return 1; }else{ cfg->pub_mode = MSGMODE_FILE; cfg->file_input = strdup(argv[i+1]); if(!cfg->file_input){ err_printf(cfg, "Error: Out of memory.\n"); return 1; } } i++; }else if(!strcmp(argv[i], "-F")){ if(pub_or_sub == CLIENT_PUB){ goto unknown_option; } if(i==argc-1){ fprintf(stderr, "Error: -F argument given but no format specified.\n\n"); return 1; }else{ cfg->format = strdup(argv[i+1]); if(!cfg->format){ fprintf(stderr, "Error: Out of memory.\n"); return 1; } if(check_format(cfg->format)){ return 1; } } i++; }else if(!strcmp(argv[i], "--help")){ return 2; }else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--host")){ if(i==argc-1){ fprintf(stderr, "Error: -h argument given but no host specified.\n\n"); return 1; }else{ cfg->host = strdup(argv[i+1]); } i++; #ifdef WITH_TLS }else if(!strcmp(argv[i], "--insecure")){ cfg->insecure = true; #endif }else if(!strcmp(argv[i], "-i") || !strcmp(argv[i], "--id")){ if(cfg->id_prefix){ fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n"); return 1; } if(i==argc-1){ fprintf(stderr, "Error: -i argument given but no id specified.\n\n"); return 1; }else{ cfg->id = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "-I") || !strcmp(argv[i], "--id-prefix")){ if(cfg->id){ fprintf(stderr, "Error: -i and -I argument cannot be used together.\n\n"); return 1; } if(i==argc-1){ fprintf(stderr, "Error: -I argument given but no id prefix specified.\n\n"); return 1; }else{ cfg->id_prefix = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "-k") || !strcmp(argv[i], "--keepalive")){ if(i==argc-1){ fprintf(stderr, "Error: -k argument given but no keepalive specified.\n\n"); return 1; }else{ cfg->keepalive = atoi(argv[i+1]); if(cfg->keepalive<5 || cfg->keepalive>UINT16_MAX){ fprintf(stderr, "Error: Invalid keepalive given, it must be between 5 and 65535 inclusive.\n\n"); return 1; } } i++; #ifdef WITH_TLS }else if(!strcmp(argv[i], "--key")){ if(i==argc-1){ fprintf(stderr, "Error: --key argument given but no file specified.\n\n"); return 1; }else{ cfg->keyfile = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--keyform")){ if(i==argc-1){ fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n"); return 1; }else{ cfg->keyform = strdup(argv[i+1]); } i++; #endif }else if(!strcmp(argv[i], "-L") || !strcmp(argv[i], "--url")){ if(i==argc-1){ fprintf(stderr, "Error: -L argument given but no URL specified.\n\n"); return 1; } else { char *url = argv[i+1]; char *topic; char *tmp; if(!strncasecmp(url, "mqtt://", 7)) { url += 7; cfg->port = 1883; } else if(!strncasecmp(url, "mqtts://", 8)) { #ifdef WITH_TLS url += 8; cfg->port = 8883; cfg->tls_use_os_certs = true; #else fprintf(stderr, "Error: TLS support not available.\n\n"); return 1; #endif } else { fprintf(stderr, "Error: unsupported URL scheme.\n\n"); return 1; } topic = strchr(url, '/'); if(!topic){ fprintf(stderr, "Error: Invalid URL for -L argument specified - topic missing.\n"); return 1; } *topic++ = 0; if(cfg_add_topic(cfg, pub_or_sub, topic, "-L topic")) return 1; tmp = strchr(url, '@'); if(tmp) { char *colon; *tmp++ = 0; colon = strchr(url, ':'); if(colon) { *colon = 0; cfg->password = strdup(colon + 1); } cfg->username = strdup(url); url = tmp; } cfg->host = url; tmp = strchr(url, ':'); if(tmp) { *tmp++ = 0; cfg->port = atoi(tmp); } /* Now we've removed the port, time to get the host on the heap */ cfg->host = strdup(cfg->host); } i++; }else if(!strcmp(argv[i], "-l") || !strcmp(argv[i], "--stdin-line")){ if(pub_or_sub != CLIENT_PUB){ goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE){ fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; }else{ cfg->pub_mode = MSGMODE_STDIN_LINE; } }else if(!strcmp(argv[i], "-m") || !strcmp(argv[i], "--message")){ if(pub_or_sub == CLIENT_SUB){ goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE){ fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; }else if(i==argc-1){ fprintf(stderr, "Error: -m argument given but no message specified.\n\n"); return 1; }else{ cfg->message = strdup(argv[i+1]); if(cfg->message == NULL){ fprintf(stderr, "Error: Out of memory.\n\n"); return 1; } szt = strlen(cfg->message); if(szt > MQTT_MAX_PAYLOAD){ fprintf(stderr, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD); return 1; } cfg->msglen = (int )szt; cfg->pub_mode = MSGMODE_CMD; } i++; }else if(!strcmp(argv[i], "-M")){ if(i==argc-1){ fprintf(stderr, "Error: -M argument given but max_inflight not specified.\n\n"); return 1; }else{ tmpi = atoi(argv[i+1]); if(tmpi < 1){ fprintf(stderr, "Error: Maximum inflight messages must be greater than 0.\n\n"); return 1; } cfg->max_inflight = (unsigned int )tmpi; } i++; }else if(!strcmp(argv[i], "--nodelay")){ cfg->tcp_nodelay = true; }else if(!strcmp(argv[i], "-n") || !strcmp(argv[i], "--null-message")){ if(pub_or_sub == CLIENT_SUB){ goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE){ fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; }else{ cfg->pub_mode = MSGMODE_NULL; } }else if(!strcmp(argv[i], "-N")){ if(pub_or_sub == CLIENT_PUB){ goto unknown_option; } cfg->eol = false; }else if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){ if(i==argc-1){ fprintf(stderr, "Error: -p argument given but no port specified.\n\n"); return 1; }else{ cfg->port = atoi(argv[i+1]); if(cfg->port<0 || cfg->port>65535){ fprintf(stderr, "Error: Invalid port given: %d\n", cfg->port); return 1; } } i++; }else if(!strcmp(argv[i], "--pretty")){ if(pub_or_sub == CLIENT_PUB){ goto unknown_option; } cfg->pretty = true; }else if(!strcmp(argv[i], "-P") || !strcmp(argv[i], "--pw")){ if(i==argc-1){ fprintf(stderr, "Error: -P argument given but no password specified.\n\n"); return 1; }else{ cfg->password = strdup(argv[i+1]); } i++; #ifdef WITH_SOCKS }else if(!strcmp(argv[i], "--proxy")){ if(i==argc-1){ fprintf(stderr, "Error: --proxy argument given but no proxy url specified.\n\n"); return 1; }else{ if(mosquitto__parse_socks_url(cfg, argv[i+1])){ return 1; } i++; } #endif #ifdef FINAL_WITH_TLS_PSK }else if(!strcmp(argv[i], "--psk")){ if(i==argc-1){ fprintf(stderr, "Error: --psk argument given but no key specified.\n\n"); return 1; }else{ cfg->psk = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--psk-identity")){ if(i==argc-1){ fprintf(stderr, "Error: --psk-identity argument given but no identity specified.\n\n"); return 1; }else{ cfg->psk_identity = strdup(argv[i+1]); } i++; #endif }else if(!strcmp(argv[i], "-q") || !strcmp(argv[i], "--qos")){ if(i==argc-1){ fprintf(stderr, "Error: -q argument given but no QoS specified.\n\n"); return 1; }else{ cfg->qos = atoi(argv[i+1]); if(cfg->qos<0 || cfg->qos>2){ fprintf(stderr, "Error: Invalid QoS given: %d\n", cfg->qos); return 1; } } i++; }else if(!strcmp(argv[i], "--quiet")){ cfg->quiet = true; }else if(!strcmp(argv[i], "-r") || !strcmp(argv[i], "--retain")){ if(pub_or_sub != CLIENT_PUB){ goto unknown_option; } cfg->retain = 1; }else if(!strcmp(argv[i], "-R")){ if(pub_or_sub == CLIENT_PUB){ goto unknown_option; } cfg->no_retain = true; cfg->sub_opts |= MQTT_SUB_OPT_SEND_RETAIN_NEVER; }else if(!strcmp(argv[i], "--random-filter")){ if(pub_or_sub != CLIENT_SUB){ goto unknown_option; } if(i==argc-1){ fprintf(stderr, "Error: --random-filter argument given but no chance specified.\n\n"); return 1; }else{ cfg->random_filter = (int)(10.0*atof(argv[i+1])); if(cfg->random_filter > 10000 || cfg->random_filter < 1){ fprintf(stderr, "Error: --random-filter chance must be between 0.1-100.0\n\n"); return 1; } } i++; }else if(!strcmp(argv[i], "--remove-retained")){ if(pub_or_sub != CLIENT_SUB){ goto unknown_option; } cfg->remove_retained = true; }else if(!strcmp(argv[i], "--repeat")){ if(pub_or_sub != CLIENT_PUB){ goto unknown_option; } if(i==argc-1){ fprintf(stderr, "Error: --repeat argument given but no count specified.\n\n"); return 1; }else{ cfg->repeat_count = atoi(argv[i+1]); if(cfg->repeat_count < 1){ fprintf(stderr, "Error: --repeat argument must be >0.\n\n"); return 1; } } i++; }else if(!strcmp(argv[i], "--repeat-delay")){ if(pub_or_sub != CLIENT_PUB){ goto unknown_option; } if(i==argc-1){ fprintf(stderr, "Error: --repeat-delay argument given but no time specified.\n\n"); return 1; }else{ f = (float )atof(argv[i+1]); if(f < 0.0f){ fprintf(stderr, "Error: --repeat-delay argument must be >=0.0.\n\n"); return 1; } f *= 1.0e6f; cfg->repeat_delay.tv_sec = (int)f/1000000; cfg->repeat_delay.tv_usec = (int)f%1000000; } i++; }else if(!strcmp(argv[i], "--retain-as-published")){ if(pub_or_sub == CLIENT_PUB){ goto unknown_option; } cfg->sub_opts |= MQTT_SUB_OPT_RETAIN_AS_PUBLISHED; }else if(!strcmp(argv[i], "--retained-only")){ if(pub_or_sub != CLIENT_SUB){ goto unknown_option; } cfg->retained_only = true; }else if(!strcmp(argv[i], "-s") || !strcmp(argv[i], "--stdin-file")){ if(pub_or_sub == CLIENT_SUB){ goto unknown_option; } if(cfg->pub_mode != MSGMODE_NONE){ fprintf(stderr, "Error: Only one type of message can be sent at once.\n\n"); return 1; }else{ cfg->pub_mode = MSGMODE_STDIN_FILE; } #ifdef WITH_SRV }else if(!strcmp(argv[i], "-S")){ cfg->use_srv = true; #endif }else if(!strcmp(argv[i], "-t") || !strcmp(argv[i], "--topic")){ if(i==argc-1){ fprintf(stderr, "Error: -t argument given but no topic specified.\n\n"); return 1; }else{ if(cfg_add_topic(cfg, pub_or_sub, argv[i + 1], "-t")) return 1; i++; } }else if(!strcmp(argv[i], "-T") || !strcmp(argv[i], "--filter-out")){ if(pub_or_sub != CLIENT_SUB){ goto unknown_option; } if(i==argc-1){ fprintf(stderr, "Error: -T argument given but no topic filter specified.\n\n"); return 1; }else{ if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){ fprintf(stderr, "Error: Malformed UTF-8 in -T argument.\n\n"); return 1; } if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){ fprintf(stderr, "Error: Invalid filter topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]); return 1; } cfg->filter_out_count++; cfg->filter_outs = realloc(cfg->filter_outs, (size_t )cfg->filter_out_count*sizeof(char *)); if(!cfg->filter_outs){ fprintf(stderr, "Error: Out of memory.\n"); return 1; } cfg->filter_outs[cfg->filter_out_count-1] = strdup(argv[i+1]); } i++; #ifdef WITH_TLS }else if(!strcmp(argv[i], "--tls-alpn")){ if(i==argc-1){ fprintf(stderr, "Error: --tls-alpn argument given but no protocol specified.\n\n"); return 1; }else{ cfg->tls_alpn = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--tls-engine")){ if(i==argc-1){ fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n"); return 1; }else{ cfg->tls_engine = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--tls-engine-kpass-sha1")){ if(i==argc-1){ fprintf(stderr, "Error: --tls-engine-kpass-sha1 argument given but no kpass sha1 specified.\n\n"); return 1; }else{ cfg->tls_engine_kpass_sha1 = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--tls-use-os-certs")){ cfg->tls_use_os_certs = true; }else if(!strcmp(argv[i], "--tls-version")){ if(i==argc-1){ fprintf(stderr, "Error: --tls-version argument given but no version specified.\n\n"); return 1; }else{ cfg->tls_version = strdup(argv[i+1]); } i++; #endif }else if(!strcmp(argv[i], "-U") || !strcmp(argv[i], "--unsubscribe")){ if(pub_or_sub != CLIENT_SUB){ goto unknown_option; } if(i==argc-1){ fprintf(stderr, "Error: -U argument given but no unsubscribe topic specified.\n\n"); return 1; }else{ if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){ fprintf(stderr, "Error: Malformed UTF-8 in -U argument.\n\n"); return 1; } if(mosquitto_sub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){ fprintf(stderr, "Error: Invalid unsubscribe topic '%s', are all '+' and '#' wildcards correct?\n", argv[i+1]); return 1; } cfg->unsub_topic_count++; cfg->unsub_topics = realloc(cfg->unsub_topics, (size_t )cfg->unsub_topic_count*sizeof(char *)); if(!cfg->unsub_topics){ fprintf(stderr, "Error: Out of memory.\n"); return 1; } cfg->unsub_topics[cfg->unsub_topic_count-1] = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "-u") || !strcmp(argv[i], "--username")){ if(i==argc-1){ fprintf(stderr, "Error: -u argument given but no username specified.\n\n"); return 1; }else{ cfg->username = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "--unix")){ if(i==argc-1){ fprintf(stderr, "Error: --unix argument given but no socket path specified.\n\n"); return 1; }else{ cfg->host = strdup(argv[i+1]); cfg->port = 0; } i++; }else if(!strcmp(argv[i], "-V") || !strcmp(argv[i], "--protocol-version")){ if(i==argc-1){ fprintf(stderr, "Error: --protocol-version argument given but no version specified.\n\n"); return 1; }else{ if(!strcmp(argv[i+1], "mqttv31") || !strcmp(argv[i+1], "31")){ cfg->protocol_version = MQTT_PROTOCOL_V31; }else if(!strcmp(argv[i+1], "mqttv311") || !strcmp(argv[i+1], "311")){ cfg->protocol_version = MQTT_PROTOCOL_V311; }else if(!strcmp(argv[i+1], "mqttv5") || !strcmp(argv[i+1], "5")){ cfg->protocol_version = MQTT_PROTOCOL_V5; }else{ fprintf(stderr, "Error: Invalid protocol version argument given.\n\n"); return 1; } i++; } }else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){ if(pub_or_sub == CLIENT_PUB){ goto unknown_option; } cfg->verbose = 1; }else if(!strcmp(argv[i], "--version")){ return 3; }else if(!strcmp(argv[i], "-W")){ if(pub_or_sub == CLIENT_PUB){ goto unknown_option; }else{ if(i==argc-1){ fprintf(stderr, "Error: -W argument given but no timeout specified.\n\n"); return 1; }else{ tmpi = atoi(argv[i+1]); if(tmpi < 1){ fprintf(stderr, "Error: Invalid timeout \"%d\".\n\n", tmpi); return 1; } cfg->timeout = (unsigned int )tmpi; } i++; } }else if(!strcmp(argv[i], "--will-payload")){ if(i==argc-1){ fprintf(stderr, "Error: --will-payload argument given but no will payload specified.\n\n"); return 1; }else{ cfg->will_payload = strdup(argv[i+1]); cfg->will_payloadlen = (int )strlen(cfg->will_payload); } i++; }else if(!strcmp(argv[i], "--will-qos")){ if(i==argc-1){ fprintf(stderr, "Error: --will-qos argument given but no will QoS specified.\n\n"); return 1; }else{ cfg->will_qos = atoi(argv[i+1]); if(cfg->will_qos < 0 || cfg->will_qos > 2){ fprintf(stderr, "Error: Invalid will QoS %d.\n\n", cfg->will_qos); return 1; } } i++; }else if(!strcmp(argv[i], "--will-retain")){ cfg->will_retain = true; }else if(!strcmp(argv[i], "--will-topic")){ if(i==argc-1){ fprintf(stderr, "Error: --will-topic argument given but no will topic specified.\n\n"); return 1; }else{ if(mosquitto_validate_utf8(argv[i+1], (int )strlen(argv[i+1]))){ fprintf(stderr, "Error: Malformed UTF-8 in --will-topic argument.\n\n"); return 1; } if(mosquitto_pub_topic_check(argv[i+1]) == MOSQ_ERR_INVAL){ fprintf(stderr, "Error: Invalid will topic '%s', does it contain '+' or '#'?\n", argv[i+1]); return 1; } cfg->will_topic = strdup(argv[i+1]); } i++; }else if(!strcmp(argv[i], "-x")){ if(i==argc-1){ fprintf(stderr, "Error: -x argument given but no session expiry interval specified.\n\n"); return 1; }else{ if(!strcmp(argv[i+1], "∞")){ cfg->session_expiry_interval = UINT32_MAX; }else{ char *endptr = NULL; cfg->session_expiry_interval = strtol(argv[i+1], &endptr, 0); if(endptr == argv[i+1] || endptr[0] != '\0'){ /* Entirety of argument wasn't a number */ fprintf(stderr, "Error: session-expiry-interval not a number.\n\n"); return 1; } if(cfg->session_expiry_interval > UINT32_MAX || cfg->session_expiry_interval < -1){ fprintf(stderr, "Error: session-expiry-interval out of range.\n\n"); return 1; } if(cfg->session_expiry_interval == -1){ /* Convenience value for infinity. */ cfg->session_expiry_interval = UINT32_MAX; } } } i++; }else{ goto unknown_option; } } return MOSQ_ERR_SUCCESS; unknown_option: fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]); return 1; } int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) { #if defined(WITH_TLS) || defined(WITH_SOCKS) int rc; #endif mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, cfg->protocol_version); if(cfg->will_topic && mosquitto_will_set_v5(mosq, cfg->will_topic, cfg->will_payloadlen, cfg->will_payload, cfg->will_qos, cfg->will_retain, cfg->will_props)){ err_printf(cfg, "Error: Problem setting will.\n"); mosquitto_lib_cleanup(); return 1; } cfg->will_props = NULL; if((cfg->username || cfg->password) && mosquitto_username_pw_set(mosq, cfg->username, cfg->password)){ err_printf(cfg, "Error: Problem setting username and/or password.\n"); mosquitto_lib_cleanup(); return 1; } #ifdef WITH_TLS if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){ err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->cafile || cfg->capath){ rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL); if(rc){ if(rc == MOSQ_ERR_INVAL){ err_printf(cfg, "Error: Problem setting TLS options: File not found.\n"); }else{ err_printf(cfg, "Error: Problem setting TLS options: %s.\n", mosquitto_strerror(rc)); } mosquitto_lib_cleanup(); return 1; } # ifdef FINAL_WITH_TLS_PSK }else if(cfg->psk){ if(mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){ err_printf(cfg, "Error: Problem setting TLS-PSK options.\n"); mosquitto_lib_cleanup(); return 1; } # endif }else if(cfg->port == 8883){ mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1); } if(cfg->tls_use_os_certs){ mosquitto_int_option(mosq, MOSQ_OPT_TLS_USE_OS_CERTS, 1); } if(cfg->insecure && mosquitto_tls_insecure_set(mosq, true)){ err_printf(cfg, "Error: Problem setting TLS insecure option.\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_engine && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE, cfg->tls_engine)){ err_printf(cfg, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){ err_printf(cfg, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n"); mosquitto_lib_cleanup(); return 1; } if(cfg->tls_alpn && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ALPN, cfg->tls_alpn)){ err_printf(cfg, "Error: Problem setting TLS ALPN protocol.\n"); mosquitto_lib_cleanup(); return 1; } if((cfg->tls_version || cfg->ciphers) && mosquitto_tls_opts_set(mosq, 1, cfg->tls_version, cfg->ciphers)){ err_printf(cfg, "Error: Problem setting TLS options, check the options are valid.\n"); mosquitto_lib_cleanup(); return 1; } #endif mosquitto_max_inflight_messages_set(mosq, cfg->max_inflight); #ifdef WITH_SOCKS if(cfg->socks5_host){ rc = mosquitto_socks5_set(mosq, cfg->socks5_host, cfg->socks5_port, cfg->socks5_username, cfg->socks5_password); if(rc){ mosquitto_lib_cleanup(); return rc; } } #endif if(cfg->tcp_nodelay){ mosquitto_int_option(mosq, MOSQ_OPT_TCP_NODELAY, 1); } if(cfg->msg_count > 0 && cfg->msg_count < 20){ /* 20 is the default "receive maximum" * If we don't set this, then we can receive > msg_count messages * before we quit.*/ mosquitto_int_option(mosq, MOSQ_OPT_RECEIVE_MAXIMUM, cfg->msg_count); } return MOSQ_ERR_SUCCESS; } int client_id_generate(struct mosq_config *cfg) { if(cfg->id_prefix){ cfg->id = malloc(strlen(cfg->id_prefix)+10); if(!cfg->id){ err_printf(cfg, "Error: Out of memory.\n"); mosquitto_lib_cleanup(); return 1; } snprintf(cfg->id, strlen(cfg->id_prefix)+10, "%s%d", cfg->id_prefix, getpid()); } return MOSQ_ERR_SUCCESS; } int client_connect(struct mosquitto *mosq, struct mosq_config *cfg) { #ifndef WIN32 char *err; #else char err[1024]; #endif int rc; int port; if(cfg->port == PORT_UNDEFINED){ #ifdef WITH_TLS if(cfg->cafile || cfg->capath # ifdef FINAL_WITH_TLS_PSK || cfg->psk # endif ){ port = 8883; }else #endif { port = 1883; } }else{ port = cfg->port; } #ifdef WITH_SRV if(cfg->use_srv){ rc = mosquitto_connect_srv(mosq, cfg->host, cfg->keepalive, cfg->bind_address); }else{ rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props); } #else rc = mosquitto_connect_bind_v5(mosq, cfg->host, port, cfg->keepalive, cfg->bind_address, cfg->connect_props); #endif if(rc>0){ if(rc == MOSQ_ERR_ERRNO){ #ifndef WIN32 err = strerror(errno); #else FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, errno, 0, (LPTSTR)&err, 1024, NULL); #endif err_printf(cfg, "Error: %s\n", err); }else{ err_printf(cfg, "Unable to connect (%s).\n", mosquitto_strerror(rc)); } mosquitto_lib_cleanup(); return rc; } return MOSQ_ERR_SUCCESS; } #ifdef WITH_SOCKS /* Convert %25 -> %, %3a, %3A -> :, %40 -> @ */ static int mosquitto__urldecode(char *str) { size_t i, j; size_t len; if(!str) return 0; if(!strchr(str, '%')) return 0; len = strlen(str); for(i=0; i= len){ return 1; } if(str[i+1] == '2' && str[i+2] == '5'){ str[i] = '%'; len -= 2; for(j=i+1; j start){ len = i-start; if(host){ /* Have already seen a @ , so this must be of form * socks5h://username[:password]@host:port */ port = malloc(len + 1); if(!port){ err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(port, &(str[start]), len); port[len] = '\0'; }else if(username_or_host){ /* Haven't seen a @ before, so must be of form * socks5h://host:port */ host = username_or_host; username_or_host = NULL; port = malloc(len + 1); if(!port){ err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(port, &(str[start]), len); port[len] = '\0'; }else{ host = malloc(len + 1); if(!host){ err_printf(cfg, "Error: Out of memory.\n"); goto cleanup; } memcpy(host, &(str[start]), len); host[len] = '\0'; } } if(!host){ err_printf(cfg, "Error: Invalid proxy.\n"); goto cleanup; } if(mosquitto__urldecode(username)){ goto cleanup; } if(mosquitto__urldecode(password)){ goto cleanup; } if(port){ port_int = atoi(port); if(port_int < 1 || port_int > 65535){ err_printf(cfg, "Error: Invalid proxy port %d\n", port_int); goto cleanup; } free(port); }else{ port_int = 1080; } cfg->socks5_username = username; cfg->socks5_password = password; cfg->socks5_host = host; cfg->socks5_port = port_int; return 0; cleanup: free(username_or_host); free(username); free(password); free(host); free(port); return 1; } #endif void err_printf(const struct mosq_config *cfg, const char *fmt, ...) { va_list va; if(cfg->quiet) return; va_start(va, fmt); vfprintf(stderr, fmt, va); va_end(va); } mosquitto-2.0.18/client/client_shared.h000066400000000000000000000067361450213760600201210ustar00rootroot00000000000000/* Copyright (c) 2014-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef CLIENT_CONFIG_H #define CLIENT_CONFIG_H #include #ifdef WIN32 # include #else # include #endif #ifndef __GNUC__ #define __attribute__(attrib) #endif /* pub_client.c modes */ #define MSGMODE_NONE 0 #define MSGMODE_CMD 1 #define MSGMODE_STDIN_LINE 2 #define MSGMODE_STDIN_FILE 3 #define MSGMODE_FILE 4 #define MSGMODE_NULL 5 #define CLIENT_PUB 1 #define CLIENT_SUB 2 #define CLIENT_RR 3 #define CLIENT_RESPONSE_TOPIC 4 #define PORT_UNDEFINED -1 #define PORT_UNIX 0 struct mosq_config { char *id; char *id_prefix; int protocol_version; int keepalive; char *host; int port; int qos; bool retain; int pub_mode; /* pub, rr */ char *file_input; /* pub, rr */ char *message; /* pub, rr */ int msglen; /* pub, rr */ char *topic; /* pub, rr */ char *bind_address; int repeat_count; /* pub */ struct timeval repeat_delay; /* pub */ #ifdef WITH_SRV bool use_srv; #endif bool debug; bool quiet; unsigned int max_inflight; char *username; char *password; char *will_topic; char *will_payload; int will_payloadlen; int will_qos; bool will_retain; #ifdef WITH_TLS char *cafile; char *capath; char *certfile; char *keyfile; char *ciphers; bool insecure; char *tls_alpn; char *tls_version; char *tls_engine; char *tls_engine_kpass_sha1; char *keyform; bool tls_use_os_certs; # ifdef FINAL_WITH_TLS_PSK char *psk; char *psk_identity; # endif #endif bool clean_session; char **topics; /* sub, rr */ int topic_count; /* sub, rr */ bool exit_after_sub; /* sub */ bool no_retain; /* sub */ bool retained_only; /* sub */ bool remove_retained; /* sub */ char **filter_outs; /* sub */ int filter_out_count; /* sub */ char **unsub_topics; /* sub */ int unsub_topic_count; /* sub */ bool verbose; /* sub */ bool eol; /* sub */ int msg_count; /* sub */ char *format; /* sub, rr */ bool pretty; /* sub, rr */ unsigned int timeout; /* sub */ int sub_opts; /* sub */ long session_expiry_interval; int random_filter; /* sub */ #ifdef WITH_SOCKS char *socks5_host; int socks5_port; char *socks5_username; char *socks5_password; #endif mosquitto_property *connect_props; mosquitto_property *publish_props; mosquitto_property *subscribe_props; mosquitto_property *unsubscribe_props; mosquitto_property *disconnect_props; mosquitto_property *will_props; bool have_topic_alias; /* pub */ char *response_topic; /* rr */ bool tcp_nodelay; }; int client_config_load(struct mosq_config *config, int pub_or_sub, int argc, char *argv[]); void client_config_cleanup(struct mosq_config *cfg); int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg); int client_id_generate(struct mosq_config *cfg); int client_connect(struct mosquitto *mosq, struct mosq_config *cfg); int cfg_parse_property(struct mosq_config *cfg, int argc, char *argv[], int *idx); void err_printf(const struct mosq_config *cfg, const char *fmt, ...) __attribute__((format(printf, 2, 3))); #endif mosquitto-2.0.18/client/pub_client.c000066400000000000000000000466261450213760600174360ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #ifndef WIN32 #include #include #else #include #include #define snprintf sprintf_s #endif #include #include #include "client_shared.h" #include "pub_shared.h" /* Global variables for use in callbacks. See sub_client.c for an example of * using a struct to hold variables for use in callbacks. */ static bool first_publish = true; static int last_mid = -1; static int last_mid_sent = -1; static char *line_buf = NULL; static int line_buf_len = 1024; static bool disconnect_sent = false; static int publish_count = 0; static bool ready_for_repeat = false; static volatile int status = STATUS_CONNECTING; static int connack_result = 0; #ifdef WIN32 static uint64_t next_publish_tv; static void set_repeat_time(void) { uint64_t ticks = GetTickCount64(); next_publish_tv = ticks + cfg.repeat_delay.tv_sec*1000 + cfg.repeat_delay.tv_usec/1000; } static int check_repeat_time(void) { uint64_t ticks = GetTickCount64(); if(ticks > next_publish_tv){ return 1; }else{ return 0; } } #else static struct timeval next_publish_tv; static void set_repeat_time(void) { gettimeofday(&next_publish_tv, NULL); next_publish_tv.tv_sec += cfg.repeat_delay.tv_sec; next_publish_tv.tv_usec += cfg.repeat_delay.tv_usec; next_publish_tv.tv_sec += next_publish_tv.tv_usec/1000000; next_publish_tv.tv_usec = next_publish_tv.tv_usec%1000000; } static int check_repeat_time(void) { struct timeval tv; gettimeofday(&tv, NULL); if(tv.tv_sec > next_publish_tv.tv_sec){ return 1; }else if(tv.tv_sec == next_publish_tv.tv_sec && tv.tv_usec > next_publish_tv.tv_usec){ return 1; } return 0; } #endif void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(obj); UNUSED(rc); UNUSED(properties); if(rc == 0){ status = STATUS_DISCONNECTED; } } int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain) { ready_for_repeat = false; if(cfg.protocol_version == MQTT_PROTOCOL_V5 && cfg.have_topic_alias && first_publish == false){ return mosquitto_publish_v5(mosq, mid, NULL, payloadlen, payload, qos, retain, cfg.publish_props); }else{ first_publish = false; return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props); } } void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties) { int rc = MOSQ_ERR_SUCCESS; UNUSED(obj); UNUSED(flags); UNUSED(properties); connack_result = result; if(!result){ first_publish = true; switch(cfg.pub_mode){ case MSGMODE_CMD: case MSGMODE_FILE: case MSGMODE_STDIN_FILE: rc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain); break; case MSGMODE_NULL: rc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain); break; case MSGMODE_STDIN_LINE: status = STATUS_CONNACK_RECVD; break; } if(rc){ switch(rc){ case MOSQ_ERR_INVAL: err_printf(&cfg, "Error: Invalid input. Does your topic contain '+' or '#'?\n"); break; case MOSQ_ERR_NOMEM: err_printf(&cfg, "Error: Out of memory when trying to publish message.\n"); break; case MOSQ_ERR_NO_CONN: err_printf(&cfg, "Error: Client not connected when trying to publish.\n"); break; case MOSQ_ERR_PROTOCOL: err_printf(&cfg, "Error: Protocol error when communicating with broker.\n"); break; case MOSQ_ERR_PAYLOAD_SIZE: err_printf(&cfg, "Error: Message payload is too large.\n"); break; case MOSQ_ERR_QOS_NOT_SUPPORTED: err_printf(&cfg, "Error: Message QoS not supported on broker, try a lower QoS.\n"); break; } mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } }else{ if(result){ if(cfg.protocol_version == MQTT_PROTOCOL_V5){ if(result == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){ err_printf(&cfg, "Connection error: %s. Try connecting to an MQTT v5 broker, or use MQTT v3.x mode.\n", mosquitto_reason_string(result)); }else{ err_printf(&cfg, "Connection error: %s\n", mosquitto_reason_string(result)); } }else{ err_printf(&cfg, "Connection error: %s\n", mosquitto_connack_string(result)); } /* let the loop know that this is an unrecoverable connection */ status = STATUS_NOHOPE; } } } void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { char *reason_string = NULL; UNUSED(obj); UNUSED(properties); last_mid_sent = mid; if(reason_code > 127){ err_printf(&cfg, "Warning: Publish %d failed: %s.\n", mid, mosquitto_reason_string(reason_code)); mosquitto_property_read_string(properties, MQTT_PROP_REASON_STRING, &reason_string, false); if(reason_string){ err_printf(&cfg, "%s\n", reason_string); free(reason_string); } } publish_count++; if(cfg.pub_mode == MSGMODE_STDIN_LINE){ if(mid == last_mid){ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); disconnect_sent = true; } }else if(publish_count < cfg.repeat_count){ ready_for_repeat = true; set_repeat_time(); }else if(disconnect_sent == false){ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); disconnect_sent = true; } } int pub_shared_init(void) { line_buf = malloc((size_t )line_buf_len); if(!line_buf){ err_printf(&cfg, "Error: Out of memory.\n"); return 1; } return 0; } static int pub_stdin_line_loop(struct mosquitto *mosq) { char *buf2; int buf_len_actual = 0; int pos; int rc = MOSQ_ERR_SUCCESS; int read_len; bool stdin_finished = false; mosquitto_loop_start(mosq); stdin_finished = false; do{ if(status == STATUS_CONNECTING){ #ifdef WIN32 Sleep(100); #else struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 100000000; nanosleep(&ts, NULL); #endif } if(status == STATUS_NOHOPE){ return MOSQ_ERR_CONN_REFUSED; } if(status == STATUS_CONNACK_RECVD){ pos = 0; read_len = line_buf_len; while(status == STATUS_CONNACK_RECVD && fgets(&line_buf[pos], read_len, stdin)){ buf_len_actual = (int )strlen(line_buf); if(line_buf[buf_len_actual-1] == '\n'){ line_buf[buf_len_actual-1] = '\0'; rc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual-1, line_buf, cfg.qos, cfg.retain); pos = 0; if(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){ return rc; } break; }else{ line_buf_len += 1024; pos += read_len-1; read_len = 1024; buf2 = realloc(line_buf, (size_t )line_buf_len); if(!buf2){ err_printf(&cfg, "Error: Out of memory.\n"); return MOSQ_ERR_NOMEM; } line_buf = buf2; } } if(pos != 0){ rc = my_publish(mosq, &mid_sent, cfg.topic, buf_len_actual, line_buf, cfg.qos, cfg.retain); if(rc){ if(cfg.qos>0) return rc; } } if(feof(stdin)){ if(mid_sent == -1){ /* Empty file */ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); disconnect_sent = true; status = STATUS_DISCONNECTING; }else{ last_mid = mid_sent; status = STATUS_WAITING; } stdin_finished = true; }else if(status == STATUS_DISCONNECTED){ /* Not end of stdin, so we've lost our connection and must * reconnect */ } } if(status == STATUS_WAITING){ if(last_mid_sent == last_mid && disconnect_sent == false){ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); disconnect_sent = true; } #ifdef WIN32 Sleep(100); #else struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 100000000; nanosleep(&ts, NULL); #endif } }while(stdin_finished == false); mosquitto_loop_stop(mosq, false); if(status == STATUS_DISCONNECTED){ return MOSQ_ERR_SUCCESS; }else{ return rc; } } static int pub_other_loop(struct mosquitto *mosq) { int rc; int loop_delay = 1000; if(cfg.repeat_count > 1 && (cfg.repeat_delay.tv_sec == 0 || cfg.repeat_delay.tv_usec != 0)){ loop_delay = (int )cfg.repeat_delay.tv_usec / 2000; } do{ rc = mosquitto_loop(mosq, loop_delay, 1); if(ready_for_repeat && check_repeat_time()){ rc = MOSQ_ERR_SUCCESS; switch(cfg.pub_mode){ case MSGMODE_CMD: case MSGMODE_FILE: case MSGMODE_STDIN_FILE: rc = my_publish(mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain); break; case MSGMODE_NULL: rc = my_publish(mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain); break; } if(rc != MOSQ_ERR_SUCCESS && rc != MOSQ_ERR_NO_CONN){ err_printf(&cfg, "Error sending repeat publish: %s", mosquitto_strerror(rc)); } } }while(rc == MOSQ_ERR_SUCCESS); if(status == STATUS_DISCONNECTED){ return MOSQ_ERR_SUCCESS; }else{ return rc; } } int pub_shared_loop(struct mosquitto *mosq) { if(cfg.pub_mode == MSGMODE_STDIN_LINE){ return pub_stdin_line_loop(mosq); }else{ return pub_other_loop(mosq); } } void pub_shared_cleanup(void) { free(line_buf); } static void print_version(void) { int major, minor, revision; mosquitto_lib_version(&major, &minor, &revision); printf("mosquitto_pub version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision); } static void print_usage(void) { int major, minor, revision; mosquitto_lib_version(&major, &minor, &revision); printf("mosquitto_pub is a simple mqtt client that will publish a message on a single topic and exit.\n"); printf("mosquitto_pub version %s running on libmosquitto %d.%d.%d.\n\n", VERSION, major, minor, revision); printf("Usage: mosquitto_pub {[-h host] [--unix path] [-p port] [-u username] [-P password] -t topic | -L URL}\n"); printf(" {-f file | -l | -n | -m message}\n"); printf(" [-c] [-k keepalive] [-q qos] [-r] [--repeat N] [--repeat-delay time] [-x session-expiry]\n"); #ifdef WITH_SRV printf(" [-A bind_address] [--nodelay] [-S]\n"); #else printf(" [-A bind_address] [--nodelay]\n"); #endif printf(" [-i id] [-I id_prefix]\n"); printf(" [-d] [--quiet]\n"); printf(" [-M max_inflight]\n"); printf(" [-u username [-P password]]\n"); printf(" [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n"); #ifdef WITH_TLS printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n"); printf(" [--ciphers ciphers] [--insecure]\n"); printf(" [--tls-alpn protocol]\n"); printf(" [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\n"); printf(" [--tls-use-os-certs]\n"); #ifdef FINAL_WITH_TLS_PSK printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); #endif #endif #ifdef WITH_SOCKS printf(" [--proxy socks-url]\n"); #endif printf(" [--property command identifier value]\n"); printf(" [-D command identifier value]\n"); printf(" mosquitto_pub --help\n\n"); printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n"); printf(" the client communicates over.\n"); printf(" -d : enable debug messages.\n"); printf(" -c : disable clean session/enable persistent client mode\n"); printf(" When this argument is used, the broker will be instructed not to clean existing sessions\n"); printf(" for the same client id when the client connects, and sessions will never expire when the\n"); printf(" client disconnects. MQTT v5 clients can change their session expiry interval with the -x\n"); printf(" argument.\n"); printf(" -D : Define MQTT v5 properties. See the documentation for more details.\n"); printf(" -f : send the contents of a file as the message.\n"); printf(" -h : mqtt host to connect to. Defaults to localhost.\n"); printf(" -i : id to use for this client. Defaults to mosquitto_pub_ appended with the process id.\n"); printf(" -I : define the client id as id_prefix appended with the process id. Useful for when the\n"); printf(" broker is using the clientid_prefixes option.\n"); printf(" -k : keep alive in seconds for this client. Defaults to 60.\n"); printf(" -L : specify user, password, hostname, port and topic as a URL in the form:\n"); printf(" mqtt(s)://[username[:password]@]host[:port]/topic\n"); printf(" -l : read messages from stdin, sending a separate message for each line.\n"); printf(" -m : message payload to send.\n"); printf(" -M : the maximum inflight messages for QoS 1/2..\n"); printf(" -n : send a null (zero length) message.\n"); printf(" -p : network port to connect to. Defaults to 1883 for plain MQTT and 8883 for MQTT over TLS.\n"); printf(" -P : provide a password\n"); printf(" -q : quality of service level to use for all messages. Defaults to 0.\n"); printf(" -r : message should be retained.\n"); printf(" -s : read message from stdin, sending the entire input as a message.\n"); #ifdef WITH_SRV printf(" -S : use SRV lookups to determine which host to connect to.\n"); #endif printf(" -t : mqtt topic to publish to.\n"); printf(" -u : provide a username\n"); printf(" -V : specify the version of the MQTT protocol to use when connecting.\n"); printf(" Can be mqttv5, mqttv311 or mqttv31. Defaults to mqttv311.\n"); printf(" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\n"); printf(" clients only. Set to 0-4294967294 to specify the session will expire in that many\n"); printf(" seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\n"); printf(" that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\n"); printf(" --help : display this message.\n"); printf(" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\n"); printf(" expense of more packets being sent.\n"); printf(" --quiet : don't print error messages.\n"); printf(" --repeat : if publish mode is -f, -m, or -s, then repeat the publish N times.\n"); printf(" --repeat-delay : if using --repeat, wait time seconds between publishes. Defaults to 0.\n"); printf(" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\n"); printf(" e.g. /tmp/mosquitto.sock\n"); printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n"); printf(" unexpected disconnection. If not given and will-topic is set, a zero\n"); printf(" length message will be sent.\n"); printf(" --will-qos : QoS level for the client Will.\n"); printf(" --will-retain : if given, make the client Will retained.\n"); printf(" --will-topic : the topic on which to publish the client Will.\n"); #ifdef WITH_TLS printf(" --cafile : path to a file containing trusted CA certificates to enable encrypted\n"); printf(" communication.\n"); printf(" --capath : path to a directory containing trusted CA certificates to enable encrypted\n"); printf(" communication.\n"); printf(" --cert : client certificate for authentication, if required by server.\n"); printf(" --key : client private key for authentication, if required by server.\n"); printf(" --keyform : keyfile type, can be either \"pem\" or \"engine\".\n"); printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n"); printf(" --tls-version : TLS protocol version, can be one of tlsv1.3 tlsv1.2 or tlsv1.1.\n"); printf(" Defaults to tlsv1.2 if available.\n"); printf(" --insecure : do not check that the server certificate hostname matches the remote\n"); printf(" hostname. Using this option means that you cannot be sure that the\n"); printf(" remote host is the server you wish to connect to and so is insecure.\n"); printf(" Do not use this option in a production environment.\n"); printf(" --tls-engine : If set, enables the use of a TLS engine device.\n"); printf(" --tls-engine-kpass-sha1 : SHA1 of the key password to be used with the selected SSL engine.\n"); printf(" --tls-use-os-certs : Load and trust OS provided CA certificates.\n"); # ifdef FINAL_WITH_TLS_PSK printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n"); printf(" --psk-identity : client identity string for TLS-PSK mode.\n"); # endif #endif #ifdef WITH_SOCKS printf(" --proxy : SOCKS5 proxy URL of the form:\n"); printf(" socks5h://[username[:password]@]hostname[:port]\n"); printf(" Only \"none\" and \"username\" authentication is supported.\n"); #endif printf("\nSee https://mosquitto.org/ for more information.\n\n"); } int main(int argc, char *argv[]) { struct mosquitto *mosq = NULL; int rc; mosquitto_lib_init(); if(pub_shared_init()) return 1; rc = client_config_load(&cfg, CLIENT_PUB, argc, argv); if(rc){ if(rc == 2){ /* --help */ print_usage(); }else if(rc == 3){ print_version(); }else{ fprintf(stderr, "\nUse 'mosquitto_pub --help' to see usage.\n"); } goto cleanup; } #ifndef WITH_THREADING if(cfg.pub_mode == MSGMODE_STDIN_LINE){ fprintf(stderr, "Error: '-l' mode not available, threading support has not been compiled in.\n"); goto cleanup; } #endif if(cfg.pub_mode == MSGMODE_STDIN_FILE){ if(load_stdin()){ err_printf(&cfg, "Error loading input from stdin.\n"); goto cleanup; } }else if(cfg.file_input){ if(load_file(cfg.file_input)){ err_printf(&cfg, "Error loading input file \"%s\".\n", cfg.file_input); goto cleanup; } } if(!cfg.topic || cfg.pub_mode == MSGMODE_NONE){ fprintf(stderr, "Error: Both topic and message must be supplied.\n"); print_usage(); goto cleanup; } if(client_id_generate(&cfg)){ goto cleanup; } mosq = mosquitto_new(cfg.id, cfg.clean_session, NULL); if(!mosq){ switch(errno){ case ENOMEM: err_printf(&cfg, "Error: Out of memory.\n"); break; case EINVAL: err_printf(&cfg, "Error: Invalid id.\n"); break; } goto cleanup; } if(cfg.debug){ mosquitto_log_callback_set(mosq, my_log_callback); } mosquitto_connect_v5_callback_set(mosq, my_connect_callback); mosquitto_disconnect_v5_callback_set(mosq, my_disconnect_callback); mosquitto_publish_v5_callback_set(mosq, my_publish_callback); if(client_opts_set(mosq, &cfg)){ goto cleanup; } rc = client_connect(mosq, &cfg); if(rc){ goto cleanup; } rc = pub_shared_loop(mosq); if(cfg.message && cfg.pub_mode == MSGMODE_FILE){ free(cfg.message); cfg.message = NULL; } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); client_config_cleanup(&cfg); pub_shared_cleanup(); if(rc){ err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc)); } if(connack_result){ return connack_result; }else{ return rc; } cleanup: mosquitto_lib_cleanup(); client_config_cleanup(&cfg); pub_shared_cleanup(); return 1; } mosquitto-2.0.18/client/pub_shared.c000066400000000000000000000057431450213760600174210ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #ifndef WIN32 #include #else #include #include #define snprintf sprintf_s #endif #include #include #include "client_shared.h" #include "pub_shared.h" /* Global variables for use in callbacks. See sub_client.c for an example of * using a struct to hold variables for use in callbacks. */ int mid_sent = -1; struct mosq_config cfg; void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str) { UNUSED(mosq); UNUSED(obj); UNUSED(level); printf("%s\n", str); } int load_stdin(void) { size_t pos = 0, rlen; char buf[1024]; char *aux_message = NULL; cfg.pub_mode = MSGMODE_STDIN_FILE; while(!feof(stdin)){ rlen = fread(buf, 1, 1024, stdin); aux_message = realloc(cfg.message, pos+rlen); if(!aux_message){ err_printf(&cfg, "Error: Out of memory.\n"); free(cfg.message); return 1; } else { cfg.message = aux_message; } memcpy(&(cfg.message[pos]), buf, rlen); pos += rlen; } if(pos > MQTT_MAX_PAYLOAD){ err_printf(&cfg, "Error: Message length must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD); free(cfg.message); return 1; } cfg.msglen = (int )pos; if(!cfg.msglen){ err_printf(&cfg, "Error: Zero length input.\n"); return 1; } return 0; } int load_file(const char *filename) { size_t pos, rlen; FILE *fptr = NULL; long flen; fptr = fopen(filename, "rb"); if(!fptr){ err_printf(&cfg, "Error: Unable to open file \"%s\".\n", filename); return 1; } cfg.pub_mode = MSGMODE_FILE; fseek(fptr, 0, SEEK_END); flen = ftell(fptr); if(flen > MQTT_MAX_PAYLOAD){ fclose(fptr); err_printf(&cfg, "Error: File must be less than %u bytes.\n\n", MQTT_MAX_PAYLOAD); free(cfg.message); return 1; }else if(flen == 0){ fclose(fptr); cfg.message = NULL; cfg.msglen = 0; return 0; }else if(flen < 0){ fclose(fptr); err_printf(&cfg, "Error: Unable to determine size of file \"%s\".\n", filename); return 1; } cfg.msglen = (int )flen; fseek(fptr, 0, SEEK_SET); cfg.message = malloc((size_t )cfg.msglen); if(!cfg.message){ fclose(fptr); err_printf(&cfg, "Error: Out of memory.\n"); return 1; } pos = 0; while(pos < (size_t)cfg.msglen){ rlen = fread(&(cfg.message[pos]), sizeof(char), (size_t )cfg.msglen-pos, fptr); pos += rlen; } fclose(fptr); return 0; } mosquitto-2.0.18/client/pub_shared.h000066400000000000000000000030631450213760600174170ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef PUB_SHARED_H #define PUB_SHARED_H #define STATUS_CONNECTING 0 #define STATUS_CONNACK_RECVD 1 #define STATUS_WAITING 2 #define STATUS_DISCONNECTING 3 #define STATUS_DISCONNECTED 4 #define STATUS_NOHOPE 5 extern int mid_sent; extern struct mosq_config cfg; void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties); void my_disconnect_callback(struct mosquitto *mosq, void *obj, int rc, const mosquitto_property *properties); void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties); void my_log_callback(struct mosquitto *mosq, void *obj, int level, const char *str); int load_stdin(void); int load_file(const char *filename); int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain); int pub_shared_init(void); int pub_shared_loop(struct mosquitto *mosq); void pub_shared_cleanup(void); #endif mosquitto-2.0.18/client/pub_test_properties000077500000000000000000000015421450213760600211610ustar00rootroot00000000000000LD_LIBRARY_PATH=../lib ./mosquitto_pub \ \ -t asdf -V mqttv5 -m '{"key":"value"}' \ \ -D connect authentication-data password \ -D connect authentication-method something \ -D connect maximum-packet-size 0191 \ -D connect receive-maximum 1000 \ -D connect request-problem-information 1 \ -D connect request-response-information 1 \ -D connect session-expiry-interval 39 \ -D connect topic-alias-maximum 123 \ -D connect user-property connect up \ \ -D publish content-type application/json \ -D publish correlation-data some-data \ -D publish message-expiry-interval 59 \ -D publish payload-format-indicator 1 \ -D publish response-topic /dev/null \ -D publish topic-alias 4 \ -D publish user-property publish up \ \ -D disconnect reason-string "reason" \ -D disconnect session-expiry-interval 40 \ -D disconnect user-property disconnect up mosquitto-2.0.18/client/rr_client.c000066400000000000000000000340621450213760600172620ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #ifndef WIN32 #include #include #else #include #include #define snprintf sprintf_s #endif #include #include #include "client_shared.h" #include "pub_shared.h" #include "sub_client_output.h" enum rr__state { rr_s_new, rr_s_connected, rr_s_subscribed, rr_s_ready_to_publish, rr_s_wait_for_response, rr_s_disconnect }; static enum rr__state client_state = rr_s_new; bool process_messages = true; int msg_count = 0; struct mosquitto *g_mosq = NULL; static bool timed_out = false; static int connack_result = 0; #ifndef WIN32 static void my_signal_handler(int signum) { if(signum == SIGALRM){ process_messages = false; mosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); timed_out = true; } } #endif int my_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, void *payload, int qos, bool retain) { if(cfg.protocol_version < MQTT_PROTOCOL_V5){ return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL); }else{ return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, cfg.publish_props); } } static void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(obj); UNUSED(properties); if(process_messages == false) return; if(message->retain && cfg.no_retain) return; print_message(&cfg, message, properties); switch(cfg.pub_mode){ case MSGMODE_CMD: case MSGMODE_FILE: case MSGMODE_STDIN_FILE: case MSGMODE_NULL: client_state = rr_s_disconnect; break; case MSGMODE_STDIN_LINE: client_state = rr_s_ready_to_publish; break; } } void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties) { UNUSED(obj); UNUSED(flags); UNUSED(properties); connack_result = result; if(!result){ client_state = rr_s_connected; mosquitto_subscribe_v5(mosq, NULL, cfg.response_topic, cfg.qos, 0, cfg.subscribe_props); }else{ client_state = rr_s_disconnect; if(result){ if(result == MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION){ err_printf(&cfg, "Connection error: %s. mosquitto_rr only supports connecting to an MQTT v5 broker\n", mosquitto_reason_string(result)); }else{ err_printf(&cfg, "Connection error: %s\n", mosquitto_reason_string(result)); } } mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } } static void my_subscribe_callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { UNUSED(obj); UNUSED(mid); UNUSED(qos_count); if(granted_qos[0] < 128){ client_state = rr_s_ready_to_publish; }else{ client_state = rr_s_disconnect; err_printf(&cfg, "%s\n", mosquitto_reason_string(granted_qos[0])); mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } } void my_publish_callback(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(obj); UNUSED(mid); UNUSED(reason_code); UNUSED(properties); client_state = rr_s_wait_for_response; } static void print_version(void) { int major, minor, revision; mosquitto_lib_version(&major, &minor, &revision); printf("mosquitto_rr version %s running on libmosquitto %d.%d.%d.\n", VERSION, major, minor, revision); } static void print_usage(void) { int major, minor, revision; mosquitto_lib_version(&major, &minor, &revision); printf("mosquitto_rr is an mqtt client that can be used to publish a request message and wait for a response.\n"); printf(" Defaults to MQTT v5, where the Request-Response feature will be used, but v3.1.1 can also be used\n"); printf(" with v3.1.1 brokers.\n"); printf("mosquitto_rr version %s running on libmosquitto %d.%d.%d.\n\n", VERSION, major, minor, revision); printf("Usage: mosquitto_rr {[-h host] [--unix path] [-p port] [-u username] [-P password] -t topic | -L URL} -e response-topic\n"); printf(" [-c] [-k keepalive] [-q qos] [-R] [-x session-expiry-interval\n"); printf(" [-F format]\n"); #ifndef WIN32 printf(" [-W timeout_secs]\n"); #endif #ifdef WITH_SRV printf(" [-A bind_address] [--nodelay] [-S]\n"); #else printf(" [-A bind_address] [--nodelay]\n"); #endif printf(" [-i id] [-I id_prefix]\n"); printf(" [-d] [-N] [--quiet] [-v]\n"); printf(" [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n"); #ifdef WITH_TLS printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n"); printf(" [--ciphers ciphers] [--insecure]\n"); printf(" [--tls-alpn protocol]\n"); printf(" [--tls-engine engine] [--keyform keyform] [--tls-engine-kpass-sha1]]\n"); printf(" [--tls-use-os-certs]\n"); #ifdef FINAL_WITH_TLS_PSK printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); #endif #endif #ifdef WITH_SOCKS printf(" [--proxy socks-url]\n"); #endif printf(" [-D command identifier value]\n"); printf(" mosquitto_rr --help\n\n"); printf(" -A : bind the outgoing socket to this host/ip address. Use to control which interface\n"); printf(" the client communicates over.\n"); printf(" -c : disable clean session/enable persistent client mode\n"); printf(" When this argument is used, the broker will be instructed not to clean existing sessions\n"); printf(" for the same client id when the client connects, and sessions will never expire when the\n"); printf(" client disconnects. MQTT v5 clients can change their session expiry interval with the -x\n"); printf(" argument.\n"); printf(" -d : enable debug messages.\n"); printf(" -D : Define MQTT v5 properties. See the documentation for more details.\n"); printf(" -e : Response topic. The client will subscribe to this topic to wait for a response.\n"); printf(" -F : output format.\n"); printf(" -h : mqtt host to connect to. Defaults to localhost.\n"); printf(" -i : id to use for this client. Defaults to mosquitto_rr_ appended with the process id.\n"); printf(" -k : keep alive in seconds for this client. Defaults to 60.\n"); printf(" -L : specify user, password, hostname, port and topic as a URL in the form:\n"); printf(" mqtt(s)://[username[:password]@]host[:port]/topic\n"); printf(" -N : do not add an end of line character when printing the payload.\n"); printf(" -p : network port to connect to. Defaults to 1883 for plain MQTT and 8883 for MQTT over TLS.\n"); printf(" -P : provide a password\n"); printf(" -q : quality of service level to use for communications. Defaults to 0.\n"); printf(" -R : do not print stale messages (those with retain set).\n"); #ifdef WITH_SRV printf(" -S : use SRV lookups to determine which host to connect to.\n"); #endif printf(" -t : topic where the request message will be sent.\n"); printf(" -u : provide a username\n"); printf(" -v : print received messages verbosely.\n"); printf(" -V : specify the version of the MQTT protocol to use when connecting.\n"); printf(" Defaults to 5.\n"); #ifndef WIN32 printf(" -W : Specifies a timeout in seconds how long to wait for a response.\n"); #endif printf(" -x : Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5\n"); printf(" clients only. Set to 0-4294967294 to specify the session will expire in that many\n"); printf(" seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session\n"); printf(" that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given.\n"); printf(" --help : display this message.\n"); printf(" --nodelay : disable Nagle's algorithm, to reduce socket sending latency at the possible\n"); printf(" expense of more packets being sent.\n"); printf(" --pretty : print formatted output rather than minimised output when using the\n"); printf(" JSON output format option.\n"); printf(" --quiet : don't print error messages.\n"); printf(" --unix : connect to a broker through a unix domain socket instead of a TCP socket,\n"); printf(" e.g. /tmp/mosquitto.sock\n"); printf(" --will-payload : payload for the client Will, which is sent by the broker in case of\n"); printf(" unexpected disconnection. If not given and will-topic is set, a zero\n"); printf(" length message will be sent.\n"); printf(" --will-qos : QoS level for the client Will.\n"); printf(" --will-retain : if given, make the client Will retained.\n"); printf(" --will-topic : the topic on which to publish the client Will.\n"); #ifdef WITH_TLS printf(" --cafile : path to a file containing trusted CA certificates to enable encrypted\n"); printf(" certificate based communication.\n"); printf(" --capath : path to a directory containing trusted CA certificates to enable encrypted\n"); printf(" communication.\n"); printf(" --cert : client certificate for authentication, if required by server.\n"); printf(" --key : client private key for authentication, if required by server.\n"); printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n"); printf(" --tls-use-os-certs : Load and trust OS provided CA certificates.\n"); printf(" --tls-version : TLS protocol version, can be one of tlsv1.3 tlsv1.2 or tlsv1.1.\n"); printf(" Defaults to tlsv1.2 if available.\n"); printf(" --insecure : do not check that the server certificate hostname matches the remote\n"); printf(" hostname. Using this option means that you cannot be sure that the\n"); printf(" remote host is the server you wish to connect to and so is insecure.\n"); printf(" Do not use this option in a production environment.\n"); #ifdef WITH_TLS_PSK printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n"); printf(" --psk-identity : client identity string for TLS-PSK mode.\n"); #endif #endif #ifdef WITH_SOCKS printf(" --proxy : SOCKS5 proxy URL of the form:\n"); printf(" socks5h://[username[:password]@]hostname[:port]\n"); printf(" Only \"none\" and \"username\" authentication is supported.\n"); #endif printf("\nSee https://mosquitto.org/ for more information.\n\n"); } int main(int argc, char *argv[]) { int rc; #ifndef WIN32 struct sigaction sigact; #endif mosquitto_lib_init(); output_init(); rc = client_config_load(&cfg, CLIENT_RR, argc, argv); if(rc){ if(rc == 2){ /* --help */ print_usage(); }else if(rc == 3){ /* --version */ print_version(); }else{ fprintf(stderr, "\nUse 'mosquitto_rr --help' to see usage.\n"); } goto cleanup; } if(!cfg.topic || cfg.pub_mode == MSGMODE_NONE || !cfg.response_topic){ fprintf(stderr, "Error: All of topic, message, and response topic must be supplied.\n"); fprintf(stderr, "\nUse 'mosquitto_rr --help' to see usage.\n"); goto cleanup; } rc = mosquitto_property_add_string(&cfg.publish_props, MQTT_PROP_RESPONSE_TOPIC, cfg.response_topic); if(rc){ fprintf(stderr, "Error adding property RESPONSE_TOPIC.\n"); goto cleanup; } rc = mosquitto_property_check_all(CMD_PUBLISH, cfg.publish_props); if(rc){ err_printf(&cfg, "Error in PUBLISH properties: Duplicate response topic.\n"); goto cleanup; } if(client_id_generate(&cfg)){ goto cleanup; } g_mosq = mosquitto_new(cfg.id, cfg.clean_session, &cfg); if(!g_mosq){ switch(errno){ case ENOMEM: err_printf(&cfg, "Error: Out of memory.\n"); break; case EINVAL: err_printf(&cfg, "Error: Invalid id and/or clean_session.\n"); break; } goto cleanup; } if(client_opts_set(g_mosq, &cfg)){ goto cleanup; } if(cfg.debug){ mosquitto_log_callback_set(g_mosq, my_log_callback); } mosquitto_connect_v5_callback_set(g_mosq, my_connect_callback); mosquitto_subscribe_callback_set(g_mosq, my_subscribe_callback); mosquitto_message_v5_callback_set(g_mosq, my_message_callback); rc = client_connect(g_mosq, &cfg); if(rc){ goto cleanup; } #ifndef WIN32 sigact.sa_handler = my_signal_handler; sigemptyset(&sigact.sa_mask); sigact.sa_flags = 0; if(sigaction(SIGALRM, &sigact, NULL) == -1){ perror("sigaction"); goto cleanup; } if(cfg.timeout){ alarm(cfg.timeout); } #endif do{ rc = mosquitto_loop(g_mosq, -1, 1); if(client_state == rr_s_ready_to_publish){ client_state = rr_s_wait_for_response; switch(cfg.pub_mode){ case MSGMODE_CMD: case MSGMODE_FILE: case MSGMODE_STDIN_FILE: rc = my_publish(g_mosq, &mid_sent, cfg.topic, cfg.msglen, cfg.message, cfg.qos, cfg.retain); break; case MSGMODE_NULL: rc = my_publish(g_mosq, &mid_sent, cfg.topic, 0, NULL, cfg.qos, cfg.retain); break; case MSGMODE_STDIN_LINE: /* FIXME */ break; } } }while(rc == MOSQ_ERR_SUCCESS && client_state != rr_s_disconnect); mosquitto_destroy(g_mosq); mosquitto_lib_cleanup(); if(cfg.msg_count>0 && rc == MOSQ_ERR_NO_CONN){ rc = 0; } client_config_cleanup(&cfg); if(timed_out){ err_printf(&cfg, "Timed out\n"); return MOSQ_ERR_TIMEOUT; }else if(rc){ err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc)); } if(connack_result){ return connack_result; }else{ return rc; } cleanup: mosquitto_lib_cleanup(); client_config_cleanup(&cfg); return 1; } mosquitto-2.0.18/client/sub_client.c000066400000000000000000000362601450213760600174320ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #ifndef WIN32 #include #include #else #include #include #define snprintf sprintf_s #endif #include #include #include "client_shared.h" #include "sub_client_output.h" struct mosq_config cfg; bool process_messages = true; int msg_count = 0; struct mosquitto *g_mosq = NULL; int last_mid = 0; static bool timed_out = false; static int connack_result = 0; bool connack_received = false; #ifndef WIN32 static void my_signal_handler(int signum) { if(signum == SIGALRM || signum == SIGTERM || signum == SIGINT){ if(connack_received){ process_messages = false; mosquitto_disconnect_v5(g_mosq, MQTT_RC_DISCONNECT_WITH_WILL_MSG, cfg.disconnect_props); }else{ exit(-1); } } if(signum == SIGALRM){ timed_out = true; } } #endif static void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message, const mosquitto_property *properties) { int i; bool res; UNUSED(obj); UNUSED(properties); if(process_messages == false) return; if(cfg.retained_only && !message->retain && process_messages){ process_messages = false; if(last_mid == 0){ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } return; } if(message->retain && cfg.no_retain) return; if(cfg.filter_outs){ for(i=0; itopic, &res); if(res) return; } } if(cfg.remove_retained && message->retain){ mosquitto_publish(mosq, &last_mid, message->topic, 0, NULL, 1, true); } print_message(&cfg, message, properties); if(ferror(stdout)){ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } if(cfg.msg_count>0){ msg_count++; if(cfg.msg_count == msg_count){ process_messages = false; if(last_mid == 0){ mosquitto_disconnect_v5(mosq, 0, cfg.disconnect_props); } } } } static void my_connect_callback(struct mosquitto *mosq, void *obj, int result, int flags, const mosquitto_property *properties) { int i; UNUSED(obj); UNUSED(flags); UNUSED(properties); connack_received = true; connack_result = result; if(!result){ mosquitto_subscribe_multiple(mosq, NULL, cfg.topic_count, cfg.topics, cfg.qos, cfg.sub_opts, cfg.subscribe_props); for(i=0; i0 && rc == MOSQ_ERR_NO_CONN){ rc = 0; } client_config_cleanup(&cfg); if(timed_out){ err_printf(&cfg, "Timed out\n"); return MOSQ_ERR_TIMEOUT; }else if(rc){ err_printf(&cfg, "Error: %s\n", mosquitto_strerror(rc)); } if(connack_result){ return connack_result; }else{ return rc; } cleanup: mosquitto_destroy(g_mosq); mosquitto_lib_cleanup(); client_config_cleanup(&cfg); return 1; } mosquitto-2.0.18/client/sub_client_output.c000066400000000000000000000446271450213760600210600ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WIN32 /* For rand_s on Windows */ # define _CRT_RAND_S # include # include #endif #include #include #include #include #include #include #ifndef WIN32 #include #else #include #include #define snprintf sprintf_s #endif #ifdef WITH_CJSON # include #endif #ifdef __APPLE__ # include #endif #include #include #include "client_shared.h" #include "sub_client_output.h" extern struct mosq_config cfg; static int get_time(struct tm **ti, long *ns) { #ifdef WIN32 SYSTEMTIME st; #elif defined(__APPLE__) struct timeval tv; #else struct timespec ts; #endif time_t s; #ifdef WIN32 s = time(NULL); GetLocalTime(&st); *ns = st.wMilliseconds*1000000L; #elif defined(__APPLE__) gettimeofday(&tv, NULL); s = tv.tv_sec; *ns = tv.tv_usec*1000; #else if(clock_gettime(CLOCK_REALTIME, &ts) != 0){ err_printf(&cfg, "Error obtaining system time.\n"); return 1; } s = ts.tv_sec; *ns = ts.tv_nsec; #endif *ti = localtime(&s); if(!(*ti)){ err_printf(&cfg, "Error obtaining system time.\n"); return 1; } return 0; } static void write_payload(const unsigned char *payload, int payloadlen, int hex, char align, char pad, int field_width, int precision) { int i; int padlen; UNUSED(precision); /* FIXME - use or remove */ if(field_width > 0){ if(payloadlen > field_width){ payloadlen = field_width; } if(hex > 0){ payloadlen /= 2; padlen = field_width - payloadlen*2; }else{ padlen = field_width - payloadlen; } }else{ padlen = field_width - payloadlen; } if(align != '-'){ for(i=0; i=0 && payload[i] < 32)){ printf("\\u%04x", payload[i]); }else{ fputc(payload[i], stdout); } } } #endif #ifdef WITH_CJSON static int json_print_properties(cJSON *root, const mosquitto_property *properties) { int identifier; uint8_t i8value = 0; uint16_t i16value = 0; uint32_t i32value = 0; char *strname = NULL, *strvalue = NULL; char *binvalue = NULL; cJSON *tmp, *prop_json, *user_json = NULL; const mosquitto_property *prop = NULL; prop_json = cJSON_CreateObject(); if(prop_json == NULL){ cJSON_Delete(prop_json); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "properties", prop_json); for(prop=properties; prop != NULL; prop = mosquitto_property_next(prop)){ tmp = NULL; identifier = mosquitto_property_identifier(prop); switch(identifier){ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: mosquitto_property_read_byte(prop, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, &i8value, false); tmp = cJSON_CreateNumber(i8value); break; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: mosquitto_property_read_int32(prop, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i32value, false); tmp = cJSON_CreateNumber(i32value); break; case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: mosquitto_property_read_string(prop, identifier, &strvalue, false); if(strvalue == NULL) return MOSQ_ERR_NOMEM; tmp = cJSON_CreateString(strvalue); free(strvalue); strvalue = NULL; break; case MQTT_PROP_CORRELATION_DATA: mosquitto_property_read_binary(prop, MQTT_PROP_CORRELATION_DATA, (void **)&binvalue, &i16value, false); if(binvalue == NULL) return MOSQ_ERR_NOMEM; tmp = cJSON_CreateString(binvalue); free(binvalue); binvalue = NULL; break; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: mosquitto_property_read_varint(prop, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false); tmp = cJSON_CreateNumber(i32value); break; case MQTT_PROP_TOPIC_ALIAS: mosquitto_property_read_int16(prop, MQTT_PROP_TOPIC_ALIAS, &i16value, false); tmp = cJSON_CreateNumber(i16value); break; case MQTT_PROP_USER_PROPERTY: if(user_json == NULL){ user_json = cJSON_CreateObject(); if(user_json == NULL){ return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(prop_json, "user-properties", user_json); } mosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, false); if(strname == NULL || strvalue == NULL) return MOSQ_ERR_NOMEM; tmp = cJSON_CreateString(strvalue); free(strvalue); if(tmp == NULL){ free(strname); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(user_json, strname, tmp); free(strname); strname = NULL; strvalue = NULL; tmp = NULL; /* Don't add this to prop_json below */ break; } if(tmp != NULL){ cJSON_AddItemToObject(prop_json, mosquitto_property_identifier_to_string(identifier), tmp); } } return MOSQ_ERR_SUCCESS; } #endif static void format_time_8601(const struct tm *ti, int ns, char *buf, size_t len) { char c; strftime(buf, len, "%Y-%m-%dT%H:%M:%S.000000%z", ti); c = buf[strlen("2020-05-06T21:48:00.000000")]; snprintf(&buf[strlen("2020-05-06T21:48:00.")], 9, "%06d", ns/1000); buf[strlen("2020-05-06T21:48:00.000000")] = c; } static int json_print(const struct mosquitto_message *message, const mosquitto_property *properties, const struct tm *ti, int ns, bool escaped, bool pretty) { char buf[100]; #ifdef WITH_CJSON cJSON *root; cJSON *tmp; char *json_str; const char *return_parse_end; root = cJSON_CreateObject(); if(root == NULL){ return MOSQ_ERR_NOMEM; } format_time_8601(ti, ns, buf, sizeof(buf)); tmp = cJSON_CreateStringReference(buf); if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "tst", tmp); tmp = cJSON_CreateString(message->topic); if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "topic", tmp); tmp = cJSON_CreateNumber(message->qos); if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "qos", tmp); tmp = cJSON_CreateNumber(message->retain); if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "retain", tmp); tmp = cJSON_CreateNumber(message->payloadlen); if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "payloadlen", tmp); if(message->qos > 0){ tmp = cJSON_CreateNumber(message->mid); if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "mid", tmp); } /* Properties */ if(properties){ if(json_print_properties(root, properties)){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } } /* Payload */ if(escaped){ if(message->payload){ tmp = cJSON_CreateString(message->payload); }else{ tmp = cJSON_CreateNull(); } if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(root, "payload", tmp); }else{ return_parse_end = NULL; if(message->payload){ tmp = cJSON_ParseWithOpts(message->payload, &return_parse_end, true); if(tmp == NULL || return_parse_end != (char *)message->payload + message->payloadlen){ cJSON_Delete(root); return MOSQ_ERR_INVAL; } }else{ tmp = cJSON_CreateNull(); if(tmp == NULL){ cJSON_Delete(root); return MOSQ_ERR_INVAL; } } cJSON_AddItemToObject(root, "payload", tmp); } if(pretty){ json_str = cJSON_Print(root); }else{ json_str = cJSON_PrintUnformatted(root); } cJSON_Delete(root); if(json_str == NULL){ return MOSQ_ERR_NOMEM; } fputs(json_str, stdout); free(json_str); return MOSQ_ERR_SUCCESS; #else UNUSED(properties); UNUSED(pretty); format_time_8601(ti, ns, buf, sizeof(buf)); printf("{\"tst\":\"%s\",\"topic\":\"%s\",\"qos\":%d,\"retain\":%d,\"payloadlen\":%d,", buf, message->topic, message->qos, message->retain, message->payloadlen); if(message->qos > 0){ printf("\"mid\":%d,", message->mid); } if(escaped){ fputs("\"payload\":\"", stdout); write_json_payload(message->payload, message->payloadlen); fputs("\"}", stdout); }else{ fputs("\"payload\":", stdout); write_payload(message->payload, message->payloadlen, 0, 0, 0, 0, 0); fputs("}", stdout); } return MOSQ_ERR_SUCCESS; #endif } static void formatted_print_blank(char pad, int field_width) { int i; for(i=0; ipretty) != MOSQ_ERR_SUCCESS){ err_printf(lcfg, "Error: Out of memory.\n"); return; } break; case 'J': if(!ti){ if(get_time(&ti, &ns)){ err_printf(lcfg, "Error obtaining system time.\n"); return; } } rc = json_print(message, properties, ti, (int)ns, false, lcfg->pretty); if(rc == MOSQ_ERR_NOMEM){ err_printf(lcfg, "Error: Out of memory.\n"); return; }else if(rc == MOSQ_ERR_INVAL){ err_printf(lcfg, "Error: Message payload is not valid JSON on topic %s.\n", message->topic); return; } break; case 'l': formatted_print_int(message->payloadlen, align, pad, field_width); break; case 'm': formatted_print_int(message->mid, align, pad, field_width); break; case 'P': strname = NULL; strvalue = NULL; prop = mosquitto_property_read_string_pair(properties, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, false); while(prop){ printf("%s:%s", strname, strvalue); free(strname); free(strvalue); strname = NULL; strvalue = NULL; prop = mosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, true); if(prop){ fputc(' ', stdout); } } free(strname); free(strvalue); break; case 'p': write_payload(message->payload, message->payloadlen, 0, align, pad, field_width, precision); break; case 'q': fputc(message->qos + 48, stdout); break; case 'R': if(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &strvalue, false)){ formatted_print_str(strvalue, align, field_width, precision); free(strvalue); } break; case 'r': if(message->retain){ fputc('1', stdout); }else{ fputc('0', stdout); } break; case 'S': if(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false)){ formatted_print_int((int)i32value, align, pad, field_width); }else{ formatted_print_blank(pad, field_width); } break; case 't': formatted_print_str(message->topic, align, field_width, precision); break; case 'U': if(!ti){ if(get_time(&ti, &ns)){ err_printf(lcfg, "Error obtaining system time.\n"); return; } } if(strftime(buf, 100, "%s", ti) != 0){ printf("%s.%09ld", buf, ns); } break; case 'x': write_payload(message->payload, message->payloadlen, 1, align, pad, field_width, precision); break; case 'X': write_payload(message->payload, message->payloadlen, 2, align, pad, field_width, precision); break; } } static void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties) { size_t len; size_t i; struct tm *ti = NULL; long ns = 0; char strf[3] = {0, 0 ,0}; char buf[100]; char align, pad; int field_width, precision; len = strlen(lcfg->format); for(i=0; iformat[i] == '%'){ align = 0; pad = ' '; field_width = 0; precision = -1; if(i < len-1){ i++; /* Optional alignment */ if(lcfg->format[i] == '-'){ align = lcfg->format[i]; if(i < len-1){ i++; } } /* "%-040p" is allowed by this combination of checks, but isn't * a valid format specifier, the '0' will be ignored. */ /* Optional zero padding */ if(lcfg->format[i] == '0'){ pad = '0'; if(i < len-1){ i++; } } /* Optional field width */ while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){ field_width *= 10; field_width += lcfg->format[i]-'0'; i++; } /* Optional precision */ if(lcfg->format[i] == '.'){ if(i < len-1){ i++; precision = 0; while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){ precision *= 10; precision += lcfg->format[i]-'0'; i++; } } } if(i < len){ formatted_print_percent(lcfg, message, properties, lcfg->format[i], align, pad, field_width, precision); } } }else if(lcfg->format[i] == '@'){ if(i < len-1){ i++; if(lcfg->format[i] == '@'){ fputc('@', stdout); }else{ if(!ti){ if(get_time(&ti, &ns)){ err_printf(lcfg, "Error obtaining system time.\n"); return; } } strf[0] = '%'; strf[1] = lcfg->format[i]; strf[2] = 0; if(lcfg->format[i] == 'N'){ printf("%09ld", ns); }else{ if(strftime(buf, 100, strf, ti) != 0){ fputs(buf, stdout); } } } } }else if(lcfg->format[i] == '\\'){ if(i < len-1){ i++; switch(lcfg->format[i]){ case '\\': fputc('\\', stdout); break; case '0': fputc('\0', stdout); break; case 'a': fputc('\a', stdout); break; case 'e': fputc('\033', stdout); break; case 'n': fputc('\n', stdout); break; case 'r': fputc('\r', stdout); break; case 't': fputc('\t', stdout); break; case 'v': fputc('\v', stdout); break; } } }else{ fputc(lcfg->format[i], stdout); } } if(lcfg->eol){ fputc('\n', stdout); } fflush(stdout); } void output_init(void) { #ifndef WIN32 struct tm *ti = NULL; long ns; if(!get_time(&ti, &ns)){ srandom((unsigned int)ns); } #else /* Disable text translation so binary payloads aren't modified */ _setmode(_fileno(stdout), _O_BINARY); #endif } void print_message(struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties) { #ifdef WIN32 unsigned int r = 0; #else long r = 0; #endif if(lcfg->random_filter < 10000){ #ifdef WIN32 rand_s(&r); #else r = random(); #endif if((long)(r%10000) >= lcfg->random_filter){ return; } } if(lcfg->format){ formatted_print(lcfg, message, properties); }else if(lcfg->verbose){ if(message->payloadlen){ printf("%s ", message->topic); write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0); if(lcfg->eol){ printf("\n"); } }else{ if(lcfg->eol){ printf("%s (null)\n", message->topic); } } fflush(stdout); }else{ if(message->payloadlen){ write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0); if(lcfg->eol){ printf("\n"); } fflush(stdout); } } } mosquitto-2.0.18/client/sub_client_output.h000066400000000000000000000015141450213760600210510ustar00rootroot00000000000000/* Copyright (c) 2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef SUB_CLIENT_OUTPUT_H #define SUB_CLIENT_OUTPUT_H #include "mosquitto.h" #include "client_shared.h" void output_init(void); void print_message(struct mosq_config *cfg, const struct mosquitto_message *message, const mosquitto_property *properties); #endif mosquitto-2.0.18/client/sub_test_fixed_width000077500000000000000000000027561450213760600212760ustar00rootroot00000000000000LD_LIBRARY_PATH=../lib ./mosquitto_sub \ -h test.mosquitto.org \ --retained-only \ --remove-retained \ -t FW/# \ -W 1>/dev/null 2>/dev/null LD_LIBRARY_PATH=../lib ./mosquitto_pub \ -h test.mosquitto.org \ -D publish content-type "application/json" \ -D publish message-expiry-interval 360000 \ -D publish payload-format-indicator 1 \ -D publish response-topic response-topic \ -m ABCDEFGHIJKLMNOPQRSTUVWXYZ \ -q 2 \ -r \ -t FW/truncate \ -V 5 LD_LIBRARY_PATH=../lib ./mosquitto_pub \ -h test.mosquitto.org \ -D publish content-type "null" \ -D publish message-expiry-interval 3600 \ -D publish payload-format-indicator 1 \ -D publish response-topic r-t \ -m Off \ -q 2 \ -r \ -t FW/expire \ -V 5 LD_LIBRARY_PATH=../lib ./mosquitto_pub \ -h test.mosquitto.org \ -D publish payload-format-indicator 1 \ -D publish response-topic rt \ -m Offline \ -q 2 \ -r \ -t FW/1 \ -V 5 LD_LIBRARY_PATH=../lib ./mosquitto_sub \ -h test.mosquitto.org \ -C 3 \ -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 |" \ -q 2 \ -t 'FW/#' \ -V 5 echo LD_LIBRARY_PATH=../lib ./mosquitto_sub \ -h test.mosquitto.org \ -C 3 \ -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 |" \ -q 2 \ -t 'FW/#' \ -V 5 mosquitto-2.0.18/client/sub_test_properties000077500000000000000000000020211450213760600211550ustar00rootroot00000000000000LD_LIBRARY_PATH=../lib ./mosquitto_sub \ \ -V mqttv5 -C 10 -t \$SYS/# -v -U unsub --will-topic will --will-payload '{"key":"value"}' \ \ -D connect authentication-data password \ -D connect authentication-method something \ -D connect maximum-packet-size 0191 \ -D connect receive-maximum 1000 \ -D connect request-problem-information 1 \ -D connect request-response-information 1 \ -D connect session-expiry-interval 39 \ -D connect topic-alias-maximum 123 \ -D connect user-property connect up \ \ -D will content-type application/json \ -D will correlation-data some-data \ -D will message-expiry-interval 59 \ -D will payload-format-indicator 1 \ -D will response-topic /dev/null \ -D will user-property will up \ -D will will-delay-interval 100 \ \ -D subscribe subscription-identifier 1 \ -D subscribe user-property subscribe up \ \ -D unsubscribe user-property unsubscribe up \ \ -D disconnect reason-string "reason" \ -D disconnect session-expiry-interval 40 \ -D disconnect user-property disconnect up mosquitto-2.0.18/cmake/000077500000000000000000000000001450213760600147325ustar00rootroot00000000000000mosquitto-2.0.18/cmake/FindcJSON.cmake000066400000000000000000000013251450213760600174520ustar00rootroot00000000000000INCLUDE( FindPackageHandleStandardArgs ) # Checks an environment variable; note that the first check # does not require the usual CMake $-sign. IF( DEFINED ENV{CJSON_DIR} ) SET( CJSON_DIR "$ENV{CJSON_DIR}" ) ENDIF() FIND_PATH( CJSON_INCLUDE_DIR cjson/cJSON.h HINTS CJSON_DIR ) FIND_LIBRARY( CJSON_LIBRARY NAMES cjson HINTS ${CJSON_DIR} ) FIND_PACKAGE_HANDLE_STANDARD_ARGS( cJSON DEFAULT_MSG CJSON_INCLUDE_DIR CJSON_LIBRARY ) IF( CJSON_FOUND ) SET( CJSON_INCLUDE_DIRS ${CJSON_INCLUDE_DIR} ) SET( CJSON_LIBRARIES ${CJSON_LIBRARY} ) MARK_AS_ADVANCED( CJSON_LIBRARY CJSON_INCLUDE_DIR CJSON_DIR ) ELSE() SET( CJSON_DIR "" CACHE STRING "An optional hint to a directory for finding `cJSON`" ) ENDIF() mosquitto-2.0.18/config.h000066400000000000000000000041401450213760600152670ustar00rootroot00000000000000#ifndef CONFIG_H #define CONFIG_H /* ============================================================ * Platform options * ============================================================ */ #ifdef __APPLE__ # define __DARWIN_C_SOURCE #elif defined(__FreeBSD__) || defined(__NetBSD__) || defined(__SYMBIAN32__) # define _XOPEN_SOURCE 700 # define __BSD_VISIBLE 1 # define HAVE_NETINET_IN_H #elif defined(__QNX__) # define _XOPEN_SOURCE 600 # define __BSD_VISIBLE 1 # define HAVE_NETINET_IN_H #else # define _XOPEN_SOURCE 700 # define _DEFAULT_SOURCE 1 # define _POSIX_C_SOURCE 200809L #endif #ifndef _GNU_SOURCE # define _GNU_SOURCE #endif #define OPENSSL_LOAD_CONF /* ============================================================ * Compatibility defines * ============================================================ */ #if defined(_MSC_VER) && _MSC_VER < 1900 # define snprintf sprintf_s # define EPROTO ECONNABORTED # ifndef ECONNABORTED # define ECONNABORTED WSAECONNABORTED # endif # ifndef ENOTCONN # define ENOTCONN WSAENOTCONN # endif # ifndef ECONNREFUSED # define ECONNREFUSED WSAECONNREFUSED # endif #endif #ifdef WIN32 # ifndef strcasecmp # define strcasecmp strcmpi # endif # define strtok_r strtok_s # define strerror_r(e, b, l) strerror_s(b, l, e) #endif #define uthash_malloc(sz) mosquitto_malloc(sz) #define uthash_free(ptr,sz) mosquitto_free(ptr) #ifdef WITH_TLS # include # if defined(WITH_TLS_PSK) && !defined(OPENSSL_NO_PSK) # define FINAL_WITH_TLS_PSK # endif #endif #ifdef __COVERITY__ # include /* These are "wrong", but we don't use them so it doesn't matter */ # define _Float32 uint32_t # define _Float32x uint32_t # define _Float64 uint64_t # define _Float64x uint64_t # define _Float128 uint64_t #endif #define UNUSED(A) (void)(A) /* Android Bionic libpthread implementation doesn't have pthread_cancel */ #ifndef ANDROID # define HAVE_PTHREAD_CANCEL #endif #ifdef WITH_CJSON # include # define CJSON_VERSION_FULL (CJSON_VERSION_MAJOR*1000000+CJSON_VERSION_MINOR*1000+CJSON_VERSION_PATCH) #endif #endif mosquitto-2.0.18/config.mk000066400000000000000000000250531450213760600154550ustar00rootroot00000000000000# ============================================================================= # User configuration section. # # These options control compilation on all systems apart from Windows and Mac # OS X. Use CMake to compile on Windows and Mac. # # Largely, these are options that are designed to make mosquitto run more # easily in restrictive environments by removing features. # # Modify the variable below to enable/disable features. # # Can also be overriden at the command line, e.g.: # # make WITH_TLS=no # ============================================================================= # Uncomment to compile the broker with tcpd/libwrap support. #WITH_WRAP:=yes # Comment out to disable SSL/TLS support in the broker and client. # Disabling this will also mean that passwords must be stored in plain text. It # is strongly recommended that you only disable WITH_TLS if you are not using # password authentication at all. WITH_TLS:=yes # Comment out to disable TLS/PSK support in the broker and client. Requires # WITH_TLS=yes. # This must be disabled if using openssl < 1.0. WITH_TLS_PSK:=yes # Comment out to disable client threading support. WITH_THREADING:=yes # Comment out to remove bridge support from the broker. This allow the broker # to connect to other brokers and subscribe/publish to topics. You probably # want to leave this included unless you want to save a very small amount of # memory size and CPU time. WITH_BRIDGE:=yes # Comment out to remove persistent database support from the broker. This # allows the broker to store retained messages and durable subscriptions to a # file periodically and on shutdown. This is usually desirable (and is # suggested by the MQTT spec), but it can be disabled if required. WITH_PERSISTENCE:=yes # Comment out to remove memory tracking support from the broker. If disabled, # mosquitto won't track heap memory usage nor export '$SYS/broker/heap/current # size', but will use slightly less memory and CPU time. WITH_MEMORY_TRACKING:=yes # Compile with database upgrading support? If disabled, mosquitto won't # automatically upgrade old database versions. # Not currently supported. #WITH_DB_UPGRADE:=yes # Comment out to remove publishing of the $SYS topic hierarchy containing # information about the broker state. WITH_SYS_TREE:=yes # Build with systemd support. If enabled, mosquitto will notify systemd after # initialization. See README in service/systemd/ for more information. # Setting to yes means the libsystemd-dev or similar package will need to be # installed. WITH_SYSTEMD:=no # Build with SRV lookup support. WITH_SRV:=no # Build with websockets support on the broker. WITH_WEBSOCKETS:=no # Use elliptic keys in broker WITH_EC:=yes # Build man page documentation by default. WITH_DOCS:=yes # Build with client support for SOCK5 proxy. WITH_SOCKS:=yes # Strip executables and shared libraries on install. WITH_STRIP:=no # Build static libraries WITH_STATIC_LIBRARIES:=no # Use this variable to add extra library dependencies when building the clients # with the static libmosquitto library. This may be required on some systems # where e.g. -lz or -latomic are needed for openssl. CLIENT_STATIC_LDADD:= # Build shared libraries WITH_SHARED_LIBRARIES:=yes # Build with async dns lookup support for bridges (temporary). Requires glibc. #WITH_ADNS:=yes # Build with epoll support. WITH_EPOLL:=yes # Build with bundled uthash.h WITH_BUNDLED_DEPS:=yes # Build with coverage options WITH_COVERAGE:=no # Build with unix domain socket support WITH_UNIX_SOCKETS:=yes # Build mosquitto_sub with cJSON support WITH_CJSON:=yes # Build mosquitto with support for the $CONTROL topics. WITH_CONTROL:=yes # Build the broker with the jemalloc allocator WITH_JEMALLOC:=no # Build with xtreport capability. This is for debugging purposes and is # probably of no particular interest to end users. WITH_XTREPORT=no # Build using clang and with address sanitiser enabled WITH_ASAN=no # ============================================================================= # End of user configuration # ============================================================================= # Also bump lib/mosquitto.h, CMakeLists.txt, # installer/mosquitto.nsi, installer/mosquitto64.nsi VERSION=2.0.18 # Client library SO version. Bump if incompatible API/ABI changes are made. SOVERSION=1 # Man page generation requires xsltproc and docbook-xsl XSLTPROC=xsltproc --nonet # For html generation DB_HTML_XSL=man/html.xsl #MANCOUNTRIES=en_GB UNAME:=$(shell uname -s) ARCH:=$(shell uname -p) ifeq ($(UNAME),SunOS) ifeq ($(CC),cc) CFLAGS?=-O else CFLAGS?=-Wall -ggdb -O2 endif else CFLAGS?=-Wall -ggdb -O2 -Wconversion -Wextra endif ifeq ($(WITH_ASAN),yes) CC:=clang CFLAGS+=-fsanitize=address LDFLAGS+=-fsanitize=address endif STATIC_LIB_DEPS:= APP_CPPFLAGS=$(CPPFLAGS) -I. -I../../ -I../../include -I../../src -I../../lib APP_CFLAGS=$(CFLAGS) -DVERSION=\""${VERSION}\"" APP_LDFLAGS:=$(LDFLAGS) LIB_CPPFLAGS=$(CPPFLAGS) -I. -I.. -I../include -I../../include LIB_CFLAGS:=$(CFLAGS) LIB_CXXFLAGS:=$(CXXFLAGS) LIB_LDFLAGS:=$(LDFLAGS) LIB_LIBADD:=$(LIBADD) BROKER_CPPFLAGS:=$(LIB_CPPFLAGS) -I../lib BROKER_CFLAGS:=${CFLAGS} -DVERSION="\"${VERSION}\"" -DWITH_BROKER BROKER_LDFLAGS:=${LDFLAGS} BROKER_LDADD:= CLIENT_CPPFLAGS:=$(CPPFLAGS) -I.. -I../include CLIENT_CFLAGS:=${CFLAGS} -DVERSION="\"${VERSION}\"" CLIENT_LDFLAGS:=$(LDFLAGS) -L../lib CLIENT_LDADD:= PASSWD_LDADD:= PLUGIN_CPPFLAGS:=$(CPPFLAGS) -I../.. -I../../include PLUGIN_CFLAGS:=$(CFLAGS) -fPIC PLUGIN_LDFLAGS:=$(LDFLAGS) ifneq ($(or $(findstring $(UNAME),FreeBSD), $(findstring $(UNAME),OpenBSD), $(findstring $(UNAME),NetBSD)),) BROKER_LDADD:=$(BROKER_LDADD) -lm BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -Wl,--dynamic-list=linker.syms SEDINPLACE:=-i "" else BROKER_LDADD:=$(BROKER_LDADD) -ldl -lm SEDINPLACE:=-i endif ifeq ($(UNAME),Linux) BROKER_LDADD:=$(BROKER_LDADD) -lrt BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -Wl,--dynamic-list=linker.syms LIB_LIBADD:=$(LIB_LIBADD) -lrt endif ifeq ($(WITH_SHARED_LIBRARIES),yes) CLIENT_LDADD:=${CLIENT_LDADD} ../lib/libmosquitto.so.${SOVERSION} endif ifeq ($(UNAME),SunOS) SEDINPLACE:= ifeq ($(ARCH),sparc) ifeq ($(CC),cc) LIB_CFLAGS:=$(LIB_CFLAGS) -xc99 -KPIC else LIB_CFLAGS:=$(LIB_CFLAGS) -fPIC endif endif ifeq ($(ARCH),i386) LIB_CFLAGS:=$(LIB_CFLAGS) -fPIC endif ifeq ($(CXX),CC) LIB_CXXFLAGS:=$(LIB_CXXFLAGS) -KPIC else LIB_CXXFLAGS:=$(LIB_CXXFLAGS) -fPIC endif else LIB_CFLAGS:=$(LIB_CFLAGS) -fPIC LIB_CXXFLAGS:=$(LIB_CXXFLAGS) -fPIC endif ifneq ($(UNAME),SunOS) LIB_LDFLAGS:=$(LIB_LDFLAGS) -Wl,--version-script=linker.version -Wl,-soname,libmosquitto.so.$(SOVERSION) endif ifeq ($(UNAME),QNX) BROKER_LDADD:=$(BROKER_LDADD) -lsocket LIB_LIBADD:=$(LIB_LIBADD) -lsocket endif ifeq ($(WITH_WRAP),yes) BROKER_LDADD:=$(BROKER_LDADD) -lwrap BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_WRAP endif ifeq ($(WITH_TLS),yes) APP_CPPFLAGS:=$(APP_CPPFLAGS) -DWITH_TLS BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_TLS BROKER_LDADD:=$(BROKER_LDADD) -lssl -lcrypto CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_TLS LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_TLS LIB_LIBADD:=$(LIB_LIBADD) -lssl -lcrypto PASSWD_LDADD:=$(PASSWD_LDADD) -lcrypto STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -lssl -lcrypto ifeq ($(WITH_TLS_PSK),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_TLS_PSK LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_TLS_PSK CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_TLS_PSK endif endif ifeq ($(WITH_THREADING),yes) LIB_LDFLAGS:=$(LIB_LDFLAGS) -pthread LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_THREADING CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_THREADING STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -pthread endif ifeq ($(WITH_SOCKS),yes) LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_SOCKS CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_SOCKS endif ifeq ($(WITH_BRIDGE),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_BRIDGE endif ifeq ($(WITH_PERSISTENCE),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_PERSISTENCE endif ifeq ($(WITH_MEMORY_TRACKING),yes) ifneq ($(UNAME),SunOS) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_MEMORY_TRACKING endif endif ifeq ($(WITH_SYS_TREE),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_SYS_TREE endif ifeq ($(WITH_SYSTEMD),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_SYSTEMD BROKER_LDADD:=$(BROKER_LDADD) -lsystemd endif ifeq ($(WITH_SRV),yes) LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_SRV LIB_LIBADD:=$(LIB_LIBADD) -lcares CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_SRV STATIC_LIB_DEPS:=$(STATIC_LIB_DEPS) -lcares endif ifeq ($(UNAME),SunOS) BROKER_LDADD:=$(BROKER_LDADD) -lsocket -lnsl LIB_LIBADD:=$(LIB_LIBADD) -lsocket -lnsl endif ifeq ($(WITH_EC),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_EC endif ifeq ($(WITH_ADNS),yes) BROKER_LDADD:=$(BROKER_LDADD) -lanl BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_ADNS endif ifeq ($(WITH_CONTROL),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_CONTROL endif MAKE_ALL:=mosquitto ifeq ($(WITH_DOCS),yes) MAKE_ALL:=$(MAKE_ALL) docs endif ifeq ($(WITH_JEMALLOC),yes) BROKER_LDADD:=$(BROKER_LDADD) -ljemalloc endif ifeq ($(WITH_UNIX_SOCKETS),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_UNIX_SOCKETS LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -DWITH_UNIX_SOCKETS CLIENT_CPPFLAGS:=$(CLIENT_CPPFLAGS) -DWITH_UNIX_SOCKETS endif ifeq ($(WITH_WEBSOCKETS),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_WEBSOCKETS BROKER_LDADD:=$(BROKER_LDADD) -lwebsockets endif INSTALL?=install prefix?=/usr/local incdir?=${prefix}/include libdir?=${prefix}/lib${LIB_SUFFIX} localedir?=${prefix}/share/locale mandir?=${prefix}/share/man STRIP?=strip ifeq ($(WITH_STRIP),yes) STRIP_OPTS?=-s --strip-program=${CROSS_COMPILE}${STRIP} endif ifeq ($(WITH_EPOLL),yes) ifeq ($(UNAME),Linux) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -DWITH_EPOLL endif endif ifeq ($(WITH_BUNDLED_DEPS),yes) BROKER_CPPFLAGS:=$(BROKER_CPPFLAGS) -I../deps LIB_CPPFLAGS:=$(LIB_CPPFLAGS) -I../deps PLUGIN_CPPFLAGS:=$(PLUGIN_CPPFLAGS) -I../../deps endif ifeq ($(WITH_COVERAGE),yes) BROKER_CFLAGS:=$(BROKER_CFLAGS) -coverage BROKER_LDFLAGS:=$(BROKER_LDFLAGS) -coverage PLUGIN_CFLAGS:=$(PLUGIN_CFLAGS) -coverage PLUGIN_LDFLAGS:=$(PLUGIN_LDFLAGS) -coverage LIB_CFLAGS:=$(LIB_CFLAGS) -coverage LIB_LDFLAGS:=$(LIB_LDFLAGS) -coverage CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -coverage CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS) -coverage endif ifeq ($(WITH_CJSON),yes) CLIENT_CFLAGS:=$(CLIENT_CFLAGS) -DWITH_CJSON CLIENT_LDADD:=$(CLIENT_LDADD) -lcjson CLIENT_STATIC_LDADD:=$(CLIENT_STATIC_LDADD) -lcjson CLIENT_LDFLAGS:=$(CLIENT_LDFLAGS) endif ifeq ($(WITH_XTREPORT),yes) BROKER_CFLAGS:=$(BROKER_CFLAGS) -DWITH_XTREPORT endif BROKER_LDADD:=${BROKER_LDADD} ${LDADD} CLIENT_LDADD:=${CLIENT_LDADD} ${LDADD} PASSWD_LDADD:=${PASSWD_LDADD} ${LDADD} mosquitto-2.0.18/deps/000077500000000000000000000000001450213760600146055ustar00rootroot00000000000000mosquitto-2.0.18/deps/uthash.h000066400000000000000000002311651450213760600162620ustar00rootroot00000000000000/* Copyright (c) 2003-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTHASH_H #define UTHASH_H #define UTHASH_VERSION 2.1.0 #include /* memcmp, memset, strlen */ #include /* ptrdiff_t */ #include /* exit */ /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ when compiling c++ source) this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ #if !defined(DECLTYPE) && !defined(NO_DECLTYPE) #if defined(_MSC_VER) /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define DECLTYPE(x) (decltype(x)) #else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE #endif #elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) #define NO_DECLTYPE #else /* GNU, Sun and other compilers */ #define DECLTYPE(x) (__typeof(x)) #endif #endif #ifdef NO_DECLTYPE #define DECLTYPE(x) #define DECLTYPE_ASSIGN(dst,src) \ do { \ char **_da_dst = (char**)(&(dst)); \ *_da_dst = (char*)(src); \ } while (0) #else #define DECLTYPE_ASSIGN(dst,src) \ do { \ (dst) = DECLTYPE(dst)(src); \ } while (0) #endif /* a number of the hash function use uint32_t which isn't defined on Pre VS2010 */ #if defined(_WIN32) #if defined(_MSC_VER) && _MSC_VER >= 1600 #include #elif defined(__WATCOMC__) || defined(__MINGW32__) || defined(__CYGWIN__) #include #else typedef unsigned int uint32_t; typedef unsigned char uint8_t; #endif #elif defined(__GNUC__) && !defined(__VXWORKS__) #include #else typedef unsigned int uint32_t; typedef unsigned char uint8_t; #endif #ifndef uthash_malloc #define uthash_malloc(sz) malloc(sz) /* malloc fcn */ #endif #ifndef uthash_free #define uthash_free(ptr,sz) free(ptr) /* free fcn */ #endif #ifndef uthash_bzero #define uthash_bzero(a,n) memset(a,'\0',n) #endif #ifndef uthash_strlen #define uthash_strlen(s) strlen(s) #endif #ifdef uthash_memcmp /* This warning will not catch programs that define uthash_memcmp AFTER including uthash.h. */ #warning "uthash_memcmp is deprecated; please use HASH_KEYCMP instead" #else #define uthash_memcmp(a,b,n) memcmp(a,b,n) #endif #ifndef HASH_KEYCMP #define HASH_KEYCMP(a,b,n) uthash_memcmp(a,b,n) #endif #ifndef uthash_noexpand_fyi #define uthash_noexpand_fyi(tbl) /* can be defined to log noexpand */ #endif #ifndef uthash_expand_fyi #define uthash_expand_fyi(tbl) /* can be defined to log expands */ #endif #ifndef HASH_NONFATAL_OOM #define HASH_NONFATAL_OOM 0 #endif #if HASH_NONFATAL_OOM /* malloc failures can be recovered from */ #ifndef uthash_nonfatal_oom #define uthash_nonfatal_oom(obj) do {} while (0) /* non-fatal OOM error */ #endif #define HASH_RECORD_OOM(oomed) do { (oomed) = 1; } while (0) #define IF_HASH_NONFATAL_OOM(x) x #else /* malloc failures result in lost memory, hash tables are unusable */ #ifndef uthash_fatal #define uthash_fatal(msg) exit(-1) /* fatal OOM error */ #endif #define HASH_RECORD_OOM(oomed) uthash_fatal("out of memory") #define IF_HASH_NONFATAL_OOM(x) #endif /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS 32U /* initial number of buckets */ #define HASH_INITIAL_NUM_BUCKETS_LOG2 5U /* lg2 of initial number of buckets */ #define HASH_BKT_CAPACITY_THRESH 10U /* expand when bucket count reaches */ /* calculate the element whose hash handle address is hhp */ #define ELMT_FROM_HH(tbl,hhp) ((void*)(((char*)(hhp)) - ((tbl)->hho))) /* calculate the hash handle from element address elp */ #define HH_FROM_ELMT(tbl,elp) ((UT_hash_handle *)(((char*)(elp)) + ((tbl)->hho))) #define HASH_ROLLBACK_BKT(hh, head, itemptrhh) \ do { \ struct UT_hash_handle *_hd_hh_item = (itemptrhh); \ unsigned _hd_bkt; \ HASH_TO_BKT(_hd_hh_item->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ (head)->hh.tbl->buckets[_hd_bkt].count++; \ _hd_hh_item->hh_next = NULL; \ _hd_hh_item->hh_prev = NULL; \ } while (0) #define HASH_VALUE(keyptr,keylen,hashv) \ do { \ HASH_FCN(keyptr, keylen, hashv); \ } while (0) #define HASH_FIND_BYHASHVALUE(hh,head,keyptr,keylen,hashval,out) \ do { \ (out) = NULL; \ if (head) { \ unsigned _hf_bkt; \ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _hf_bkt); \ if (HASH_BLOOM_TEST((head)->hh.tbl, hashval) != 0) { \ HASH_FIND_IN_BKT((head)->hh.tbl, hh, (head)->hh.tbl->buckets[ _hf_bkt ], keyptr, keylen, hashval, out); \ } \ } \ } while (0) #define HASH_FIND(hh,head,keyptr,keylen,out) \ do { \ unsigned _hf_hashv; \ HASH_VALUE(keyptr, keylen, _hf_hashv); \ HASH_FIND_BYHASHVALUE(hh, head, keyptr, keylen, _hf_hashv, out); \ } while (0) #ifdef HASH_BLOOM #define HASH_BLOOM_BITLEN (1UL << HASH_BLOOM) #define HASH_BLOOM_BYTELEN (HASH_BLOOM_BITLEN/8UL) + (((HASH_BLOOM_BITLEN%8UL)!=0UL) ? 1UL : 0UL) #define HASH_BLOOM_MAKE(tbl,oomed) \ do { \ (tbl)->bloom_nbits = HASH_BLOOM; \ (tbl)->bloom_bv = (uint8_t*)uthash_malloc(HASH_BLOOM_BYTELEN); \ if (!(tbl)->bloom_bv) { \ HASH_RECORD_OOM(oomed); \ } else { \ uthash_bzero((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ (tbl)->bloom_sig = HASH_BLOOM_SIGNATURE; \ } \ } while (0) #define HASH_BLOOM_FREE(tbl) \ do { \ uthash_free((tbl)->bloom_bv, HASH_BLOOM_BYTELEN); \ } while (0) #define HASH_BLOOM_BITSET(bv,idx) (bv[(idx)/8U] |= (1U << ((idx)%8U))) #define HASH_BLOOM_BITTEST(bv,idx) (bv[(idx)/8U] & (1U << ((idx)%8U))) #define HASH_BLOOM_ADD(tbl,hashv) \ HASH_BLOOM_BITSET((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) #define HASH_BLOOM_TEST(tbl,hashv) \ HASH_BLOOM_BITTEST((tbl)->bloom_bv, ((hashv) & (uint32_t)((1UL << (tbl)->bloom_nbits) - 1U))) #else #define HASH_BLOOM_MAKE(tbl,oomed) #define HASH_BLOOM_FREE(tbl) #define HASH_BLOOM_ADD(tbl,hashv) #define HASH_BLOOM_TEST(tbl,hashv) (1) #define HASH_BLOOM_BYTELEN 0U #endif #define HASH_MAKE_TABLE(hh,head,oomed) \ do { \ (head)->hh.tbl = (UT_hash_table*)uthash_malloc(sizeof(UT_hash_table)); \ if (!(head)->hh.tbl) { \ HASH_RECORD_OOM(oomed); \ } else { \ uthash_bzero((head)->hh.tbl, sizeof(UT_hash_table)); \ (head)->hh.tbl->tail = &((head)->hh); \ (head)->hh.tbl->num_buckets = HASH_INITIAL_NUM_BUCKETS; \ (head)->hh.tbl->log2_num_buckets = HASH_INITIAL_NUM_BUCKETS_LOG2; \ (head)->hh.tbl->hho = (char*)(&(head)->hh) - (char*)(head); \ (head)->hh.tbl->buckets = (UT_hash_bucket*)uthash_malloc( \ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ (head)->hh.tbl->signature = HASH_SIGNATURE; \ if (!(head)->hh.tbl->buckets) { \ HASH_RECORD_OOM(oomed); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ } else { \ uthash_bzero((head)->hh.tbl->buckets, \ HASH_INITIAL_NUM_BUCKETS * sizeof(struct UT_hash_bucket)); \ HASH_BLOOM_MAKE((head)->hh.tbl, oomed); \ IF_HASH_NONFATAL_OOM( \ if (oomed) { \ uthash_free((head)->hh.tbl->buckets, \ HASH_INITIAL_NUM_BUCKETS*sizeof(struct UT_hash_bucket)); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ } \ ) \ } \ } \ } while (0) #define HASH_REPLACE_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,replaced,cmpfcn) \ do { \ (replaced) = NULL; \ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ if (replaced) { \ HASH_DELETE(hh, head, replaced); \ } \ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn); \ } while (0) #define HASH_REPLACE_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add,replaced) \ do { \ (replaced) = NULL; \ HASH_FIND_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, replaced); \ if (replaced) { \ HASH_DELETE(hh, head, replaced); \ } \ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add); \ } while (0) #define HASH_REPLACE(hh,head,fieldname,keylen_in,add,replaced) \ do { \ unsigned _hr_hashv; \ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ HASH_REPLACE_BYHASHVALUE(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced); \ } while (0) #define HASH_REPLACE_INORDER(hh,head,fieldname,keylen_in,add,replaced,cmpfcn) \ do { \ unsigned _hr_hashv; \ HASH_VALUE(&((add)->fieldname), keylen_in, _hr_hashv); \ HASH_REPLACE_BYHASHVALUE_INORDER(hh, head, fieldname, keylen_in, _hr_hashv, add, replaced, cmpfcn); \ } while (0) #define HASH_APPEND_LIST(hh, head, add) \ do { \ (add)->hh.next = NULL; \ (add)->hh.prev = ELMT_FROM_HH((head)->hh.tbl, (head)->hh.tbl->tail); \ (head)->hh.tbl->tail->next = (add); \ (head)->hh.tbl->tail = &((add)->hh); \ } while (0) #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ do { \ do { \ if (cmpfcn(DECLTYPE(head)(_hs_iter), add) > 0) { \ break; \ } \ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ } while (0) #ifdef NO_DECLTYPE #undef HASH_AKBI_INNER_LOOP #define HASH_AKBI_INNER_LOOP(hh,head,add,cmpfcn) \ do { \ char *_hs_saved_head = (char*)(head); \ do { \ DECLTYPE_ASSIGN(head, _hs_iter); \ if (cmpfcn(head, add) > 0) { \ DECLTYPE_ASSIGN(head, _hs_saved_head); \ break; \ } \ DECLTYPE_ASSIGN(head, _hs_saved_head); \ } while ((_hs_iter = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->next)); \ } while (0) #endif #if HASH_NONFATAL_OOM #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ do { \ if (!(oomed)) { \ unsigned _ha_bkt; \ (head)->hh.tbl->num_items++; \ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ if (oomed) { \ HASH_ROLLBACK_BKT(hh, head, &(add)->hh); \ HASH_DELETE_HH(hh, head, &(add)->hh); \ (add)->hh.tbl = NULL; \ uthash_nonfatal_oom(add); \ } else { \ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ } \ } else { \ (add)->hh.tbl = NULL; \ uthash_nonfatal_oom(add); \ } \ } while (0) #else #define HASH_ADD_TO_TABLE(hh,head,keyptr,keylen_in,hashval,add,oomed) \ do { \ unsigned _ha_bkt; \ (head)->hh.tbl->num_items++; \ HASH_TO_BKT(hashval, (head)->hh.tbl->num_buckets, _ha_bkt); \ HASH_ADD_TO_BKT((head)->hh.tbl->buckets[_ha_bkt], hh, &(add)->hh, oomed); \ HASH_BLOOM_ADD((head)->hh.tbl, hashval); \ HASH_EMIT_KEY(hh, head, keyptr, keylen_in); \ } while (0) #endif #define HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh,head,keyptr,keylen_in,hashval,add,cmpfcn) \ do { \ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ (add)->hh.hashv = (hashval); \ (add)->hh.key = (char*) (keyptr); \ (add)->hh.keylen = (unsigned) (keylen_in); \ if (!(head)) { \ (add)->hh.next = NULL; \ (add)->hh.prev = NULL; \ HASH_MAKE_TABLE(hh, add, _ha_oomed); \ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ (head) = (add); \ IF_HASH_NONFATAL_OOM( } ) \ } else { \ void *_hs_iter = (head); \ (add)->hh.tbl = (head)->hh.tbl; \ HASH_AKBI_INNER_LOOP(hh, head, add, cmpfcn); \ if (_hs_iter) { \ (add)->hh.next = _hs_iter; \ if (((add)->hh.prev = HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev)) { \ HH_FROM_ELMT((head)->hh.tbl, (add)->hh.prev)->next = (add); \ } else { \ (head) = (add); \ } \ HH_FROM_ELMT((head)->hh.tbl, _hs_iter)->prev = (add); \ } else { \ HASH_APPEND_LIST(hh, head, add); \ } \ } \ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE_INORDER"); \ } while (0) #define HASH_ADD_KEYPTR_INORDER(hh,head,keyptr,keylen_in,add,cmpfcn) \ do { \ unsigned _hs_hashv; \ HASH_VALUE(keyptr, keylen_in, _hs_hashv); \ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, keyptr, keylen_in, _hs_hashv, add, cmpfcn); \ } while (0) #define HASH_ADD_BYHASHVALUE_INORDER(hh,head,fieldname,keylen_in,hashval,add,cmpfcn) \ HASH_ADD_KEYPTR_BYHASHVALUE_INORDER(hh, head, &((add)->fieldname), keylen_in, hashval, add, cmpfcn) #define HASH_ADD_INORDER(hh,head,fieldname,keylen_in,add,cmpfcn) \ HASH_ADD_KEYPTR_INORDER(hh, head, &((add)->fieldname), keylen_in, add, cmpfcn) #define HASH_ADD_KEYPTR_BYHASHVALUE(hh,head,keyptr,keylen_in,hashval,add) \ do { \ IF_HASH_NONFATAL_OOM( int _ha_oomed = 0; ) \ (add)->hh.hashv = (hashval); \ (add)->hh.key = (char*) (keyptr); \ (add)->hh.keylen = (unsigned) (keylen_in); \ if (!(head)) { \ (add)->hh.next = NULL; \ (add)->hh.prev = NULL; \ HASH_MAKE_TABLE(hh, add, _ha_oomed); \ IF_HASH_NONFATAL_OOM( if (!_ha_oomed) { ) \ (head) = (add); \ IF_HASH_NONFATAL_OOM( } ) \ } else { \ (add)->hh.tbl = (head)->hh.tbl; \ HASH_APPEND_LIST(hh, head, add); \ } \ HASH_ADD_TO_TABLE(hh, head, keyptr, keylen_in, hashval, add, _ha_oomed); \ HASH_FSCK(hh, head, "HASH_ADD_KEYPTR_BYHASHVALUE"); \ } while (0) #define HASH_ADD_KEYPTR(hh,head,keyptr,keylen_in,add) \ do { \ unsigned _ha_hashv; \ HASH_VALUE(keyptr, keylen_in, _ha_hashv); \ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, keyptr, keylen_in, _ha_hashv, add); \ } while (0) #define HASH_ADD_BYHASHVALUE(hh,head,fieldname,keylen_in,hashval,add) \ HASH_ADD_KEYPTR_BYHASHVALUE(hh, head, &((add)->fieldname), keylen_in, hashval, add) #define HASH_ADD(hh,head,fieldname,keylen_in,add) \ HASH_ADD_KEYPTR(hh, head, &((add)->fieldname), keylen_in, add) #define HASH_TO_BKT(hashv,num_bkts,bkt) \ do { \ bkt = ((hashv) & ((num_bkts) - 1U)); \ } while (0) /* delete "delptr" from the hash table. * "the usual" patch-up process for the app-order doubly-linked-list. * The use of _hd_hh_del below deserves special explanation. * These used to be expressed using (delptr) but that led to a bug * if someone used the same symbol for the head and deletee, like * HASH_DELETE(hh,users,users); * We want that to work, but by changing the head (users) below * we were forfeiting our ability to further refer to the deletee (users) * in the patch-up process. Solution: use scratch space to * copy the deletee pointer, then the latter references are via that * scratch pointer rather than through the repointed (users) symbol. */ #define HASH_DELETE(hh,head,delptr) \ HASH_DELETE_HH(hh, head, &(delptr)->hh) #define HASH_DELETE_HH(hh,head,delptrhh) \ do { \ struct UT_hash_handle *_hd_hh_del = (delptrhh); \ if ((_hd_hh_del->prev == NULL) && (_hd_hh_del->next == NULL)) { \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets * sizeof(struct UT_hash_bucket)); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ (head) = NULL; \ } else { \ unsigned _hd_bkt; \ if (_hd_hh_del == (head)->hh.tbl->tail) { \ (head)->hh.tbl->tail = HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev); \ } \ if (_hd_hh_del->prev != NULL) { \ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->prev)->next = _hd_hh_del->next; \ } else { \ DECLTYPE_ASSIGN(head, _hd_hh_del->next); \ } \ if (_hd_hh_del->next != NULL) { \ HH_FROM_ELMT((head)->hh.tbl, _hd_hh_del->next)->prev = _hd_hh_del->prev; \ } \ HASH_TO_BKT(_hd_hh_del->hashv, (head)->hh.tbl->num_buckets, _hd_bkt); \ HASH_DEL_IN_BKT((head)->hh.tbl->buckets[_hd_bkt], _hd_hh_del); \ (head)->hh.tbl->num_items--; \ } \ HASH_FSCK(hh, head, "HASH_DELETE_HH"); \ } while (0) /* convenience forms of HASH_FIND/HASH_ADD/HASH_DEL */ #define HASH_FIND_STR(head,findstr,out) \ do { \ unsigned _uthash_hfstr_keylen = (unsigned)uthash_strlen(findstr); \ HASH_FIND(hh, head, findstr, _uthash_hfstr_keylen, out); \ } while (0) #define HASH_ADD_STR(head,strfield,add) \ do { \ unsigned _uthash_hastr_keylen = (unsigned)uthash_strlen((add)->strfield); \ HASH_ADD(hh, head, strfield[0], _uthash_hastr_keylen, add); \ } while (0) #define HASH_REPLACE_STR(head,strfield,add,replaced) \ do { \ unsigned _uthash_hrstr_keylen = (unsigned)uthash_strlen((add)->strfield); \ HASH_REPLACE(hh, head, strfield[0], _uthash_hrstr_keylen, add, replaced); \ } while (0) #define HASH_FIND_INT(head,findint,out) \ HASH_FIND(hh,head,findint,sizeof(int),out) #define HASH_ADD_INT(head,intfield,add) \ HASH_ADD(hh,head,intfield,sizeof(int),add) #define HASH_REPLACE_INT(head,intfield,add,replaced) \ HASH_REPLACE(hh,head,intfield,sizeof(int),add,replaced) #define HASH_FIND_PTR(head,findptr,out) \ HASH_FIND(hh,head,findptr,sizeof(void *),out) #define HASH_ADD_PTR(head,ptrfield,add) \ HASH_ADD(hh,head,ptrfield,sizeof(void *),add) #define HASH_REPLACE_PTR(head,ptrfield,add,replaced) \ HASH_REPLACE(hh,head,ptrfield,sizeof(void *),add,replaced) #define HASH_DEL(head,delptr) \ HASH_DELETE(hh,head,delptr) /* HASH_FSCK checks hash integrity on every add/delete when HASH_DEBUG is defined. * This is for uthash developer only; it compiles away if HASH_DEBUG isn't defined. */ #ifdef HASH_DEBUG #define HASH_OOPS(...) do { fprintf(stderr,__VA_ARGS__); exit(-1); } while (0) #define HASH_FSCK(hh,head,where) \ do { \ struct UT_hash_handle *_thh; \ if (head) { \ unsigned _bkt_i; \ unsigned _count = 0; \ char *_prev; \ for (_bkt_i = 0; _bkt_i < (head)->hh.tbl->num_buckets; ++_bkt_i) { \ unsigned _bkt_count = 0; \ _thh = (head)->hh.tbl->buckets[_bkt_i].hh_head; \ _prev = NULL; \ while (_thh) { \ if (_prev != (char*)(_thh->hh_prev)) { \ HASH_OOPS("%s: invalid hh_prev %p, actual %p\n", \ (where), (void*)_thh->hh_prev, (void*)_prev); \ } \ _bkt_count++; \ _prev = (char*)(_thh); \ _thh = _thh->hh_next; \ } \ _count += _bkt_count; \ if ((head)->hh.tbl->buckets[_bkt_i].count != _bkt_count) { \ HASH_OOPS("%s: invalid bucket count %u, actual %u\n", \ (where), (head)->hh.tbl->buckets[_bkt_i].count, _bkt_count); \ } \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("%s: invalid hh item count %u, actual %u\n", \ (where), (head)->hh.tbl->num_items, _count); \ } \ _count = 0; \ _prev = NULL; \ _thh = &(head)->hh; \ while (_thh) { \ _count++; \ if (_prev != (char*)_thh->prev) { \ HASH_OOPS("%s: invalid prev %p, actual %p\n", \ (where), (void*)_thh->prev, (void*)_prev); \ } \ _prev = (char*)ELMT_FROM_HH((head)->hh.tbl, _thh); \ _thh = (_thh->next ? HH_FROM_ELMT((head)->hh.tbl, _thh->next) : NULL); \ } \ if (_count != (head)->hh.tbl->num_items) { \ HASH_OOPS("%s: invalid app item count %u, actual %u\n", \ (where), (head)->hh.tbl->num_items, _count); \ } \ } \ } while (0) #else #define HASH_FSCK(hh,head,where) #endif /* When compiled with -DHASH_EMIT_KEYS, length-prefixed keys are emitted to * the descriptor to which this macro is defined for tuning the hash function. * The app can #include to get the prototype for write(2). */ #ifdef HASH_EMIT_KEYS #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) \ do { \ unsigned _klen = fieldlen; \ write(HASH_EMIT_KEYS, &_klen, sizeof(_klen)); \ write(HASH_EMIT_KEYS, keyptr, (unsigned long)fieldlen); \ } while (0) #else #define HASH_EMIT_KEY(hh,head,keyptr,fieldlen) #endif /* default to Jenkin's hash unless overridden e.g. DHASH_FUNCTION=HASH_SAX */ #ifdef HASH_FUNCTION #define HASH_FCN HASH_FUNCTION #else #define HASH_FCN HASH_JEN #endif /* The Bernstein hash function, used in Perl prior to v5.6. Note (x<<5+x)=x*33. */ #define HASH_BER(key,keylen,hashv) \ do { \ unsigned _hb_keylen = (unsigned)keylen; \ const unsigned char *_hb_key = (const unsigned char*)(key); \ (hashv) = 0; \ while (_hb_keylen-- != 0U) { \ (hashv) = (((hashv) << 5) + (hashv)) + *_hb_key++; \ } \ } while (0) /* SAX/FNV/OAT/JEN hash functions are macro variants of those listed at * http://eternallyconfuzzled.com/tuts/algorithms/jsw_tut_hashing.aspx */ #define HASH_SAX(key,keylen,hashv) \ do { \ unsigned _sx_i; \ const unsigned char *_hs_key = (const unsigned char*)(key); \ hashv = 0; \ for (_sx_i=0; _sx_i < keylen; _sx_i++) { \ hashv ^= (hashv << 5) + (hashv >> 2) + _hs_key[_sx_i]; \ } \ } while (0) /* FNV-1a variation */ #define HASH_FNV(key,keylen,hashv) \ do { \ unsigned _fn_i; \ const unsigned char *_hf_key = (const unsigned char*)(key); \ (hashv) = 2166136261U; \ for (_fn_i=0; _fn_i < keylen; _fn_i++) { \ hashv = hashv ^ _hf_key[_fn_i]; \ hashv = hashv * 16777619U; \ } \ } while (0) #define HASH_OAT(key,keylen,hashv) \ do { \ unsigned _ho_i; \ const unsigned char *_ho_key=(const unsigned char*)(key); \ hashv = 0; \ for(_ho_i=0; _ho_i < keylen; _ho_i++) { \ hashv += _ho_key[_ho_i]; \ hashv += (hashv << 10); \ hashv ^= (hashv >> 6); \ } \ hashv += (hashv << 3); \ hashv ^= (hashv >> 11); \ hashv += (hashv << 15); \ } while (0) #define HASH_JEN_MIX(a,b,c) \ do { \ a -= b; a -= c; a ^= ( c >> 13 ); \ b -= c; b -= a; b ^= ( a << 8 ); \ c -= a; c -= b; c ^= ( b >> 13 ); \ a -= b; a -= c; a ^= ( c >> 12 ); \ b -= c; b -= a; b ^= ( a << 16 ); \ c -= a; c -= b; c ^= ( b >> 5 ); \ a -= b; a -= c; a ^= ( c >> 3 ); \ b -= c; b -= a; b ^= ( a << 10 ); \ c -= a; c -= b; c ^= ( b >> 15 ); \ } while (0) #define HASH_JEN(key,keylen,hashv) \ do { \ unsigned _hj_i,_hj_j,_hj_k; \ unsigned const char *_hj_key=(unsigned const char*)(key); \ hashv = 0xfeedbeefu; \ _hj_i = _hj_j = 0x9e3779b9u; \ _hj_k = (unsigned)(keylen); \ while (_hj_k >= 12U) { \ _hj_i += (_hj_key[0] + ( (unsigned)_hj_key[1] << 8 ) \ + ( (unsigned)_hj_key[2] << 16 ) \ + ( (unsigned)_hj_key[3] << 24 ) ); \ _hj_j += (_hj_key[4] + ( (unsigned)_hj_key[5] << 8 ) \ + ( (unsigned)_hj_key[6] << 16 ) \ + ( (unsigned)_hj_key[7] << 24 ) ); \ hashv += (_hj_key[8] + ( (unsigned)_hj_key[9] << 8 ) \ + ( (unsigned)_hj_key[10] << 16 ) \ + ( (unsigned)_hj_key[11] << 24 ) ); \ \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ \ _hj_key += 12; \ _hj_k -= 12U; \ } \ hashv += (unsigned)(keylen); \ switch ( _hj_k ) { \ case 11: hashv += ( (unsigned)_hj_key[10] << 24 ); /* FALLTHROUGH */ \ case 10: hashv += ( (unsigned)_hj_key[9] << 16 ); /* FALLTHROUGH */ \ case 9: hashv += ( (unsigned)_hj_key[8] << 8 ); /* FALLTHROUGH */ \ case 8: _hj_j += ( (unsigned)_hj_key[7] << 24 ); /* FALLTHROUGH */ \ case 7: _hj_j += ( (unsigned)_hj_key[6] << 16 ); /* FALLTHROUGH */ \ case 6: _hj_j += ( (unsigned)_hj_key[5] << 8 ); /* FALLTHROUGH */ \ case 5: _hj_j += _hj_key[4]; /* FALLTHROUGH */ \ case 4: _hj_i += ( (unsigned)_hj_key[3] << 24 ); /* FALLTHROUGH */ \ case 3: _hj_i += ( (unsigned)_hj_key[2] << 16 ); /* FALLTHROUGH */ \ case 2: _hj_i += ( (unsigned)_hj_key[1] << 8 ); /* FALLTHROUGH */ \ case 1: _hj_i += _hj_key[0]; \ } \ HASH_JEN_MIX(_hj_i, _hj_j, hashv); \ } while (0) /* The Paul Hsieh hash function */ #undef get16bits #if (defined(__GNUC__) && defined(__i386__)) || defined(__WATCOMC__) \ || defined(_MSC_VER) || defined (__BORLANDC__) || defined (__TURBOC__) #define get16bits(d) (*((const uint16_t *) (d))) #endif #if !defined (get16bits) #define get16bits(d) ((((uint32_t)(((const uint8_t *)(d))[1])) << 8) \ +(uint32_t)(((const uint8_t *)(d))[0]) ) #endif #define HASH_SFH(key,keylen,hashv) \ do { \ unsigned const char *_sfh_key=(unsigned const char*)(key); \ uint32_t _sfh_tmp, _sfh_len = (uint32_t)keylen; \ \ unsigned _sfh_rem = _sfh_len & 3U; \ _sfh_len >>= 2; \ hashv = 0xcafebabeu; \ \ /* Main loop */ \ for (;_sfh_len > 0U; _sfh_len--) { \ hashv += get16bits (_sfh_key); \ _sfh_tmp = ((uint32_t)(get16bits (_sfh_key+2)) << 11) ^ hashv; \ hashv = (hashv << 16) ^ _sfh_tmp; \ _sfh_key += 2U*sizeof (uint16_t); \ hashv += hashv >> 11; \ } \ \ /* Handle end cases */ \ switch (_sfh_rem) { \ case 3: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 16; \ hashv ^= (uint32_t)(_sfh_key[sizeof (uint16_t)]) << 18; \ hashv += hashv >> 11; \ break; \ case 2: hashv += get16bits (_sfh_key); \ hashv ^= hashv << 11; \ hashv += hashv >> 17; \ break; \ case 1: hashv += *_sfh_key; \ hashv ^= hashv << 10; \ hashv += hashv >> 1; \ } \ \ /* Force "avalanching" of final 127 bits */ \ hashv ^= hashv << 3; \ hashv += hashv >> 5; \ hashv ^= hashv << 4; \ hashv += hashv >> 17; \ hashv ^= hashv << 25; \ hashv += hashv >> 6; \ } while (0) #ifdef HASH_USING_NO_STRICT_ALIASING /* The MurmurHash exploits some CPU's (x86,x86_64) tolerance for unaligned reads. * For other types of CPU's (e.g. Sparc) an unaligned read causes a bus error. * MurmurHash uses the faster approach only on CPU's where we know it's safe. * * Note the preprocessor built-in defines can be emitted using: * * gcc -m64 -dM -E - < /dev/null (on gcc) * cc -## a.c (where a.c is a simple test file) (Sun Studio) */ #if (defined(__i386__) || defined(__x86_64__) || defined(_M_IX86)) #define MUR_GETBLOCK(p,i) p[i] #else /* non intel */ #define MUR_PLUS0_ALIGNED(p) (((unsigned long)p & 3UL) == 0UL) #define MUR_PLUS1_ALIGNED(p) (((unsigned long)p & 3UL) == 1UL) #define MUR_PLUS2_ALIGNED(p) (((unsigned long)p & 3UL) == 2UL) #define MUR_PLUS3_ALIGNED(p) (((unsigned long)p & 3UL) == 3UL) #define WP(p) ((uint32_t*)((unsigned long)(p) & ~3UL)) #if (defined(__BIG_ENDIAN__) || defined(SPARC) || defined(__ppc__) || defined(__ppc64__)) #define MUR_THREE_ONE(p) ((((*WP(p))&0x00ffffff) << 8) | (((*(WP(p)+1))&0xff000000) >> 24)) #define MUR_TWO_TWO(p) ((((*WP(p))&0x0000ffff) <<16) | (((*(WP(p)+1))&0xffff0000) >> 16)) #define MUR_ONE_THREE(p) ((((*WP(p))&0x000000ff) <<24) | (((*(WP(p)+1))&0xffffff00) >> 8)) #else /* assume little endian non-intel */ #define MUR_THREE_ONE(p) ((((*WP(p))&0xffffff00) >> 8) | (((*(WP(p)+1))&0x000000ff) << 24)) #define MUR_TWO_TWO(p) ((((*WP(p))&0xffff0000) >>16) | (((*(WP(p)+1))&0x0000ffff) << 16)) #define MUR_ONE_THREE(p) ((((*WP(p))&0xff000000) >>24) | (((*(WP(p)+1))&0x00ffffff) << 8)) #endif #define MUR_GETBLOCK(p,i) (MUR_PLUS0_ALIGNED(p) ? ((p)[i]) : \ (MUR_PLUS1_ALIGNED(p) ? MUR_THREE_ONE(p) : \ (MUR_PLUS2_ALIGNED(p) ? MUR_TWO_TWO(p) : \ MUR_ONE_THREE(p)))) #endif #define MUR_ROTL32(x,r) (((x) << (r)) | ((x) >> (32 - (r)))) #define MUR_FMIX(_h) \ do { \ _h ^= _h >> 16; \ _h *= 0x85ebca6bu; \ _h ^= _h >> 13; \ _h *= 0xc2b2ae35u; \ _h ^= _h >> 16; \ } while (0) #define HASH_MUR(key,keylen,hashv) \ do { \ const uint8_t *_mur_data = (const uint8_t*)(key); \ const int _mur_nblocks = (int)(keylen) / 4; \ uint32_t _mur_h1 = 0xf88D5353u; \ uint32_t _mur_c1 = 0xcc9e2d51u; \ uint32_t _mur_c2 = 0x1b873593u; \ uint32_t _mur_k1 = 0; \ const uint8_t *_mur_tail; \ const uint32_t *_mur_blocks = (const uint32_t*)(_mur_data+(_mur_nblocks*4)); \ int _mur_i; \ for (_mur_i = -_mur_nblocks; _mur_i != 0; _mur_i++) { \ _mur_k1 = MUR_GETBLOCK(_mur_blocks,_mur_i); \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ _mur_k1 *= _mur_c2; \ \ _mur_h1 ^= _mur_k1; \ _mur_h1 = MUR_ROTL32(_mur_h1,13); \ _mur_h1 = (_mur_h1*5U) + 0xe6546b64u; \ } \ _mur_tail = (const uint8_t*)(_mur_data + (_mur_nblocks*4)); \ _mur_k1=0; \ switch ((keylen) & 3U) { \ case 0: break; \ case 3: _mur_k1 ^= (uint32_t)_mur_tail[2] << 16; /* FALLTHROUGH */ \ case 2: _mur_k1 ^= (uint32_t)_mur_tail[1] << 8; /* FALLTHROUGH */ \ case 1: _mur_k1 ^= (uint32_t)_mur_tail[0]; \ _mur_k1 *= _mur_c1; \ _mur_k1 = MUR_ROTL32(_mur_k1,15); \ _mur_k1 *= _mur_c2; \ _mur_h1 ^= _mur_k1; \ } \ _mur_h1 ^= (uint32_t)(keylen); \ MUR_FMIX(_mur_h1); \ hashv = _mur_h1; \ } while (0) #endif /* HASH_USING_NO_STRICT_ALIASING */ /* iterate over items in a known bucket to find desired item */ #define HASH_FIND_IN_BKT(tbl,hh,head,keyptr,keylen_in,hashval,out) \ do { \ if ((head).hh_head != NULL) { \ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (head).hh_head)); \ } else { \ (out) = NULL; \ } \ while ((out) != NULL) { \ if ((out)->hh.hashv == (hashval) && (out)->hh.keylen == (keylen_in)) { \ if (HASH_KEYCMP((out)->hh.key, keyptr, keylen_in) == 0) { \ break; \ } \ } \ if ((out)->hh.hh_next != NULL) { \ DECLTYPE_ASSIGN(out, ELMT_FROM_HH(tbl, (out)->hh.hh_next)); \ } else { \ (out) = NULL; \ } \ } \ } while (0) /* add an item to a bucket */ #define HASH_ADD_TO_BKT(head,hh,addhh,oomed) \ do { \ UT_hash_bucket *_ha_head = &(head); \ _ha_head->count++; \ (addhh)->hh_next = _ha_head->hh_head; \ (addhh)->hh_prev = NULL; \ if (_ha_head->hh_head != NULL) { \ _ha_head->hh_head->hh_prev = (addhh); \ } \ _ha_head->hh_head = (addhh); \ if ((_ha_head->count >= ((_ha_head->expand_mult + 1U) * HASH_BKT_CAPACITY_THRESH)) \ && !(addhh)->tbl->noexpand) { \ HASH_EXPAND_BUCKETS(addhh,(addhh)->tbl, oomed); \ IF_HASH_NONFATAL_OOM( \ if (oomed) { \ HASH_DEL_IN_BKT(head,addhh); \ } \ ) \ } \ } while (0) /* remove an item from a given bucket */ #define HASH_DEL_IN_BKT(head,delhh) \ do { \ UT_hash_bucket *_hd_head = &(head); \ _hd_head->count--; \ if (_hd_head->hh_head == (delhh)) { \ _hd_head->hh_head = (delhh)->hh_next; \ } \ if ((delhh)->hh_prev) { \ (delhh)->hh_prev->hh_next = (delhh)->hh_next; \ } \ if ((delhh)->hh_next) { \ (delhh)->hh_next->hh_prev = (delhh)->hh_prev; \ } \ } while (0) /* Bucket expansion has the effect of doubling the number of buckets * and redistributing the items into the new buckets. Ideally the * items will distribute more or less evenly into the new buckets * (the extent to which this is true is a measure of the quality of * the hash function as it applies to the key domain). * * With the items distributed into more buckets, the chain length * (item count) in each bucket is reduced. Thus by expanding buckets * the hash keeps a bound on the chain length. This bounded chain * length is the essence of how a hash provides constant time lookup. * * The calculation of tbl->ideal_chain_maxlen below deserves some * explanation. First, keep in mind that we're calculating the ideal * maximum chain length based on the *new* (doubled) bucket count. * In fractions this is just n/b (n=number of items,b=new num buckets). * Since the ideal chain length is an integer, we want to calculate * ceil(n/b). We don't depend on floating point arithmetic in this * hash, so to calculate ceil(n/b) with integers we could write * * ceil(n/b) = (n/b) + ((n%b)?1:0) * * and in fact a previous version of this hash did just that. * But now we have improved things a bit by recognizing that b is * always a power of two. We keep its base 2 log handy (call it lb), * so now we can write this with a bit shift and logical AND: * * ceil(n/b) = (n>>lb) + ( (n & (b-1)) ? 1:0) * */ #define HASH_EXPAND_BUCKETS(hh,tbl,oomed) \ do { \ unsigned _he_bkt; \ unsigned _he_bkt_i; \ struct UT_hash_handle *_he_thh, *_he_hh_nxt; \ UT_hash_bucket *_he_new_buckets, *_he_newbkt; \ _he_new_buckets = (UT_hash_bucket*)uthash_malloc( \ 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ if (!_he_new_buckets) { \ HASH_RECORD_OOM(oomed); \ } else { \ uthash_bzero(_he_new_buckets, \ 2UL * (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ (tbl)->ideal_chain_maxlen = \ ((tbl)->num_items >> ((tbl)->log2_num_buckets+1U)) + \ ((((tbl)->num_items & (((tbl)->num_buckets*2U)-1U)) != 0U) ? 1U : 0U); \ (tbl)->nonideal_items = 0; \ for (_he_bkt_i = 0; _he_bkt_i < (tbl)->num_buckets; _he_bkt_i++) { \ _he_thh = (tbl)->buckets[ _he_bkt_i ].hh_head; \ while (_he_thh != NULL) { \ _he_hh_nxt = _he_thh->hh_next; \ HASH_TO_BKT(_he_thh->hashv, (tbl)->num_buckets * 2U, _he_bkt); \ _he_newbkt = &(_he_new_buckets[_he_bkt]); \ if (++(_he_newbkt->count) > (tbl)->ideal_chain_maxlen) { \ (tbl)->nonideal_items++; \ if (_he_newbkt->count > _he_newbkt->expand_mult * (tbl)->ideal_chain_maxlen) { \ _he_newbkt->expand_mult++; \ } \ } \ _he_thh->hh_prev = NULL; \ _he_thh->hh_next = _he_newbkt->hh_head; \ if (_he_newbkt->hh_head != NULL) { \ _he_newbkt->hh_head->hh_prev = _he_thh; \ } \ _he_newbkt->hh_head = _he_thh; \ _he_thh = _he_hh_nxt; \ } \ } \ uthash_free((tbl)->buckets, (tbl)->num_buckets * sizeof(struct UT_hash_bucket)); \ (tbl)->num_buckets *= 2U; \ (tbl)->log2_num_buckets++; \ (tbl)->buckets = _he_new_buckets; \ (tbl)->ineff_expands = ((tbl)->nonideal_items > ((tbl)->num_items >> 1)) ? \ ((tbl)->ineff_expands+1U) : 0U; \ if ((tbl)->ineff_expands > 1U) { \ (tbl)->noexpand = 1; \ uthash_noexpand_fyi(tbl); \ } \ uthash_expand_fyi(tbl); \ } \ } while (0) /* This is an adaptation of Simon Tatham's O(n log(n)) mergesort */ /* Note that HASH_SORT assumes the hash handle name to be hh. * HASH_SRT was added to allow the hash handle name to be passed in. */ #define HASH_SORT(head,cmpfcn) HASH_SRT(hh,head,cmpfcn) #define HASH_SRT(hh,head,cmpfcn) \ do { \ unsigned _hs_i; \ unsigned _hs_looping,_hs_nmerges,_hs_insize,_hs_psize,_hs_qsize; \ struct UT_hash_handle *_hs_p, *_hs_q, *_hs_e, *_hs_list, *_hs_tail; \ if (head != NULL) { \ _hs_insize = 1; \ _hs_looping = 1; \ _hs_list = &((head)->hh); \ while (_hs_looping != 0U) { \ _hs_p = _hs_list; \ _hs_list = NULL; \ _hs_tail = NULL; \ _hs_nmerges = 0; \ while (_hs_p != NULL) { \ _hs_nmerges++; \ _hs_q = _hs_p; \ _hs_psize = 0; \ for (_hs_i = 0; _hs_i < _hs_insize; ++_hs_i) { \ _hs_psize++; \ _hs_q = ((_hs_q->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ if (_hs_q == NULL) { \ break; \ } \ } \ _hs_qsize = _hs_insize; \ while ((_hs_psize != 0U) || ((_hs_qsize != 0U) && (_hs_q != NULL))) { \ if (_hs_psize == 0U) { \ _hs_e = _hs_q; \ _hs_q = ((_hs_q->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ _hs_qsize--; \ } else if ((_hs_qsize == 0U) || (_hs_q == NULL)) { \ _hs_e = _hs_p; \ if (_hs_p != NULL) { \ _hs_p = ((_hs_p->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ } \ _hs_psize--; \ } else if ((cmpfcn( \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_p)), \ DECLTYPE(head)(ELMT_FROM_HH((head)->hh.tbl, _hs_q)) \ )) <= 0) { \ _hs_e = _hs_p; \ if (_hs_p != NULL) { \ _hs_p = ((_hs_p->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_p->next) : NULL); \ } \ _hs_psize--; \ } else { \ _hs_e = _hs_q; \ _hs_q = ((_hs_q->next != NULL) ? \ HH_FROM_ELMT((head)->hh.tbl, _hs_q->next) : NULL); \ _hs_qsize--; \ } \ if ( _hs_tail != NULL ) { \ _hs_tail->next = ((_hs_e != NULL) ? \ ELMT_FROM_HH((head)->hh.tbl, _hs_e) : NULL); \ } else { \ _hs_list = _hs_e; \ } \ if (_hs_e != NULL) { \ _hs_e->prev = ((_hs_tail != NULL) ? \ ELMT_FROM_HH((head)->hh.tbl, _hs_tail) : NULL); \ } \ _hs_tail = _hs_e; \ } \ _hs_p = _hs_q; \ } \ if (_hs_tail != NULL) { \ _hs_tail->next = NULL; \ } \ if (_hs_nmerges <= 1U) { \ _hs_looping = 0; \ (head)->hh.tbl->tail = _hs_tail; \ DECLTYPE_ASSIGN(head, ELMT_FROM_HH((head)->hh.tbl, _hs_list)); \ } \ _hs_insize *= 2U; \ } \ HASH_FSCK(hh, head, "HASH_SRT"); \ } \ } while (0) /* This function selects items from one hash into another hash. * The end result is that the selected items have dual presence * in both hashes. There is no copy of the items made; rather * they are added into the new hash through a secondary hash * hash handle that must be present in the structure. */ #define HASH_SELECT(hh_dst, dst, hh_src, src, cond) \ do { \ unsigned _src_bkt, _dst_bkt; \ void *_last_elt = NULL, *_elt; \ UT_hash_handle *_src_hh, *_dst_hh, *_last_elt_hh=NULL; \ ptrdiff_t _dst_hho = ((char*)(&(dst)->hh_dst) - (char*)(dst)); \ if ((src) != NULL) { \ for (_src_bkt=0; _src_bkt < (src)->hh_src.tbl->num_buckets; _src_bkt++) { \ for (_src_hh = (src)->hh_src.tbl->buckets[_src_bkt].hh_head; \ _src_hh != NULL; \ _src_hh = _src_hh->hh_next) { \ _elt = ELMT_FROM_HH((src)->hh_src.tbl, _src_hh); \ if (cond(_elt)) { \ IF_HASH_NONFATAL_OOM( int _hs_oomed = 0; ) \ _dst_hh = (UT_hash_handle*)(((char*)_elt) + _dst_hho); \ _dst_hh->key = _src_hh->key; \ _dst_hh->keylen = _src_hh->keylen; \ _dst_hh->hashv = _src_hh->hashv; \ _dst_hh->prev = _last_elt; \ _dst_hh->next = NULL; \ if (_last_elt_hh != NULL) { \ _last_elt_hh->next = _elt; \ } \ if ((dst) == NULL) { \ DECLTYPE_ASSIGN(dst, _elt); \ HASH_MAKE_TABLE(hh_dst, dst, _hs_oomed); \ IF_HASH_NONFATAL_OOM( \ if (_hs_oomed) { \ uthash_nonfatal_oom(_elt); \ (dst) = NULL; \ continue; \ } \ ) \ } else { \ _dst_hh->tbl = (dst)->hh_dst.tbl; \ } \ HASH_TO_BKT(_dst_hh->hashv, _dst_hh->tbl->num_buckets, _dst_bkt); \ HASH_ADD_TO_BKT(_dst_hh->tbl->buckets[_dst_bkt], hh_dst, _dst_hh, _hs_oomed); \ (dst)->hh_dst.tbl->num_items++; \ IF_HASH_NONFATAL_OOM( \ if (_hs_oomed) { \ HASH_ROLLBACK_BKT(hh_dst, dst, _dst_hh); \ HASH_DELETE_HH(hh_dst, dst, _dst_hh); \ _dst_hh->tbl = NULL; \ uthash_nonfatal_oom(_elt); \ continue; \ } \ ) \ HASH_BLOOM_ADD(_dst_hh->tbl, _dst_hh->hashv); \ _last_elt = _elt; \ _last_elt_hh = _dst_hh; \ } \ } \ } \ } \ HASH_FSCK(hh_dst, dst, "HASH_SELECT"); \ } while (0) #define HASH_CLEAR(hh,head) \ do { \ if ((head) != NULL) { \ HASH_BLOOM_FREE((head)->hh.tbl); \ uthash_free((head)->hh.tbl->buckets, \ (head)->hh.tbl->num_buckets*sizeof(struct UT_hash_bucket)); \ uthash_free((head)->hh.tbl, sizeof(UT_hash_table)); \ (head) = NULL; \ } \ } while (0) #define HASH_OVERHEAD(hh,head) \ (((head) != NULL) ? ( \ (size_t)(((head)->hh.tbl->num_items * sizeof(UT_hash_handle)) + \ ((head)->hh.tbl->num_buckets * sizeof(UT_hash_bucket)) + \ sizeof(UT_hash_table) + \ (HASH_BLOOM_BYTELEN))) : 0U) #ifdef NO_DECLTYPE #define HASH_ITER(hh,head,el,tmp) \ for(((el)=(head)), ((*(char**)(&(tmp)))=(char*)((head!=NULL)?(head)->hh.next:NULL)); \ (el) != NULL; ((el)=(tmp)), ((*(char**)(&(tmp)))=(char*)((tmp!=NULL)?(tmp)->hh.next:NULL))) #else #define HASH_ITER(hh,head,el,tmp) \ for(((el)=(head)), ((tmp)=DECLTYPE(el)((head!=NULL)?(head)->hh.next:NULL)); \ (el) != NULL; ((el)=(tmp)), ((tmp)=DECLTYPE(el)((tmp!=NULL)?(tmp)->hh.next:NULL))) #endif /* obtain a count of items in the hash */ #define HASH_COUNT(head) HASH_CNT(hh,head) #define HASH_CNT(hh,head) ((head != NULL)?((head)->hh.tbl->num_items):0U) typedef struct UT_hash_bucket { struct UT_hash_handle *hh_head; unsigned count; /* expand_mult is normally set to 0. In this situation, the max chain length * threshold is enforced at its default value, HASH_BKT_CAPACITY_THRESH. (If * the bucket's chain exceeds this length, bucket expansion is triggered). * However, setting expand_mult to a non-zero value delays bucket expansion * (that would be triggered by additions to this particular bucket) * until its chain length reaches a *multiple* of HASH_BKT_CAPACITY_THRESH. * (The multiplier is simply expand_mult+1). The whole idea of this * multiplier is to reduce bucket expansions, since they are expensive, in * situations where we know that a particular bucket tends to be overused. * It is better to let its chain length grow to a longer yet-still-bounded * value, than to do an O(n) bucket expansion too often. */ unsigned expand_mult; } UT_hash_bucket; /* random signature used only to find hash tables in external analysis */ #define HASH_SIGNATURE 0xa0111fe1u #define HASH_BLOOM_SIGNATURE 0xb12220f2u typedef struct UT_hash_table { UT_hash_bucket *buckets; unsigned num_buckets, log2_num_buckets; unsigned num_items; struct UT_hash_handle *tail; /* tail hh in app order, for fast append */ ptrdiff_t hho; /* hash handle offset (byte pos of hash handle in element */ /* in an ideal situation (all buckets used equally), no bucket would have * more than ceil(#items/#buckets) items. that's the ideal chain length. */ unsigned ideal_chain_maxlen; /* nonideal_items is the number of items in the hash whose chain position * exceeds the ideal chain maxlen. these items pay the penalty for an uneven * hash distribution; reaching them in a chain traversal takes >ideal steps */ unsigned nonideal_items; /* ineffective expands occur when a bucket doubling was performed, but * afterward, more than half the items in the hash had nonideal chain * positions. If this happens on two consecutive expansions we inhibit any * further expansion, as it's not helping; this happens when the hash * function isn't a good fit for the key domain. When expansion is inhibited * the hash will still work, albeit no longer in constant time. */ unsigned ineff_expands, noexpand; uint32_t signature; /* used only to find hash tables in external analysis */ #ifdef HASH_BLOOM uint32_t bloom_sig; /* used only to test bloom exists in external analysis */ uint8_t *bloom_bv; uint8_t bloom_nbits; #endif } UT_hash_table; typedef struct UT_hash_handle { struct UT_hash_table *tbl; void *prev; /* prev element in app order */ void *next; /* next element in app order */ struct UT_hash_handle *hh_prev; /* previous hh in bucket order */ struct UT_hash_handle *hh_next; /* next hh in bucket order */ void *key; /* ptr to enclosing struct's key */ unsigned keylen; /* enclosing struct's key len */ unsigned hashv; /* result of hash-fcn(key) */ } UT_hash_handle; #endif /* UTHASH_H */ mosquitto-2.0.18/deps/utlist.h000066400000000000000000002371401450213760600163110ustar00rootroot00000000000000/* Copyright (c) 2007-2018, Troy D. Hanson http://troydhanson.github.com/uthash/ All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef UTLIST_H #define UTLIST_H #define UTLIST_VERSION 2.1.0 #include /* * This file contains macros to manipulate singly and doubly-linked lists. * * 1. LL_ macros: singly-linked lists. * 2. DL_ macros: doubly-linked lists. * 3. CDL_ macros: circular doubly-linked lists. * * To use singly-linked lists, your structure must have a "next" pointer. * To use doubly-linked lists, your structure must "prev" and "next" pointers. * Either way, the pointer to the head of the list must be initialized to NULL. * * ----------------.EXAMPLE ------------------------- * struct item { * int id; * struct item *prev, *next; * } * * struct item *list = NULL: * * int main() { * struct item *item; * ... allocate and populate item ... * DL_APPEND(list, item); * } * -------------------------------------------------- * * For doubly-linked lists, the append and delete macros are O(1) * For singly-linked lists, append and delete are O(n) but prepend is O(1) * The sort macro is O(n log(n)) for all types of single/double/circular lists. */ /* These macros use decltype or the earlier __typeof GNU extension. As decltype is only available in newer compilers (VS2010 or gcc 4.3+ when compiling c++ source) this code uses whatever method is needed or, for VS2008 where neither is available, uses casting workarounds. */ #if !defined(LDECLTYPE) && !defined(NO_DECLTYPE) #if defined(_MSC_VER) /* MS compiler */ #if _MSC_VER >= 1600 && defined(__cplusplus) /* VS2010 or newer in C++ mode */ #define LDECLTYPE(x) decltype(x) #else /* VS2008 or older (or VS2010 in C mode) */ #define NO_DECLTYPE #endif #elif defined(__BORLANDC__) || defined(__ICCARM__) || defined(__LCC__) || defined(__WATCOMC__) #define NO_DECLTYPE #else /* GNU, Sun and other compilers */ #define LDECLTYPE(x) __typeof(x) #endif #endif /* for VS2008 we use some workarounds to get around the lack of decltype, * namely, we always reassign our tmp variable to the list head if we need * to dereference its prev/next pointers, and save/restore the real head.*/ #ifdef NO_DECLTYPE #define IF_NO_DECLTYPE(x) x #define LDECLTYPE(x) char* #define UTLIST_SV(elt,list) _tmp = (char*)(list); {char **_alias = (char**)&(list); *_alias = (elt); } #define UTLIST_NEXT(elt,list,next) ((char*)((list)->next)) #define UTLIST_NEXTASGN(elt,list,to,next) { char **_alias = (char**)&((list)->next); *_alias=(char*)(to); } /* #define UTLIST_PREV(elt,list,prev) ((char*)((list)->prev)) */ #define UTLIST_PREVASGN(elt,list,to,prev) { char **_alias = (char**)&((list)->prev); *_alias=(char*)(to); } #define UTLIST_RS(list) { char **_alias = (char**)&(list); *_alias=_tmp; } #define UTLIST_CASTASGN(a,b) { char **_alias = (char**)&(a); *_alias=(char*)(b); } #else #define IF_NO_DECLTYPE(x) #define UTLIST_SV(elt,list) #define UTLIST_NEXT(elt,list,next) ((elt)->next) #define UTLIST_NEXTASGN(elt,list,to,next) ((elt)->next)=(to) /* #define UTLIST_PREV(elt,list,prev) ((elt)->prev) */ #define UTLIST_PREVASGN(elt,list,to,prev) ((elt)->prev)=(to) #define UTLIST_RS(list) #define UTLIST_CASTASGN(a,b) (a)=(b) #endif /****************************************************************************** * The sort macro is an adaptation of Simon Tatham's O(n log(n)) mergesort * * Unwieldy variable names used here to avoid shadowing passed-in variables. * *****************************************************************************/ #define LL_SORT(list, cmp) \ LL_SORT2(list, cmp, next) #define LL_SORT2(list, cmp, next) \ do { \ LDECLTYPE(list) _ls_p; \ LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ UTLIST_CASTASGN(_ls_p,list); \ (list) = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ } else if (_ls_qsize == 0 || !_ls_q) { \ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else { \ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ } \ if (_ls_tail) { \ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ } else { \ UTLIST_CASTASGN(list,_ls_e); \ } \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ if (_ls_tail) { \ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ } \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) #define DL_SORT(list, cmp) \ DL_SORT2(list, cmp, prev, next) #define DL_SORT2(list, cmp, prev, next) \ do { \ LDECLTYPE(list) _ls_p; \ LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ IF_NO_DECLTYPE(LDECLTYPE(list) _tmp;) \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ UTLIST_CASTASGN(_ls_p,list); \ (list) = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ UTLIST_SV(_ls_q,list); _ls_q = UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while ((_ls_psize > 0) || ((_ls_qsize > 0) && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ } else if ((_ls_qsize == 0) || (!_ls_q)) { \ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ } else { \ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ } \ if (_ls_tail) { \ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ } else { \ UTLIST_CASTASGN(list,_ls_e); \ } \ UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ UTLIST_CASTASGN((list)->prev, _ls_tail); \ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,NULL,next); UTLIST_RS(list); \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) #define CDL_SORT(list, cmp) \ CDL_SORT2(list, cmp, prev, next) #define CDL_SORT2(list, cmp, prev, next) \ do { \ LDECLTYPE(list) _ls_p; \ LDECLTYPE(list) _ls_q; \ LDECLTYPE(list) _ls_e; \ LDECLTYPE(list) _ls_tail; \ LDECLTYPE(list) _ls_oldhead; \ LDECLTYPE(list) _tmp; \ int _ls_insize, _ls_nmerges, _ls_psize, _ls_qsize, _ls_i, _ls_looping; \ if (list) { \ _ls_insize = 1; \ _ls_looping = 1; \ while (_ls_looping) { \ UTLIST_CASTASGN(_ls_p,list); \ UTLIST_CASTASGN(_ls_oldhead,list); \ (list) = NULL; \ _ls_tail = NULL; \ _ls_nmerges = 0; \ while (_ls_p) { \ _ls_nmerges++; \ _ls_q = _ls_p; \ _ls_psize = 0; \ for (_ls_i = 0; _ls_i < _ls_insize; _ls_i++) { \ _ls_psize++; \ UTLIST_SV(_ls_q,list); \ if (UTLIST_NEXT(_ls_q,list,next) == _ls_oldhead) { \ _ls_q = NULL; \ } else { \ _ls_q = UTLIST_NEXT(_ls_q,list,next); \ } \ UTLIST_RS(list); \ if (!_ls_q) break; \ } \ _ls_qsize = _ls_insize; \ while (_ls_psize > 0 || (_ls_qsize > 0 && _ls_q)) { \ if (_ls_psize == 0) { \ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } else if (_ls_qsize == 0 || !_ls_q) { \ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else if (cmp(_ls_p,_ls_q) <= 0) { \ _ls_e = _ls_p; UTLIST_SV(_ls_p,list); _ls_p = \ UTLIST_NEXT(_ls_p,list,next); UTLIST_RS(list); _ls_psize--; \ if (_ls_p == _ls_oldhead) { _ls_p = NULL; } \ } else { \ _ls_e = _ls_q; UTLIST_SV(_ls_q,list); _ls_q = \ UTLIST_NEXT(_ls_q,list,next); UTLIST_RS(list); _ls_qsize--; \ if (_ls_q == _ls_oldhead) { _ls_q = NULL; } \ } \ if (_ls_tail) { \ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_ls_e,next); UTLIST_RS(list); \ } else { \ UTLIST_CASTASGN(list,_ls_e); \ } \ UTLIST_SV(_ls_e,list); UTLIST_PREVASGN(_ls_e,list,_ls_tail,prev); UTLIST_RS(list); \ _ls_tail = _ls_e; \ } \ _ls_p = _ls_q; \ } \ UTLIST_CASTASGN((list)->prev,_ls_tail); \ UTLIST_CASTASGN(_tmp,list); \ UTLIST_SV(_ls_tail,list); UTLIST_NEXTASGN(_ls_tail,list,_tmp,next); UTLIST_RS(list); \ if (_ls_nmerges <= 1) { \ _ls_looping=0; \ } \ _ls_insize *= 2; \ } \ } \ } while (0) /****************************************************************************** * singly linked list macros (non-circular) * *****************************************************************************/ #define LL_PREPEND(head,add) \ LL_PREPEND2(head,add,next) #define LL_PREPEND2(head,add,next) \ do { \ (add)->next = (head); \ (head) = (add); \ } while (0) #define LL_CONCAT(head1,head2) \ LL_CONCAT2(head1,head2,next) #define LL_CONCAT2(head1,head2,next) \ do { \ LDECLTYPE(head1) _tmp; \ if (head1) { \ _tmp = (head1); \ while (_tmp->next) { _tmp = _tmp->next; } \ _tmp->next=(head2); \ } else { \ (head1)=(head2); \ } \ } while (0) #define LL_APPEND(head,add) \ LL_APPEND2(head,add,next) #define LL_APPEND2(head,add,next) \ do { \ LDECLTYPE(head) _tmp; \ (add)->next=NULL; \ if (head) { \ _tmp = (head); \ while (_tmp->next) { _tmp = _tmp->next; } \ _tmp->next=(add); \ } else { \ (head)=(add); \ } \ } while (0) #define LL_INSERT_INORDER(head,add,cmp) \ LL_INSERT_INORDER2(head,add,cmp,next) #define LL_INSERT_INORDER2(head,add,cmp,next) \ do { \ LDECLTYPE(head) _tmp; \ if (head) { \ LL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ LL_APPEND_ELEM2(head, _tmp, add, next); \ } else { \ (head) = (add); \ (head)->next = NULL; \ } \ } while (0) #define LL_LOWER_BOUND(head,elt,like,cmp) \ LL_LOWER_BOUND2(head,elt,like,cmp,next) #define LL_LOWER_BOUND2(head,elt,like,cmp,next) \ do { \ if ((head) == NULL || (cmp(head, like)) >= 0) { \ (elt) = NULL; \ } else { \ for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ if (cmp((elt)->next, like) >= 0) { \ break; \ } \ } \ } \ } while (0) #define LL_DELETE(head,del) \ LL_DELETE2(head,del,next) #define LL_DELETE2(head,del,next) \ do { \ LDECLTYPE(head) _tmp; \ if ((head) == (del)) { \ (head)=(head)->next; \ } else { \ _tmp = (head); \ while (_tmp->next && (_tmp->next != (del))) { \ _tmp = _tmp->next; \ } \ if (_tmp->next) { \ _tmp->next = (del)->next; \ } \ } \ } while (0) #define LL_COUNT(head,el,counter) \ LL_COUNT2(head,el,counter,next) \ #define LL_COUNT2(head,el,counter,next) \ do { \ (counter) = 0; \ LL_FOREACH2(head,el,next) { ++(counter); } \ } while (0) #define LL_FOREACH(head,el) \ LL_FOREACH2(head,el,next) #define LL_FOREACH2(head,el,next) \ for ((el) = (head); el; (el) = (el)->next) #define LL_FOREACH_SAFE(head,el,tmp) \ LL_FOREACH_SAFE2(head,el,tmp,next) #define LL_FOREACH_SAFE2(head,el,tmp,next) \ for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) #define LL_SEARCH_SCALAR(head,out,field,val) \ LL_SEARCH_SCALAR2(head,out,field,val,next) #define LL_SEARCH_SCALAR2(head,out,field,val,next) \ do { \ LL_FOREACH2(head,out,next) { \ if ((out)->field == (val)) break; \ } \ } while (0) #define LL_SEARCH(head,out,elt,cmp) \ LL_SEARCH2(head,out,elt,cmp,next) #define LL_SEARCH2(head,out,elt,cmp,next) \ do { \ LL_FOREACH2(head,out,next) { \ if ((cmp(out,elt))==0) break; \ } \ } while (0) #define LL_REPLACE_ELEM2(head, el, add, next) \ do { \ LDECLTYPE(head) _tmp; \ assert((head) != NULL); \ assert((el) != NULL); \ assert((add) != NULL); \ (add)->next = (el)->next; \ if ((head) == (el)) { \ (head) = (add); \ } else { \ _tmp = (head); \ while (_tmp->next && (_tmp->next != (el))) { \ _tmp = _tmp->next; \ } \ if (_tmp->next) { \ _tmp->next = (add); \ } \ } \ } while (0) #define LL_REPLACE_ELEM(head, el, add) \ LL_REPLACE_ELEM2(head, el, add, next) #define LL_PREPEND_ELEM2(head, el, add, next) \ do { \ if (el) { \ LDECLTYPE(head) _tmp; \ assert((head) != NULL); \ assert((add) != NULL); \ (add)->next = (el); \ if ((head) == (el)) { \ (head) = (add); \ } else { \ _tmp = (head); \ while (_tmp->next && (_tmp->next != (el))) { \ _tmp = _tmp->next; \ } \ if (_tmp->next) { \ _tmp->next = (add); \ } \ } \ } else { \ LL_APPEND2(head, add, next); \ } \ } while (0) \ #define LL_PREPEND_ELEM(head, el, add) \ LL_PREPEND_ELEM2(head, el, add, next) #define LL_APPEND_ELEM2(head, el, add, next) \ do { \ if (el) { \ assert((head) != NULL); \ assert((add) != NULL); \ (add)->next = (el)->next; \ (el)->next = (add); \ } else { \ LL_PREPEND2(head, add, next); \ } \ } while (0) \ #define LL_APPEND_ELEM(head, el, add) \ LL_APPEND_ELEM2(head, el, add, next) #ifdef NO_DECLTYPE /* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ #undef LL_CONCAT2 #define LL_CONCAT2(head1,head2,next) \ do { \ char *_tmp; \ if (head1) { \ _tmp = (char*)(head1); \ while ((head1)->next) { (head1) = (head1)->next; } \ (head1)->next = (head2); \ UTLIST_RS(head1); \ } else { \ (head1)=(head2); \ } \ } while (0) #undef LL_APPEND2 #define LL_APPEND2(head,add,next) \ do { \ if (head) { \ (add)->next = head; /* use add->next as a temp variable */ \ while ((add)->next->next) { (add)->next = (add)->next->next; } \ (add)->next->next=(add); \ } else { \ (head)=(add); \ } \ (add)->next=NULL; \ } while (0) #undef LL_INSERT_INORDER2 #define LL_INSERT_INORDER2(head,add,cmp,next) \ do { \ if ((head) == NULL || (cmp(head, add)) >= 0) { \ (add)->next = (head); \ (head) = (add); \ } else { \ char *_tmp = (char*)(head); \ while ((head)->next != NULL && (cmp((head)->next, add)) < 0) { \ (head) = (head)->next; \ } \ (add)->next = (head)->next; \ (head)->next = (add); \ UTLIST_RS(head); \ } \ } while (0) #undef LL_DELETE2 #define LL_DELETE2(head,del,next) \ do { \ if ((head) == (del)) { \ (head)=(head)->next; \ } else { \ char *_tmp = (char*)(head); \ while ((head)->next && ((head)->next != (del))) { \ (head) = (head)->next; \ } \ if ((head)->next) { \ (head)->next = ((del)->next); \ } \ UTLIST_RS(head); \ } \ } while (0) #undef LL_REPLACE_ELEM2 #define LL_REPLACE_ELEM2(head, el, add, next) \ do { \ assert((head) != NULL); \ assert((el) != NULL); \ assert((add) != NULL); \ if ((head) == (el)) { \ (head) = (add); \ } else { \ (add)->next = head; \ while ((add)->next->next && ((add)->next->next != (el))) { \ (add)->next = (add)->next->next; \ } \ if ((add)->next->next) { \ (add)->next->next = (add); \ } \ } \ (add)->next = (el)->next; \ } while (0) #undef LL_PREPEND_ELEM2 #define LL_PREPEND_ELEM2(head, el, add, next) \ do { \ if (el) { \ assert((head) != NULL); \ assert((add) != NULL); \ if ((head) == (el)) { \ (head) = (add); \ } else { \ (add)->next = (head); \ while ((add)->next->next && ((add)->next->next != (el))) { \ (add)->next = (add)->next->next; \ } \ if ((add)->next->next) { \ (add)->next->next = (add); \ } \ } \ (add)->next = (el); \ } else { \ LL_APPEND2(head, add, next); \ } \ } while (0) \ #endif /* NO_DECLTYPE */ /****************************************************************************** * doubly linked list macros (non-circular) * *****************************************************************************/ #define DL_PREPEND(head,add) \ DL_PREPEND2(head,add,prev,next) #define DL_PREPEND2(head,add,prev,next) \ do { \ (add)->next = (head); \ if (head) { \ (add)->prev = (head)->prev; \ (head)->prev = (add); \ } else { \ (add)->prev = (add); \ } \ (head) = (add); \ } while (0) #define DL_APPEND(head,add) \ DL_APPEND2(head,add,prev,next) #define DL_APPEND2(head,add,prev,next) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (head)->prev->next = (add); \ (head)->prev = (add); \ (add)->next = NULL; \ } else { \ (head)=(add); \ (head)->prev = (head); \ (head)->next = NULL; \ } \ } while (0) #define DL_INSERT_INORDER(head,add,cmp) \ DL_INSERT_INORDER2(head,add,cmp,prev,next) #define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ do { \ LDECLTYPE(head) _tmp; \ if (head) { \ DL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ DL_APPEND_ELEM2(head, _tmp, add, prev, next); \ } else { \ (head) = (add); \ (head)->prev = (head); \ (head)->next = NULL; \ } \ } while (0) #define DL_LOWER_BOUND(head,elt,like,cmp) \ DL_LOWER_BOUND2(head,elt,like,cmp,next) #define DL_LOWER_BOUND2(head,elt,like,cmp,next) \ do { \ if ((head) == NULL || (cmp(head, like)) >= 0) { \ (elt) = NULL; \ } else { \ for ((elt) = (head); (elt)->next != NULL; (elt) = (elt)->next) { \ if ((cmp((elt)->next, like)) >= 0) { \ break; \ } \ } \ } \ } while (0) #define DL_CONCAT(head1,head2) \ DL_CONCAT2(head1,head2,prev,next) #define DL_CONCAT2(head1,head2,prev,next) \ do { \ LDECLTYPE(head1) _tmp; \ if (head2) { \ if (head1) { \ UTLIST_CASTASGN(_tmp, (head2)->prev); \ (head2)->prev = (head1)->prev; \ (head1)->prev->next = (head2); \ UTLIST_CASTASGN((head1)->prev, _tmp); \ } else { \ (head1)=(head2); \ } \ } \ } while (0) #define DL_DELETE(head,del) \ DL_DELETE2(head,del,prev,next) #define DL_DELETE2(head,del,prev,next) \ do { \ assert((head) != NULL); \ assert((del)->prev != NULL); \ if ((del)->prev == (del)) { \ (head)=NULL; \ } else if ((del)==(head)) { \ (del)->next->prev = (del)->prev; \ (head) = (del)->next; \ } else { \ (del)->prev->next = (del)->next; \ if ((del)->next) { \ (del)->next->prev = (del)->prev; \ } else { \ (head)->prev = (del)->prev; \ } \ } \ } while (0) #define DL_COUNT(head,el,counter) \ DL_COUNT2(head,el,counter,next) \ #define DL_COUNT2(head,el,counter,next) \ do { \ (counter) = 0; \ DL_FOREACH2(head,el,next) { ++(counter); } \ } while (0) #define DL_FOREACH(head,el) \ DL_FOREACH2(head,el,next) #define DL_FOREACH2(head,el,next) \ for ((el) = (head); el; (el) = (el)->next) /* this version is safe for deleting the elements during iteration */ #define DL_FOREACH_SAFE(head,el,tmp) \ DL_FOREACH_SAFE2(head,el,tmp,next) #define DL_FOREACH_SAFE2(head,el,tmp,next) \ for ((el) = (head); (el) && ((tmp) = (el)->next, 1); (el) = (tmp)) /* these are identical to their singly-linked list counterparts */ #define DL_SEARCH_SCALAR LL_SEARCH_SCALAR #define DL_SEARCH LL_SEARCH #define DL_SEARCH_SCALAR2 LL_SEARCH_SCALAR2 #define DL_SEARCH2 LL_SEARCH2 #define DL_REPLACE_ELEM2(head, el, add, prev, next) \ do { \ assert((head) != NULL); \ assert((el) != NULL); \ assert((add) != NULL); \ if ((head) == (el)) { \ (head) = (add); \ (add)->next = (el)->next; \ if ((el)->next == NULL) { \ (add)->prev = (add); \ } else { \ (add)->prev = (el)->prev; \ (add)->next->prev = (add); \ } \ } else { \ (add)->next = (el)->next; \ (add)->prev = (el)->prev; \ (add)->prev->next = (add); \ if ((el)->next == NULL) { \ (head)->prev = (add); \ } else { \ (add)->next->prev = (add); \ } \ } \ } while (0) #define DL_REPLACE_ELEM(head, el, add) \ DL_REPLACE_ELEM2(head, el, add, prev, next) #define DL_PREPEND_ELEM2(head, el, add, prev, next) \ do { \ if (el) { \ assert((head) != NULL); \ assert((add) != NULL); \ (add)->next = (el); \ (add)->prev = (el)->prev; \ (el)->prev = (add); \ if ((head) == (el)) { \ (head) = (add); \ } else { \ (add)->prev->next = (add); \ } \ } else { \ DL_APPEND2(head, add, prev, next); \ } \ } while (0) \ #define DL_PREPEND_ELEM(head, el, add) \ DL_PREPEND_ELEM2(head, el, add, prev, next) #define DL_APPEND_ELEM2(head, el, add, prev, next) \ do { \ if (el) { \ assert((head) != NULL); \ assert((add) != NULL); \ (add)->next = (el)->next; \ (add)->prev = (el); \ (el)->next = (add); \ if ((add)->next) { \ (add)->next->prev = (add); \ } else { \ (head)->prev = (add); \ } \ } else { \ DL_PREPEND2(head, add, prev, next); \ } \ } while (0) \ #define DL_APPEND_ELEM(head, el, add) \ DL_APPEND_ELEM2(head, el, add, prev, next) #ifdef NO_DECLTYPE /* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ #undef DL_INSERT_INORDER2 #define DL_INSERT_INORDER2(head,add,cmp,prev,next) \ do { \ if ((head) == NULL) { \ (add)->prev = (add); \ (add)->next = NULL; \ (head) = (add); \ } else if ((cmp(head, add)) >= 0) { \ (add)->prev = (head)->prev; \ (add)->next = (head); \ (head)->prev = (add); \ (head) = (add); \ } else { \ char *_tmp = (char*)(head); \ while ((head)->next && (cmp((head)->next, add)) < 0) { \ (head) = (head)->next; \ } \ (add)->prev = (head); \ (add)->next = (head)->next; \ (head)->next = (add); \ UTLIST_RS(head); \ if ((add)->next) { \ (add)->next->prev = (add); \ } else { \ (head)->prev = (add); \ } \ } \ } while (0) #endif /* NO_DECLTYPE */ /****************************************************************************** * circular doubly linked list macros * *****************************************************************************/ #define CDL_APPEND(head,add) \ CDL_APPEND2(head,add,prev,next) #define CDL_APPEND2(head,add,prev,next) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (add)->next = (head); \ (head)->prev = (add); \ (add)->prev->next = (add); \ } else { \ (add)->prev = (add); \ (add)->next = (add); \ (head) = (add); \ } \ } while (0) #define CDL_PREPEND(head,add) \ CDL_PREPEND2(head,add,prev,next) #define CDL_PREPEND2(head,add,prev,next) \ do { \ if (head) { \ (add)->prev = (head)->prev; \ (add)->next = (head); \ (head)->prev = (add); \ (add)->prev->next = (add); \ } else { \ (add)->prev = (add); \ (add)->next = (add); \ } \ (head) = (add); \ } while (0) #define CDL_INSERT_INORDER(head,add,cmp) \ CDL_INSERT_INORDER2(head,add,cmp,prev,next) #define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ do { \ LDECLTYPE(head) _tmp; \ if (head) { \ CDL_LOWER_BOUND2(head, _tmp, add, cmp, next); \ CDL_APPEND_ELEM2(head, _tmp, add, prev, next); \ } else { \ (head) = (add); \ (head)->next = (head); \ (head)->prev = (head); \ } \ } while (0) #define CDL_LOWER_BOUND(head,elt,like,cmp) \ CDL_LOWER_BOUND2(head,elt,like,cmp,next) #define CDL_LOWER_BOUND2(head,elt,like,cmp,next) \ do { \ if ((head) == NULL || (cmp(head, like)) >= 0) { \ (elt) = NULL; \ } else { \ for ((elt) = (head); (elt)->next != (head); (elt) = (elt)->next) { \ if ((cmp((elt)->next, like)) >= 0) { \ break; \ } \ } \ } \ } while (0) #define CDL_DELETE(head,del) \ CDL_DELETE2(head,del,prev,next) #define CDL_DELETE2(head,del,prev,next) \ do { \ if (((head)==(del)) && ((head)->next == (head))) { \ (head) = NULL; \ } else { \ (del)->next->prev = (del)->prev; \ (del)->prev->next = (del)->next; \ if ((del) == (head)) (head)=(del)->next; \ } \ } while (0) #define CDL_COUNT(head,el,counter) \ CDL_COUNT2(head,el,counter,next) \ #define CDL_COUNT2(head, el, counter,next) \ do { \ (counter) = 0; \ CDL_FOREACH2(head,el,next) { ++(counter); } \ } while (0) #define CDL_FOREACH(head,el) \ CDL_FOREACH2(head,el,next) #define CDL_FOREACH2(head,el,next) \ for ((el)=(head);el;(el)=(((el)->next==(head)) ? NULL : (el)->next)) #define CDL_FOREACH_SAFE(head,el,tmp1,tmp2) \ CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) #define CDL_FOREACH_SAFE2(head,el,tmp1,tmp2,prev,next) \ for ((el) = (head), (tmp1) = (head) ? (head)->prev : NULL; \ (el) && ((tmp2) = (el)->next, 1); \ (el) = ((el) == (tmp1) ? NULL : (tmp2))) #define CDL_SEARCH_SCALAR(head,out,field,val) \ CDL_SEARCH_SCALAR2(head,out,field,val,next) #define CDL_SEARCH_SCALAR2(head,out,field,val,next) \ do { \ CDL_FOREACH2(head,out,next) { \ if ((out)->field == (val)) break; \ } \ } while (0) #define CDL_SEARCH(head,out,elt,cmp) \ CDL_SEARCH2(head,out,elt,cmp,next) #define CDL_SEARCH2(head,out,elt,cmp,next) \ do { \ CDL_FOREACH2(head,out,next) { \ if ((cmp(out,elt))==0) break; \ } \ } while (0) #define CDL_REPLACE_ELEM2(head, el, add, prev, next) \ do { \ assert((head) != NULL); \ assert((el) != NULL); \ assert((add) != NULL); \ if ((el)->next == (el)) { \ (add)->next = (add); \ (add)->prev = (add); \ (head) = (add); \ } else { \ (add)->next = (el)->next; \ (add)->prev = (el)->prev; \ (add)->next->prev = (add); \ (add)->prev->next = (add); \ if ((head) == (el)) { \ (head) = (add); \ } \ } \ } while (0) #define CDL_REPLACE_ELEM(head, el, add) \ CDL_REPLACE_ELEM2(head, el, add, prev, next) #define CDL_PREPEND_ELEM2(head, el, add, prev, next) \ do { \ if (el) { \ assert((head) != NULL); \ assert((add) != NULL); \ (add)->next = (el); \ (add)->prev = (el)->prev; \ (el)->prev = (add); \ (add)->prev->next = (add); \ if ((head) == (el)) { \ (head) = (add); \ } \ } else { \ CDL_APPEND2(head, add, prev, next); \ } \ } while (0) #define CDL_PREPEND_ELEM(head, el, add) \ CDL_PREPEND_ELEM2(head, el, add, prev, next) #define CDL_APPEND_ELEM2(head, el, add, prev, next) \ do { \ if (el) { \ assert((head) != NULL); \ assert((add) != NULL); \ (add)->next = (el)->next; \ (add)->prev = (el); \ (el)->next = (add); \ (add)->next->prev = (add); \ } else { \ CDL_PREPEND2(head, add, prev, next); \ } \ } while (0) #define CDL_APPEND_ELEM(head, el, add) \ CDL_APPEND_ELEM2(head, el, add, prev, next) #ifdef NO_DECLTYPE /* Here are VS2008 / NO_DECLTYPE replacements for a few functions */ #undef CDL_INSERT_INORDER2 #define CDL_INSERT_INORDER2(head,add,cmp,prev,next) \ do { \ if ((head) == NULL) { \ (add)->prev = (add); \ (add)->next = (add); \ (head) = (add); \ } else if ((cmp(head, add)) >= 0) { \ (add)->prev = (head)->prev; \ (add)->next = (head); \ (add)->prev->next = (add); \ (head)->prev = (add); \ (head) = (add); \ } else { \ char *_tmp = (char*)(head); \ while ((char*)(head)->next != _tmp && (cmp((head)->next, add)) < 0) { \ (head) = (head)->next; \ } \ (add)->prev = (head); \ (add)->next = (head)->next; \ (add)->next->prev = (add); \ (head)->next = (add); \ UTLIST_RS(head); \ } \ } while (0) #endif /* NO_DECLTYPE */ #endif /* UTLIST_H */ mosquitto-2.0.18/doc/000077500000000000000000000000001450213760600144175ustar00rootroot00000000000000mosquitto-2.0.18/doc/historical/000077500000000000000000000000001450213760600165605ustar00rootroot00000000000000mosquitto-2.0.18/doc/historical/old-regex.txt000066400000000000000000000025551450213760600212160ustar00rootroot00000000000000This is the description of the regex used previously for topic/subscription matching. It is reproduced here for posterity. When a message is ready to be published at the broker, we need to check all of the subscriptions to see which ones the message should be sent to. This would be easy without wildcards, but requires a bit more work with them. The regex used to do the matching is of the form below for a topic of a/b/c: ^(?:(?:(a|\+)(?!$))(?:(?:/(?:(b|\+)(?!$)))(?:(?:/(?:c|\+))|/#)?|/#)?|#)$ In general, we're matching (a or +) followed by (the next levels of hierarchy or #). More specifically, all the levels of hierarchy must match, unless the last level is #. ^(?: # Must start at beginning of string (?: # (Level 1 hierarchy) (a|\+)(?!$) # Match a or +, but only if not EOL. ) # AND (?: (?: # (Level 2 hierarchy) / # Match / (?: # AND (b|\+)(?!$) # Match b or +, but only if not EOL. ) ) # AND (?: (?: # (Level 3 hierarchy) / # Match / (?: # AND c|\+ # Match c or +. ) ) | # OR (instead of level 3) /# # Match /# at level 3 )? # Level 3 exist 1/0 times | # OR (instead of level 2) /# # Match /# at level 2 )? # Level 2 exist 1/0 times | # OR (instead of level 1) # # Match # at level 1 )$ # Must end on EOL. mosquitto-2.0.18/doc/historical/topic-match.kds000066400000000000000000000002321450213760600214700ustar00rootroot00000000000000S'^(?:(?:(a|\\+)(?!$))(?:(?:/(?:(b|\\+)(?!$)))(?:(?:/(?:c|\\+))|/#)?|/#)?|#)$' p1 .S'a/#\na/b/c\na/b/+\na/b\na/+\n+\n+/b\n+/+/+\n+/b/c\na/c' p2 .I8 .S'' .mosquitto-2.0.18/doc/joss-paper/000077500000000000000000000000001450213760600165025ustar00rootroot00000000000000mosquitto-2.0.18/doc/joss-paper/codemeta.json000066400000000000000000000012441450213760600211570ustar00rootroot00000000000000{ "@context": "https://raw.githubusercontent.com/codemeta/codemeta/master/codemeta.jsonld", "@type": "Code", "author": [ { "@id": "http://orcid.org/0000-0001-9218-7797", "@type": "Person", "email": "", "name": "Roger A. Light", "affiliation": "" } ], "identifier": "", "codeRepository": "https://github.com/eclipse/mosquitto", "datePublished": "2017-05-17", "dateModified": "2017-05-17", "dateCreated": "2017-05-17", "description": "Broker and client implementation of the MQTT protocol.", "keywords": "IoT, MQTT, messaging, pubsub", "license": "EPL 2.0 / EDL 2.0", "title": "Mosquitto", "version": "v1.4.11" }mosquitto-2.0.18/doc/joss-paper/paper.bib000066400000000000000000000175141450213760600202770ustar00rootroot00000000000000@inproceedings{Schulz_2014, title = {Real-time animation of equipment in a remote laboratory}, doi = {10.1109/REV.2014.6784247}, booktitle = {2014 11th {International} {Conference} on {Remote} {Engineering} and {Virtual} {Instrumentation} ({REV})}, author = {Schulz, M. and Chen, F. and Payne, L.}, month = feb, year = {2014}, keywords = {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}, pages = {172--176} } @inproceedings{Antonic_2015, title = {Comparison of the {CUPUS} middleware and {MQTT} protocol for smart city services}, doi = {10.1109/ConTEL.2015.7231225}, booktitle = {2015 13th {International} {Conference} on {Telecommunications} ({Con}TEL)}, author = {Antonić, A. and Marjanović, M. and Skočir, P. and Žarko, I. P.}, month = jul, year = {2015}, keywords = {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}, pages = {1--8} } @inproceedings{Thangavel_2014, title = {Performance evaluation of {MQTT} and {CoAP} via a common middleware}, doi = {10.1109/ISSNIP.2014.6827678}, booktitle = {2014 {IEEE} {Ninth} {International} {Conference} on {Intelligent} {Sensors}, {Sensor} {Networks} and {Information} {Processing} ({ISSNIP})}, author = {Thangavel, D. and Ma, X. and Valera, A. and Tan, H. X. and Tan, C. K. Y.}, month = apr, year = {2014}, keywords = {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}, pages = {1--6} } @inproceedings{Kang_2017, title = {Room {Temperature} {Control} and {Fire} {Alarm} \#x002F;{Suppression} {IoT} {Service} {Using} {MQTT} on {AWS}}, doi = {10.1109/PlatCon.2017.7883724}, abstract = {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.}, booktitle = {2017 {International} {Conference} on {Platform} {Technology} and {Service} ({PlatCon})}, author = {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.}, month = feb, year = {2017}, note = {00000}, keywords = {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}, pages = {1--5} } @inproceedings{Fremantle_2014, title = {Federated {Identity} and {Access} {Management} for the {Internet} of {Things}}, doi = {10.1109/SIoT.2014.8}, abstract = {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.}, booktitle = {2014 {International} {Workshop} on {Secure} {Internet} of {Things}}, author = {Fremantle, P. and Aziz, B. and Kopecký, J. and Scott, P.}, month = sep, year = {2014}, note = {00026}, keywords = {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}, pages = {10--17} } @article{Bellavista_2017, title = {The {PeRvasive} {Environment} {Sensing} and {Sharing} {Solution}}, volume = {9}, copyright = {http://creativecommons.org/licenses/by/3.0/}, url = {http://www.mdpi.com/2071-1050/9/4/585}, doi = {10.3390/su9040585}, abstract = {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.}, language = {en}, number = {4}, urldate = {2017-05-17}, journal = {sustainability}, author = {Bellavista, Paolo and Giannelli, Carlo and Zamagna, Riccardo}, month = apr, year = {2017}, note = {00000}, keywords = {dynamic extensibility, environmental monitoring, heterogeneous monitoring data, mqtt, scalability, traffic congestion management, websockets}, pages = {585} } mosquitto-2.0.18/doc/joss-paper/paper.md000066400000000000000000000037021450213760600201350ustar00rootroot00000000000000--- title: 'Mosquitto: server and client implementation of the MQTT protocol' tags: - Internet of Things - MQTT - Pubsub - Messaging authors: - name: Roger A Light orcid: 0000-0001-9218-7797 date: 17 May 2017 bibliography: paper.bib --- # Summary Mosquitto provides standards compliant server and client implementations of the [MQTT](http://mqtt.org/) messaging protocol. MQTT uses a publish/subscribe model, has low network overhead and can be implemented on low power devices such microcontrollers that might be used in remote Internet of Things sensors. As such, Mosquitto is intended for use in all situations where there is a need for lightweight messaging, particularly on constrained devices with limited resources. The Mosquitto project is a member of the [Eclipse Foundation](http://eclipse.org/) There are three parts to the project. * The main `mosquitto` server * The `mosquitto_pub` and `mosquitto_sub` client utilities that are one method of communicating with an MQTT server * An MQTT client library written in C, with a C++ wrapper Mosquitto allows research directly related to the MQTT protocol itself, such as comparing the performance of MQTT and the Constrained Application Protocol (CoAP) [@Thangavel_2014] or investigating the use of OAuth in MQTT [@Fremantle_2014]. Mosquitto supports other research activities as a useful block for building larger systems and has been used to evaluate MQTT for use in Smart City Services [@Antonic_2015], and in the development of an environmental monitoring system [@Bellavista_2017]. Mosquitto has also been used to support research less directly as part of a scheme for remote control of an experiment [@Schulz_2014]. Outside of academia, Mosquitto is used in other open source projects such as the [openHAB](http://www.openhab.org/) home automation project and [OwnTracks](http://owntracks.org/), the personal location tracking project, and has been integrated into commercial products. # References mosquitto-2.0.18/docker/000077500000000000000000000000001450213760600151215ustar00rootroot00000000000000mosquitto-2.0.18/docker/1.5-openssl/000077500000000000000000000000001450213760600171055ustar00rootroot00000000000000mosquitto-2.0.18/docker/1.5-openssl/Dockerfile000066400000000000000000000074651450213760600211130ustar00rootroot00000000000000FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ENV VERSION=1.5.11 \ DOWNLOAD_SHA256=4a3b8a8f5505d27a7a966dd68bfd76f1e69feb51796d1b46b7271d1bb5a1a299 \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ gnupg \ linux-headers \ openssl-dev \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \ echo "$DOWNLOAD_SHA256 /tmp/mosq.tar.gz" | sha256sum -c - && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \ export GNUPGHOME="$(mktemp -d)" && \ found=''; \ for server in \ hkps://keys.openpgp.org \ hkp://keyserver.ubuntu.com:80 \ pgp.mit.edu \ ; do \ echo "Fetching GPG key $GPG_KEYS from $server"; \ gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ done; \ test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/mosq.tar.gz.asc && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include -flto" \ LDFLAGS="-L/build/lws/lib -flto" \ WITH_ADNS=no \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/src/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates libuuid && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/1.5-openssl/README.md000066400000000000000000000030061450213760600203630ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build as source from published tarballs. ## Mount Points A docker mount point has been created in the image to be used for configuration. ``` /mosquitto/config ``` Two docker volumes have been created in the image to be used for persistent storage and logs. ``` /mosquitto/data /mosquitto/log ``` ## User/Group The image runs mosquitto under the mosquitto user and group, which are created with a uid and gid of 1883. ## Configuration When creating a container from the image, the default configuration values are used. To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured, for example: ``` docker run -it -p 1883:1883 -p 8080:8080 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. mosquitto-2.0.18/docker/1.5-openssl/docker-entrypoint.sh000077500000000000000000000002461450213760600231260ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/docker/1.5/000077500000000000000000000000001450213760600154245ustar00rootroot00000000000000mosquitto-2.0.18/docker/1.5/Dockerfile000066400000000000000000000074211450213760600174220ustar00rootroot00000000000000FROM alpine:3.14 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ENV VERSION=1.5.11 \ DOWNLOAD_SHA256=4a3b8a8f5505d27a7a966dd68bfd76f1e69feb51796d1b46b7271d1bb5a1a299 \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=2.4.2 \ LWS_SHA256=73012d7fcf428dedccc816e83a63a01462e27819d5537b8e0d0c7264bfacfad6 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ gnupg \ libressl-dev \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \ echo "$DOWNLOAD_SHA256 /tmp/mosq.tar.gz" | sha256sum -c - && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \ export GNUPGHOME="$(mktemp -d)" && \ found=''; \ for server in \ hkps://keys.openpgp.org \ hkp://keyserver.ubuntu.com:80 \ pgp.mit.edu \ ; do \ echo "Fetching GPG key $GPG_KEYS from $server"; \ gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ done; \ test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/mosq.tar.gz.asc && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include -flto" \ LDFLAGS="-L/build/lws/lib -flto" \ WITH_ADNS=no \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_TLS_PSK=no \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/src/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates \ libressl \ libuuid && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/1.5/README.md000066400000000000000000000030061450213760600167020ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build as source from published tarballs. ## Mount Points A docker mount point has been created in the image to be used for configuration. ``` /mosquitto/config ``` Two docker volumes have been created in the image to be used for persistent storage and logs. ``` /mosquitto/data /mosquitto/log ``` ## User/Group The image runs mosquitto under the mosquitto user and group, which are created with a uid and gid of 1883. ## Configuration When creating a container from the image, the default configuration values are used. To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured, for example: ``` docker run -it -p 1883:1883 -p 8080:8080 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. mosquitto-2.0.18/docker/1.5/docker-entrypoint.sh000077500000000000000000000002461450213760600214450ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/docker/1.6-openssl/000077500000000000000000000000001450213760600171065ustar00rootroot00000000000000mosquitto-2.0.18/docker/1.6-openssl/Dockerfile000066400000000000000000000101521450213760600210770ustar00rootroot00000000000000FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ENV VERSION=1.6.15 \ DOWNLOAD_SHA256=5ff2271512f745bf1a451072cd3768a5daed71e90c5179fae12b049d6c02aa0f \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ gnupg \ linux-headers \ openssl-dev \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \ echo "$DOWNLOAD_SHA256 /tmp/mosq.tar.gz" | sha256sum -c - && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \ export GNUPGHOME="$(mktemp -d)" && \ found=''; \ for server in \ hkps://keys.openpgp.org \ hkp://keyserver.ubuntu.com:80 \ pgp.mit.edu \ ; do \ echo "Fetching GPG key $GPG_KEYS from $server"; \ gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ done; \ test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/mosq.tar.gz.asc && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include" \ LDFLAGS="-L/build/lws/lib" \ WITH_ADNS=no \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/src/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ install -Dm644 /build/lws/LICENSE /usr/share/licenses/libwebsockets/LICENSE && \ install -Dm644 /build/mosq/epl-v10 /usr/share/licenses/mosquitto/epl-v10 && \ install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/1.6-openssl/README.md000066400000000000000000000030061450213760600203640ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build as source from published tarballs. ## Mount Points A docker mount point has been created in the image to be used for configuration. ``` /mosquitto/config ``` Two docker volumes have been created in the image to be used for persistent storage and logs. ``` /mosquitto/data /mosquitto/log ``` ## User/Group The image runs mosquitto under the mosquitto user and group, which are created with a uid and gid of 1883. ## Configuration When creating a container from the image, the default configuration values are used. To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured, for example: ``` docker run -it -p 1883:1883 -p 8080:8080 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. mosquitto-2.0.18/docker/1.6-openssl/docker-entrypoint.sh000077500000000000000000000002461450213760600231270ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/docker/1.6/000077500000000000000000000000001450213760600154255ustar00rootroot00000000000000mosquitto-2.0.18/docker/1.6/Dockerfile000066400000000000000000000102301450213760600174130ustar00rootroot00000000000000FROM alpine:3.14 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ENV VERSION=1.6.15 \ DOWNLOAD_SHA256=5ff2271512f745bf1a451072cd3768a5daed71e90c5179fae12b049d6c02aa0f \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ gnupg \ libressl-dev \ linux-headers \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \ echo "$DOWNLOAD_SHA256 /tmp/mosq.tar.gz" | sha256sum -c - && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \ export GNUPGHOME="$(mktemp -d)" && \ found=''; \ for server in \ hkps://keys.openpgp.org \ hkp://keyserver.ubuntu.com:80 \ pgp.mit.edu \ ; do \ echo "Fetching GPG key $GPG_KEYS from $server"; \ gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ done; \ test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/mosq.tar.gz.asc && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include" \ LDFLAGS="-L/build/lws/lib" \ WITH_ADNS=no \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_TLS_PSK=no \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/src/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ install -Dm644 /build/lws/LICENSE /usr/share/licenses/libwebsockets/LICENSE && \ install -Dm644 /build/mosq/epl-v10 /usr/share/licenses/mosquitto/epl-v10 && \ install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates \ libressl && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/1.6/README.md000066400000000000000000000030061450213760600167030ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build as source from published tarballs. ## Mount Points A docker mount point has been created in the image to be used for configuration. ``` /mosquitto/config ``` Two docker volumes have been created in the image to be used for persistent storage and logs. ``` /mosquitto/data /mosquitto/log ``` ## User/Group The image runs mosquitto under the mosquitto user and group, which are created with a uid and gid of 1883. ## Configuration When creating a container from the image, the default configuration values are used. To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured, for example: ``` docker run -it -p 1883:1883 -p 8080:8080 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. mosquitto-2.0.18/docker/1.6/docker-entrypoint.sh000077500000000000000000000002461450213760600214460ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/docker/2.0-openssl/000077500000000000000000000000001450213760600171015ustar00rootroot00000000000000mosquitto-2.0.18/docker/2.0-openssl/Dockerfile000066400000000000000000000106451450213760600211010ustar00rootroot00000000000000FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ENV VERSION=2.0.17 \ DOWNLOAD_SHA256=3be7a911236567c1a9fbe25baf3e3167004ba4a0c151a448ef1f7fc077dba52f \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ cjson-dev \ gnupg \ linux-headers \ openssl-dev \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \ echo "$DOWNLOAD_SHA256 /tmp/mosq.tar.gz" | sha256sum -c - && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \ export GNUPGHOME="$(mktemp -d)" && \ found=''; \ for server in \ hkps://keys.openpgp.org \ hkp://keyserver.ubuntu.com:80 \ pgp.mit.edu \ ; do \ echo "Fetching GPG key $GPG_KEYS from $server"; \ gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ done; \ test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/mosq.tar.gz.asc && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include -I/build" \ LDFLAGS="-L/build/lws/lib" \ WITH_ADNS=no \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \ install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ install -Dm644 /build/lws/LICENSE /usr/share/licenses/libwebsockets/LICENSE && \ install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \ install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates \ cjson && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh mosquitto-no-auth.conf / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/2.0-openssl/README.md000066400000000000000000000051541450213760600203650ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build as source from published tarballs. ## Mount Points A docker mount point has been created in the image to be used for configuration. ``` /mosquitto/config ``` Two docker volumes have been created in the image to be used for persistent storage and logs. ``` /mosquitto/data /mosquitto/log ``` ## User/Group The image runs mosquitto under the mosquitto user and group, which are created with a uid and gid of 1883. ## Running without a configuration file Mosquitto 2.0 requires you to configure listeners and authentication before it will allow connections from anything other than the loopback interface. In the context of a container, this means you would normally need to provide a configuration file with your settings. If you wish to run mosquitto without any authentication, and without setting any other configuration options, you can do so by using a configuration provided in the container for this purpose: ``` docker run -it -p 1883:1883 eclipse-mosquitto: mosquitto -c /mosquitto-no-auth.conf ``` ## Configuration To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Your configuration file must include a `listener`, and you must configure some form of authentication or allow unauthenticated access. If you do not do this, clients will be unable to connect. File based authentication and authorisation: ``` listener 1883 password_file /mosquitto/data/mosquitto.password_file acl_file /mosquitto/data/mosquitto.aclfile ``` Plugin based authentication and authorisation: ``` listener 1883 plugin /usr/lib/mosquitto_dynamic_security.so plugin_opt_config_file /mosquitto/data/mosquitto-dynsec.json ``` Unauthenticated access: ``` listener 1883 allow_anonymous true ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured, for example: ``` docker run -it -p 1883:1883 -p 8080:8080 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. mosquitto-2.0.18/docker/2.0-openssl/docker-entrypoint.sh000077500000000000000000000002461450213760600231220ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/docker/2.0-openssl/mosquitto-no-auth.conf000066400000000000000000000002301450213760600233600ustar00rootroot00000000000000# This is a Mosquitto configuration file that creates a listener on port 1883 # that allows unauthenticated access. listener 1883 allow_anonymous true mosquitto-2.0.18/docker/2.0/000077500000000000000000000000001450213760600154205ustar00rootroot00000000000000mosquitto-2.0.18/docker/2.0/Dockerfile000066400000000000000000000107231450213760600174150ustar00rootroot00000000000000FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ENV VERSION=2.0.17 \ DOWNLOAD_SHA256=3be7a911236567c1a9fbe25baf3e3167004ba4a0c151a448ef1f7fc077dba52f \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ cjson-dev \ gnupg \ libressl-dev \ linux-headers \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \ echo "$DOWNLOAD_SHA256 /tmp/mosq.tar.gz" | sha256sum -c - && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \ export GNUPGHOME="$(mktemp -d)" && \ found=''; \ for server in \ hkps://keys.openpgp.org \ hkp://keyserver.ubuntu.com:80 \ pgp.mit.edu \ ; do \ echo "Fetching GPG key $GPG_KEYS from $server"; \ gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ done; \ test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/mosq.tar.gz.asc && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include -I/build" \ LDFLAGS="-L/build/lws/lib" \ WITH_ADNS=no \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_TLS_PSK=no \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \ install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ install -Dm644 /build/lws/LICENSE /usr/share/licenses/libwebsockets/LICENSE && \ install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \ install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates \ cjson \ libressl && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh mosquitto-no-auth.conf / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/2.0/README.md000066400000000000000000000051541450213760600167040ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build as source from published tarballs. ## Mount Points A docker mount point has been created in the image to be used for configuration. ``` /mosquitto/config ``` Two docker volumes have been created in the image to be used for persistent storage and logs. ``` /mosquitto/data /mosquitto/log ``` ## User/Group The image runs mosquitto under the mosquitto user and group, which are created with a uid and gid of 1883. ## Running without a configuration file Mosquitto 2.0 requires you to configure listeners and authentication before it will allow connections from anything other than the loopback interface. In the context of a container, this means you would normally need to provide a configuration file with your settings. If you wish to run mosquitto without any authentication, and without setting any other configuration options, you can do so by using a configuration provided in the container for this purpose: ``` docker run -it -p 1883:1883 eclipse-mosquitto: mosquitto -c /mosquitto-no-auth.conf ``` ## Configuration To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Your configuration file must include a `listener`, and you must configure some form of authentication or allow unauthenticated access. If you do not do this, clients will be unable to connect. File based authentication and authorisation: ``` listener 1883 password_file /mosquitto/data/mosquitto.password_file acl_file /mosquitto/data/mosquitto.aclfile ``` Plugin based authentication and authorisation: ``` listener 1883 plugin /usr/lib/mosquitto_dynamic_security.so plugin_opt_config_file /mosquitto/data/mosquitto-dynsec.json ``` Unauthenticated access: ``` listener 1883 allow_anonymous true ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured, for example: ``` docker run -it -p 1883:1883 -p 8080:8080 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. mosquitto-2.0.18/docker/2.0/docker-entrypoint.sh000077500000000000000000000002461450213760600214410ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/docker/2.0/mosquitto-no-auth.conf000066400000000000000000000002301450213760600216770ustar00rootroot00000000000000# This is a Mosquitto configuration file that creates a listener on port 1883 # that allows unauthenticated access. listener 1883 allow_anonymous true mosquitto-2.0.18/docker/README.md000066400000000000000000000022431450213760600164010ustar00rootroot00000000000000# Docker Images This directory contains Docker files for Mosquitto. The `2.0` directory contains the latest version of Mosquitto for that series, it uses libressl. The `2.0-openssl` directory is identical except that it uses openssl instead of libressl, and enables TLS-PSK and TLS v1.3 cipher support. The `1.6` directory contains the version of Mosquitto based on the 1.6 branch. It uses libressl. The `1.6-openssl` directory is identical except that it uses openssl instead of libressl, and enables TLS-PSK support. The `1.5` directory contains the version of Mosquitto based on the 1.5 branch. It uses libressl. The `1.5-openssl` directory is identical except that it uses openssl instead of libressl, and enables TLS-PSK support. The `generic` directory contains a generic Dockerfile that can be used to build arbitrary versions of Mosquitto based on the released tarballs as follows: ``` cd generic docker build -t eclipse-mosquitto:1.5.1 --build-arg VERSION="1.5.1" . docker run --rm -it eclipse-mosquitto:1.5.1 ``` The `local` directory can be used to build an image based on the files in the working directory by using `make localdocker` from the root of the repository. mosquitto-2.0.18/docker/generic/000077500000000000000000000000001450213760600165355ustar00rootroot00000000000000mosquitto-2.0.18/docker/generic/Dockerfile000066400000000000000000000121411450213760600205260ustar00rootroot00000000000000FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ARG VERSION RUN test -n "${VERSION}" ENV \ GPG_KEYS=A0D6EEA1DCAE49A635A3B2F0779B22DFB3E717B7 \ LWS_VERSION=4.2.1 \ LWS_SHA256=842da21f73ccba2be59e680de10a8cce7928313048750eb6ad73b6fa50763c51 \ CJSON_VERSION=1.7.14 \ CJSON_SHA256=fb50a663eefdc76bafa80c82bc045af13b1363e8f45cec8b442007aef6a41343 RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ gnupg \ linux-headers \ openssl-dev \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ echo "$LWS_SHA256 /tmp/lws.tar.gz" | sha256sum -c - && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://github.com/DaveGamble/cJSON/archive/v${CJSON_VERSION}.tar.gz -O /tmp/cjson.tar.gz && \ echo "$CJSON_SHA256 /tmp/cjson.tar.gz" | sha256sum -c - && \ mkdir -p /build/cjson && \ tar --strip=1 -xf /tmp/cjson.tar.gz -C /build/cjson && \ rm /tmp/cjson.tar.gz && \ cd /build/cjson && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DBUILD_SHARED_AND_STATIC_LIBS=OFF \ -DBUILD_SHARED_LIBS=OFF \ -DCJSON_BUILD_SHARED_LIBS=OFF \ -DCJSON_OVERRIDE_BUILD_SHARED_LIBS=OFF \ -DCMAKE_INSTALL_PREFIX=/usr && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz -O /tmp/mosq.tar.gz && \ wget https://mosquitto.org/files/source/mosquitto-${VERSION}.tar.gz.asc -O /tmp/mosq.tar.gz.asc && \ export GNUPGHOME="$(mktemp -d)" && \ found=''; \ for server in \ hkps://keys.openpgp.org \ hkp://keyserver.ubuntu.com:80 \ pgp.mit.edu \ ; do \ echo "Fetching GPG key $GPG_KEYS from $server"; \ gpg --keyserver "$server" --keyserver-options timeout=10 --recv-keys "$GPG_KEYS" && found=yes && break; \ done; \ test -z "$found" && echo >&2 "error: failed to fetch GPG key $GPG_KEYS" && exit 1; \ gpg --batch --verify /tmp/mosq.tar.gz.asc /tmp/mosq.tar.gz && \ gpgconf --kill all && \ rm -rf "$GNUPGHOME" /tmp/mosq.tar.gz.asc && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include -I/build" \ LDFLAGS="-L/build/lws/lib -L/build/cjson" \ WITH_ADNS=no \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_TLS_PSK=no \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \ install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ install -Dm644 /build/cjson/LICENSE /usr/share/licenses/cJSON/LICENSE && \ install -Dm644 /build/lws/LICENSE /usr/share/licenses/libwebsockets/LICENSE && \ install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \ install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh mosquitto-no-auth.conf / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/generic/README.md000066400000000000000000000041431450213760600200160ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build as source from published tarballs. ## Mount Points Three docker volumes have been created in the image to be used for configuration, persistent storage and logs. ``` /mosquitto/config /mosquitto/data /mosquitto/log ``` ## Running without a configuration file Mosquitto 2.0 requires you to configure listeners and authentication before it will allow connections from anything other than the loopback interface. In the context of a container, this means you would normally need to provide a configuration file with your settings. If you wish to run mosquitto without any authentication, and without setting any other configuration options, you can do so by using a configuration provided in the container for this purpose: ``` docker run -it -p 1883:1883 eclipse-mosquitto: mosquitto -c /mosquitto-no-auth.conf ``` ## Configuration When creating a container from the image, the default configuration values are used. To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. ## Build Build and tag the docker image for a specific version: ``` docker build -t eclipse-mosquitto: --build-arg VERSION="" . ``` ## Run Run a container using the new image: ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf -v /mosquitto/data -v /mosquitto/log eclipse-mosquitto: ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured. mosquitto-2.0.18/docker/generic/docker-entrypoint.sh000077500000000000000000000002461450213760600225560ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/docker/generic/mosquitto-no-auth.conf000066400000000000000000000002301450213760600230140ustar00rootroot00000000000000# This is a Mosquitto configuration file that creates a listener on port 1883 # that allows unauthenticated access. listener 1883 allow_anonymous true mosquitto-2.0.18/docker/local/000077500000000000000000000000001450213760600162135ustar00rootroot00000000000000mosquitto-2.0.18/docker/local/Dockerfile000066400000000000000000000072301450213760600202070ustar00rootroot00000000000000FROM alpine:3.18 LABEL maintainer="Roger Light " \ description="Eclipse Mosquitto MQTT Broker" ENV LWS_VERSION=4.2.1 \ CJSON_VERSION=1.7.14 COPY mosq.tar.gz /tmp RUN set -x && \ apk --no-cache add --virtual build-deps \ build-base \ cmake \ gnupg \ linux-headers \ openssl-dev \ util-linux-dev && \ wget https://github.com/warmcat/libwebsockets/archive/v${LWS_VERSION}.tar.gz -O /tmp/lws.tar.gz && \ mkdir -p /build/lws && \ tar --strip=1 -xf /tmp/lws.tar.gz -C /build/lws && \ rm /tmp/lws.tar.gz && \ cd /build/lws && \ cmake . \ -DCMAKE_BUILD_TYPE=MinSizeRel \ -DCMAKE_INSTALL_PREFIX=/usr \ -DDISABLE_WERROR=ON \ -DLWS_IPV6=ON \ -DLWS_WITHOUT_BUILTIN_GETIFADDRS=ON \ -DLWS_WITHOUT_CLIENT=ON \ -DLWS_WITHOUT_EXTENSIONS=ON \ -DLWS_WITHOUT_TESTAPPS=ON \ -DLWS_WITH_EXTERNAL_POLL=ON \ -DLWS_WITH_HTTP2=OFF \ -DLWS_WITH_SHARED=OFF \ -DLWS_WITH_ZIP_FOPS=OFF \ -DLWS_WITH_ZLIB=OFF && \ make -j "$(nproc)" && \ rm -rf /root/.cmake && \ wget https://github.com/DaveGamble/cJSON/archive/v${CJSON_VERSION}.tar.gz -O /tmp/cjson.tar.gz && \ mkdir -p /build/cjson && \ tar --strip=1 -xf /tmp/cjson.tar.gz -C /build/cjson && \ rm /tmp/cjson.tar.gz && \ cd /build/cjson && \ make -j "$(nproc)" libcjson.a && \ mkdir -p /build/mosq && \ tar --strip=1 -xf /tmp/mosq.tar.gz -C /build/mosq && \ rm /tmp/mosq.tar.gz && \ make -C /build/mosq -j "$(nproc)" \ CFLAGS="-Wall -O2 -I/build/lws/include -I/build" \ LDFLAGS="-L/build/lws/lib -L/build/cjson" \ WITH_ADNS=no \ WITH_CJSON=yes \ WITH_DOCS=no \ WITH_SHARED_LIBRARIES=yes \ WITH_SRV=no \ WITH_STRIP=yes \ WITH_WEBSOCKETS=yes \ prefix=/usr \ binary && \ addgroup -S -g 1883 mosquitto 2>/dev/null && \ adduser -S -u 1883 -D -H -h /var/empty -s /sbin/nologin -G mosquitto -g mosquitto mosquitto 2>/dev/null && \ mkdir -p /mosquitto/config /mosquitto/data /mosquitto/log && \ install -d /usr/sbin/ && \ install -s -m755 /build/mosq/client/mosquitto_pub /usr/bin/mosquitto_pub && \ install -s -m755 /build/mosq/client/mosquitto_rr /usr/bin/mosquitto_rr && \ install -s -m755 /build/mosq/client/mosquitto_sub /usr/bin/mosquitto_sub && \ install -s -m644 /build/mosq/lib/libmosquitto.so.1 /usr/lib/libmosquitto.so.1 && \ install -s -m755 /build/mosq/src/mosquitto /usr/sbin/mosquitto && \ install -s -m755 /build/mosq/apps/mosquitto_passwd/mosquitto_passwd /usr/bin/mosquitto_passwd && \ install -s -m755 /build/mosq/apps/mosquitto_ctrl/mosquitto_ctrl /usr/bin/mosquitto_ctrl && \ install -s -m755 /build/mosq/plugins/dynamic-security/mosquitto_dynamic_security.so /usr/lib/mosquitto_dynamic_security.so && \ install -m644 /build/mosq/mosquitto.conf /mosquitto/config/mosquitto.conf && \ install -Dm644 /build/cjson/LICENSE /usr/share/licenses/cJSON/LICENSE && \ install -Dm644 /build/lws/LICENSE /usr/share/licenses/libwebsockets/LICENSE && \ install -Dm644 /build/mosq/epl-v20 /usr/share/licenses/mosquitto/epl-v20 && \ install -Dm644 /build/mosq/edl-v10 /usr/share/licenses/mosquitto/edl-v10 && \ chown -R mosquitto:mosquitto /mosquitto && \ apk --no-cache add \ ca-certificates && \ apk del build-deps && \ rm -rf /build VOLUME ["/mosquitto/data", "/mosquitto/log"] # Set up the entry point script and default command COPY docker-entrypoint.sh / EXPOSE 1883 ENTRYPOINT ["/docker-entrypoint.sh"] CMD ["/usr/sbin/mosquitto", "-c", "/mosquitto/config/mosquitto.conf"] mosquitto-2.0.18/docker/local/README.md000066400000000000000000000033231450213760600174730ustar00rootroot00000000000000# Eclipse Mosquitto Docker Image Containers built with this Dockerfile build from a source tarball "mosq.tar.gz" placed in the local directory. Running `make localdocker` from the root of the repository will generate the source tar and build the docker image. ## Mount Points A docker mount point has been created in the image to be used for configuration. ``` /mosquitto/config ``` Two docker volumes have been created in the image to be used for persistent storage and logs. ``` /mosquitto/data /mosquitto/log ``` ## User/Group The image runs mosquitto under the mosquitto user and group, which are created with a uid and gid of 1883. ## Configuration When creating a container from the image, the default configuration values are used. To use a custom configuration file, mount a **local** configuration file to `/mosquitto/config/mosquitto.conf` ``` docker run -it -p 1883:1883 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` :boom: if the mosquitto configuration (mosquitto.conf) was modified to use non-default ports, the docker run command will need to be updated to expose the ports that have been configured, for example if you use port 8080 for websockets as well as port 1883: ``` docker run -it -p 1883:1883 -p 8080:8080 -v :/mosquitto/config/mosquitto.conf eclipse-mosquitto: ``` Configuration can be changed to: * persist data to `/mosquitto/data` * log to `/mosquitto/log/mosquitto.log` i.e. add the following to `mosquitto.conf`: ``` persistence true persistence_location /mosquitto/data/ log_dest file /mosquitto/log/mosquitto.log ``` **Note**: For any volume used, the data will be persistent between containers. mosquitto-2.0.18/docker/local/docker-entrypoint.sh000077500000000000000000000002461450213760600222340ustar00rootroot00000000000000#!/bin/ash set -e # Set permissions user="$(id -u)" if [ "$user" = '0' ]; then [ -d "/mosquitto" ] && chown -R mosquitto:mosquitto /mosquitto || true fi exec "$@" mosquitto-2.0.18/edl-v10000066400000000000000000000030401450213760600147420ustar00rootroot00000000000000Eclipse Distribution License - v 1.0 Copyright (c) 2007, Eclipse Foundation, Inc. and its licensors. All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the Eclipse Foundation, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. mosquitto-2.0.18/epl-v20000066400000000000000000000335651450213760600147760ustar00rootroot00000000000000Eclipse Public License - v 2.0 THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT. 1. DEFINITIONS "Contribution" means: a) in the case of the initial Contributor, the initial content Distributed under this Agreement, and b) in the case of each subsequent Contributor: i) changes to the Program, and ii) additions to the Program; where such changes and/or additions to the Program originate from and are Distributed by that particular Contributor. A Contribution "originates" from a Contributor if it was added to the Program by such Contributor itself or anyone acting on such Contributor's behalf. Contributions do not include changes or additions to the Program that are not Modified Works. "Contributor" means any person or entity that Distributes the Program. "Licensed Patents" mean patent claims licensable by a Contributor which are necessarily infringed by the use or sale of its Contribution alone or when combined with the Program. "Program" means the Contributions Distributed in accordance with this Agreement. "Recipient" means anyone who receives the Program under this Agreement or any Secondary License (as applicable), including Contributors. "Derivative Works" shall mean any work, whether in Source Code or other form, that is based on (or derived from) the Program and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. "Modified Works" shall mean any work in Source Code or other form that results from an addition to, deletion from, or modification of the contents of the Program, including, for purposes of clarity any new file in Source Code form that contains any contents of the Program. Modified Works shall not include works that contain only declarations, interfaces, types, classes, structures, or files of the Program solely in each case in order to link to, bind by name, or subclass the Program or Modified Works thereof. "Distribute" means the acts of a) distributing or b) making available in any manner that enables the transfer of a copy. "Source Code" means the form of a Program preferred for making modifications, including but not limited to software source code, documentation source, and configuration files. "Secondary License" means either the GNU General Public License, Version 2.0, or any later versions of that license, including any exceptions or additional permissions as identified by the initial Contributor. 2. GRANT OF RIGHTS a) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, Distribute and sublicense the Contribution of such Contributor, if any, and such Derivative Works. b) Subject to the terms of this Agreement, each Contributor hereby grants Recipient a non-exclusive, worldwide, royalty-free patent license under Licensed Patents to make, use, sell, offer to sell, import and otherwise transfer the Contribution of such Contributor, if any, in Source Code or other form. This patent license shall apply to the combination of the Contribution and the Program if, at the time the Contribution is added by the Contributor, such addition of the Contribution causes such combination to be covered by the Licensed Patents. The patent license shall not apply to any other combinations which include the Contribution. No hardware per se is licensed hereunder. c) Recipient understands that although each Contributor grants the licenses to its Contributions set forth herein, no assurances are provided by any Contributor that the Program does not infringe the patent or other intellectual property rights of any other entity. Each Contributor disclaims any liability to Recipient for claims brought by any other entity based on infringement of intellectual property rights or otherwise. As a condition to exercising the rights and licenses granted hereunder, each Recipient hereby assumes sole responsibility to secure any other intellectual property rights needed, if any. For example, if a third party patent license is required to allow Recipient to Distribute the Program, it is Recipient's responsibility to acquire that license before distributing the Program. d) Each Contributor represents that to its knowledge it has sufficient copyright rights in its Contribution, if any, to grant the copyright license set forth in this Agreement. e) Notwithstanding the terms of any Secondary License, no Contributor makes additional grants to any Recipient (other than those set forth in this Agreement) as a result of such Recipient's receipt of the Program under the terms of a Secondary License (if permitted under the terms of Section 3). 3. REQUIREMENTS 3.1 If a Contributor Distributes the Program in any form, then: a) the Program must also be made available as Source Code, in accordance with section 3.2, and the Contributor must accompany the Program with a statement that the Source Code for the Program is available under this Agreement, and informs Recipients how to obtain it in a reasonable manner on or through a medium customarily used for software exchange; and b) the Contributor may Distribute the Program under a license different than this Agreement, provided that such license: i) effectively disclaims on behalf of all other Contributors all warranties and conditions, express and implied, including warranties or conditions of title and non-infringement, and implied warranties or conditions of merchantability and fitness for a particular purpose; ii) effectively excludes on behalf of all other Contributors all liability for damages, including direct, indirect, special, incidental and consequential damages, such as lost profits; iii) does not attempt to limit or alter the recipients' rights in the Source Code under section 3.2; and iv) requires any subsequent distribution of the Program by any party to be under a license that satisfies the requirements of this section 3. 3.2 When the Program is Distributed as Source Code: a) it must be made available under this Agreement, or if the Program (i) is combined with other material in a separate file or files made available under a Secondary License, and (ii) the initial Contributor attached to the Source Code the notice described in Exhibit A of this Agreement, then the Program may be made available under the terms of such Secondary Licenses, and b) a copy of this Agreement must be included with each copy of the Program. 3.3 Contributors may not remove or alter any copyright, patent, trademark, attribution notices, disclaimers of warranty, or limitations of liability ("notices") contained within the Program from any copy of the Program which they Distribute, provided that Contributors may add their own appropriate notices. 4. COMMERCIAL DISTRIBUTION Commercial distributors of software may accept certain responsibilities with respect to end users, business partners and the like. While this license is intended to facilitate the commercial use of the Program, the Contributor who includes the Program in a commercial product offering should do so in a manner which does not create potential liability for other Contributors. Therefore, if a Contributor includes the Program in a commercial product offering, such Contributor ("Commercial Contributor") hereby agrees to defend and indemnify every other Contributor ("Indemnified Contributor") against any losses, damages and costs (collectively "Losses") arising from claims, lawsuits and other legal actions brought by a third party against the Indemnified Contributor to the extent caused by the acts or omissions of such Commercial Contributor in connection with its distribution of the Program in a commercial product offering. The obligations in this section do not apply to any claims or Losses relating to any actual or alleged intellectual property infringement. In order to qualify, an Indemnified Contributor must: a) promptly notify the Commercial Contributor in writing of such claim, and b) allow the Commercial Contributor to control, and cooperate with the Commercial Contributor in, the defense and any related settlement negotiations. The Indemnified Contributor may participate in any such claim at its own expense. For example, a Contributor might include the Program in a commercial product offering, Product X. That Contributor is then a Commercial Contributor. If that Commercial Contributor then makes performance claims, or offers warranties related to Product X, those performance claims and warranties are such Commercial Contributor's responsibility alone. Under this section, the Commercial Contributor would have to defend claims against the other Contributors related to those performance claims and warranties, and if a court requires any other Contributor to pay any damages as a result, the Commercial Contributor must pay those damages. 5. NO WARRANTY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is solely responsible for determining the appropriateness of using and distributing the Program and assumes all risks associated with its exercise of rights under this Agreement, including but not limited to the risks and costs of program errors, compliance with applicable laws, damage to or loss of data, programs or equipment, and unavailability or interruption of operations. 6. DISCLAIMER OF LIABILITY EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, AND TO THE EXTENT PERMITTED BY APPLICABLE LAW, NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. 7. GENERAL If any provision of this Agreement is invalid or unenforceable under applicable law, it shall not affect the validity or enforceability of the remainder of the terms of this Agreement, and without further action by the parties hereto, such provision shall be reformed to the minimum extent necessary to make such provision valid and enforceable. If Recipient institutes patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Program itself (excluding combinations of the Program with other software or hardware) infringes such Recipient's patent(s), then such Recipient's rights granted under Section 2(b) shall terminate as of the date such litigation is filed. All Recipient's rights under this Agreement shall terminate if it fails to comply with any of the material terms or conditions of this Agreement and does not cure such failure in a reasonable period of time after becoming aware of such noncompliance. If all Recipient's rights under this Agreement terminate, Recipient agrees to cease use and distribution of the Program as soon as reasonably practicable. However, Recipient's obligations under this Agreement and any licenses granted by Recipient relating to the Program shall continue and survive. Everyone is permitted to copy and distribute copies of this Agreement, but in order to avoid inconsistency the Agreement is copyrighted and may only be modified in the following manner. The Agreement Steward reserves the right to publish new versions (including revisions) of this Agreement from time to time. No one other than the Agreement Steward has the right to modify this Agreement. The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to serve as the Agreement Steward to a suitable separate entity. Each new version of the Agreement will be given a distinguishing version number. The Program (including Contributions) may always be Distributed subject to the version of the Agreement under which it was received. In addition, after a new version of the Agreement is published, Contributor may elect to Distribute the Program (including its Contributions) under the new version. Except as expressly stated in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to the intellectual property of any Contributor under this Agreement, whether expressly, by implication, estoppel or otherwise. All rights in the Program not expressly granted under this Agreement are reserved. Nothing in this Agreement is intended to be enforceable by any entity that is not a Contributor or Recipient. No third-party beneficiary rights are created under this Agreement. Exhibit A - Form of Secondary Licenses Notice "This Source Code may also be made available under the following Secondary Licenses when the conditions for such availability set forth in the Eclipse Public License, v. 2.0 are satisfied: {name license(s), version(s), and exceptions or additional permissions here}." Simply including a copy of this Agreement, including this Exhibit A is not sufficient to license the Source Code under Secondary Licenses. If it is not possible or desirable to put the notice in a particular file, then You may include the notice in a location (such as a LICENSE file in a relevant directory) where a recipient would be likely to look for such a notice. You may add additional accurate notices of copyright ownership. mosquitto-2.0.18/examples/000077500000000000000000000000001450213760600154705ustar00rootroot00000000000000mosquitto-2.0.18/examples/mysql_log/000077500000000000000000000000001450213760600174765ustar00rootroot00000000000000mosquitto-2.0.18/examples/mysql_log/Makefile000066400000000000000000000004361450213760600211410ustar00rootroot00000000000000CFLAGS=-Wall -ggdb LDFLAGS=../../lib/libmosquitto.so.1 -lmysqlclient .PHONY: all clean all : mosquitto_mysql_log mosquitto_mysql_log : mysql_log.o ${CC} $^ -o $@ ${LDFLAGS} mysql_log.o : mysql_log.c ${CC} -c $^ -o $@ ${CFLAGS} -I../../lib clean : -rm -f *.o mosquitto_mysql_log mosquitto-2.0.18/examples/mysql_log/mysql_log.c000066400000000000000000000052771450213760600216630ustar00rootroot00000000000000#include #include #include #ifndef WIN32 # include #else # include # define snprintf sprintf_s #endif #include #include #define db_host "localhost" #define db_username "mqtt_log" #define db_password "password" #define db_database "mqtt_log" #define db_port 3306 #define db_query "INSERT INTO mqtt_log (topic, payload) VALUES (?,?)" #define mqtt_host "localhost" #define mqtt_port 1883 static int run = 1; static MYSQL_STMT *stmt = NULL; void handle_signal(int s) { run = 0; } void connect_callback(struct mosquitto *mosq, void *obj, int result) { } void message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) { MYSQL_BIND bind[2]; memset(bind, 0, sizeof(bind)); bind[0].buffer_type = MYSQL_TYPE_STRING; bind[0].buffer = message->topic; bind[0].buffer_length = strlen(message->topic); // Note: payload is normally a binary blob and could contains // NULL byte. This sample does not handle it and assume payload is a // string. bind[1].buffer_type = MYSQL_TYPE_STRING; bind[1].buffer = message->payload; bind[1].buffer_length = message->payloadlen; mysql_stmt_bind_param(stmt, bind); mysql_stmt_execute(stmt); } int main(int argc, char *argv[]) { MYSQL *connection; my_bool reconnect = true; char clientid[24]; struct mosquitto *mosq; int rc = 0; signal(SIGINT, handle_signal); signal(SIGTERM, handle_signal); mysql_library_init(0, NULL, NULL); mosquitto_lib_init(); connection = mysql_init(NULL); if(connection){ mysql_options(connection, MYSQL_OPT_RECONNECT, &reconnect); connection = mysql_real_connect(connection, db_host, db_username, db_password, db_database, db_port, NULL, 0); if(connection){ stmt = mysql_stmt_init(connection); mysql_stmt_prepare(stmt, db_query, strlen(db_query)); memset(clientid, 0, 24); snprintf(clientid, 23, "mysql_log_%d", getpid()); mosq = mosquitto_new(clientid, true, connection); if(mosq){ mosquitto_connect_callback_set(mosq, connect_callback); mosquitto_message_callback_set(mosq, message_callback); rc = mosquitto_connect(mosq, mqtt_host, mqtt_port, 60); mosquitto_subscribe(mosq, NULL, "#", 0); while(run){ rc = mosquitto_loop(mosq, -1, 1); if(run && rc){ sleep(20); mosquitto_reconnect(mosq); } } mosquitto_destroy(mosq); } mysql_stmt_close(stmt); mysql_close(connection); }else{ fprintf(stderr, "Error: Unable to connect to database.\n"); printf("%s\n", mysql_error(connection)); rc = 1; } }else{ fprintf(stderr, "Error: Unable to start mysql.\n"); rc = 1; } mysql_library_end(); mosquitto_lib_cleanup(); return rc; } mosquitto-2.0.18/examples/publish/000077500000000000000000000000001450213760600171365ustar00rootroot00000000000000mosquitto-2.0.18/examples/publish/basic-1.c000066400000000000000000000105761450213760600205320ustar00rootroot00000000000000/* * This example shows how to publish messages from outside of the Mosquitto network loop. */ #include #include #include #include #include /* Callback called when the client receives a CONNACK message from the broker. */ void on_connect(struct mosquitto *mosq, void *obj, int reason_code) { /* Print out the connection result. mosquitto_connack_string() produces an * appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0 * clients is mosquitto_reason_string(). */ printf("on_connect: %s\n", mosquitto_connack_string(reason_code)); if(reason_code != 0){ /* If the connection fails for any reason, we don't want to keep on * retrying in this example, so disconnect. Without this, the client * will attempt to reconnect. */ mosquitto_disconnect(mosq); } /* You may wish to set a flag here to indicate to your application that the * client is now connected. */ } /* Callback called when the client knows to the best of its abilities that a * PUBLISH has been successfully sent. For QoS 0 this means the message has * been completely written to the operating system. For QoS 1 this means we * have received a PUBACK from the broker. For QoS 2 this means we have * received a PUBCOMP from the broker. */ void on_publish(struct mosquitto *mosq, void *obj, int mid) { printf("Message with mid %d has been published.\n", mid); } int get_temperature(void) { sleep(1); /* Prevent a storm of messages - this pretend sensor works at 1Hz */ return random()%100; } /* This function pretends to read some data from a sensor and publish it.*/ void publish_sensor_data(struct mosquitto *mosq) { char payload[20]; int temp; int rc; /* Get our pretend data */ temp = get_temperature(); /* Print it to a string for easy human reading - payload format is highly * application dependent. */ snprintf(payload, sizeof(payload), "%d", temp); /* Publish the message * mosq - our client instance * *mid = NULL - we don't want to know what the message id for this message is * topic = "example/temperature" - the topic on which this message will be published * payloadlen = strlen(payload) - the length of our payload in bytes * payload - the actual payload * qos = 2 - publish with QoS 2 for this example * retain = false - do not use the retained message feature for this message */ rc = mosquitto_publish(mosq, NULL, "example/temperature", strlen(payload), payload, 2, false); if(rc != MOSQ_ERR_SUCCESS){ fprintf(stderr, "Error publishing: %s\n", mosquitto_strerror(rc)); } } int main(int argc, char *argv[]) { struct mosquitto *mosq; int rc; /* Required before calling other mosquitto functions */ mosquitto_lib_init(); /* Create a new client instance. * id = NULL -> ask the broker to generate a client id for us * clean session = true -> the broker should remove old sessions when we connect * obj = NULL -> we aren't passing any of our private data for callbacks */ mosq = mosquitto_new(NULL, true, NULL); if(mosq == NULL){ fprintf(stderr, "Error: Out of memory.\n"); return 1; } /* Configure callbacks. This should be done before connecting ideally. */ mosquitto_connect_callback_set(mosq, on_connect); mosquitto_publish_callback_set(mosq, on_publish); /* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds. * This call makes the socket connection only, it does not complete the MQTT * CONNECT/CONNACK flow, you should use mosquitto_loop_start() or * mosquitto_loop_forever() for processing net traffic. */ rc = mosquitto_connect(mosq, "test.mosquitto.org", 1883, 60); if(rc != MOSQ_ERR_SUCCESS){ mosquitto_destroy(mosq); fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); return 1; } /* Run the network loop in a background thread, this call returns quickly. */ rc = mosquitto_loop_start(mosq); if(rc != MOSQ_ERR_SUCCESS){ mosquitto_destroy(mosq); fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); return 1; } /* At this point the client is connected to the network socket, but may not * have completed CONNECT/CONNACK. * It is fairly safe to start queuing messages at this point, but if you * want to be really sure you should wait until after a successful call to * the connect callback. * In this case we know it is 1 second before we start publishing. */ while(1){ publish_sensor_data(mosq); } mosquitto_lib_cleanup(); return 0; } mosquitto-2.0.18/examples/subscribe/000077500000000000000000000000001450213760600174515ustar00rootroot00000000000000mosquitto-2.0.18/examples/subscribe/basic-1.c000066400000000000000000000100531450213760600210330ustar00rootroot00000000000000/* * This example shows how to write a client that subscribes to a topic and does * not do anything other than handle the messages that are received. */ #include #include #include #include #include /* Callback called when the client receives a CONNACK message from the broker. */ void on_connect(struct mosquitto *mosq, void *obj, int reason_code) { int rc; /* Print out the connection result. mosquitto_connack_string() produces an * appropriate string for MQTT v3.x clients, the equivalent for MQTT v5.0 * clients is mosquitto_reason_string(). */ printf("on_connect: %s\n", mosquitto_connack_string(reason_code)); if(reason_code != 0){ /* If the connection fails for any reason, we don't want to keep on * retrying in this example, so disconnect. Without this, the client * will attempt to reconnect. */ mosquitto_disconnect(mosq); } /* Making subscriptions in the on_connect() callback means that if the * connection drops and is automatically resumed by the client, then the * subscriptions will be recreated when the client reconnects. */ rc = mosquitto_subscribe(mosq, NULL, "example/temperature", 1); if(rc != MOSQ_ERR_SUCCESS){ fprintf(stderr, "Error subscribing: %s\n", mosquitto_strerror(rc)); /* We might as well disconnect if we were unable to subscribe */ mosquitto_disconnect(mosq); } } /* Callback called when the broker sends a SUBACK in response to a SUBSCRIBE. */ void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { int i; bool have_subscription = false; /* In this example we only subscribe to a single topic at once, but a * SUBSCRIBE can contain many topics at once, so this is one way to check * them all. */ for(i=0; itopic, msg->qos, (char *)msg->payload); } int main(int argc, char *argv[]) { struct mosquitto *mosq; int rc; /* Required before calling other mosquitto functions */ mosquitto_lib_init(); /* Create a new client instance. * id = NULL -> ask the broker to generate a client id for us * clean session = true -> the broker should remove old sessions when we connect * obj = NULL -> we aren't passing any of our private data for callbacks */ mosq = mosquitto_new(NULL, true, NULL); if(mosq == NULL){ fprintf(stderr, "Error: Out of memory.\n"); return 1; } /* Configure callbacks. This should be done before connecting ideally. */ mosquitto_connect_callback_set(mosq, on_connect); mosquitto_subscribe_callback_set(mosq, on_subscribe); mosquitto_message_callback_set(mosq, on_message); /* Connect to test.mosquitto.org on port 1883, with a keepalive of 60 seconds. * This call makes the socket connection only, it does not complete the MQTT * CONNECT/CONNACK flow, you should use mosquitto_loop_start() or * mosquitto_loop_forever() for processing net traffic. */ rc = mosquitto_connect(mosq, "test.mosquitto.org", 1883, 60); if(rc != MOSQ_ERR_SUCCESS){ mosquitto_destroy(mosq); fprintf(stderr, "Error: %s\n", mosquitto_strerror(rc)); return 1; } /* Run the network loop in a blocking call. The only thing we do in this * example is to print incoming messages, so a blocking call here is fine. * * This call will continue forever, carrying automatic reconnections if * necessary, until the user calls mosquitto_disconnect(). */ mosquitto_loop_forever(mosq, -1, 1); mosquitto_lib_cleanup(); return 0; } mosquitto-2.0.18/examples/subscribe_simple/000077500000000000000000000000001450213760600210225ustar00rootroot00000000000000mosquitto-2.0.18/examples/subscribe_simple/Makefile000066400000000000000000000015071450213760600224650ustar00rootroot00000000000000include ../../config.mk .PHONY: all all : sub_callback sub_single sub_multiple sub_callback : callback.o ${CROSS_COMPILE}${CC} $^ -o $@ ../../lib/libmosquitto.so.${SOVERSION} sub_single : single.o ${CROSS_COMPILE}${CC} $^ -o $@ ../../lib/libmosquitto.so.${SOVERSION} sub_multiple : multiple.o ${CROSS_COMPILE}${CC} $^ -o $@ ../../lib/libmosquitto.so.${SOVERSION} callback.o : callback.c ../../lib/libmosquitto.so.${SOVERSION} ${CROSS_COMPILE}${CC} -c $< -o $@ -I../../lib ${CFLAGS} single.o : single.c ../../lib/libmosquitto.so.${SOVERSION} ${CROSS_COMPILE}${CC} -c $< -o $@ -I../../lib ${CFLAGS} multiple.o : multiple.c ../../lib/libmosquitto.so.${SOVERSION} ${CROSS_COMPILE}${CC} -c $< -o $@ -I../../lib ${CFLAGS} ../../lib/libmosquitto.so.${SOVERSION} : $(MAKE) -C ../../lib clean : -rm -f *.o sub_single sub_multiple mosquitto-2.0.18/examples/subscribe_simple/callback.c000066400000000000000000000011051450213760600227170ustar00rootroot00000000000000#include #include #include "mosquitto.h" int on_message(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *msg) { printf("%s %s (%d)\n", msg->topic, (const char *)msg->payload, msg->payloadlen); return 0; } int main(int argc, char *argv[]) { int rc; mosquitto_lib_init(); rc = mosquitto_subscribe_callback( on_message, NULL, "irc/#", 0, "test.mosquitto.org", 1883, NULL, 60, true, NULL, NULL, NULL, NULL); if(rc){ printf("Error: %s\n", mosquitto_strerror(rc)); } mosquitto_lib_cleanup(); return rc; } mosquitto-2.0.18/examples/subscribe_simple/multiple.c000066400000000000000000000011651450213760600230240ustar00rootroot00000000000000#include #include #include "mosquitto.h" #define COUNT 3 int main(int argc, char *argv[]) { int rc; int i; struct mosquitto_message *msg; mosquitto_lib_init(); rc = mosquitto_subscribe_simple( &msg, COUNT, true, "irc/#", 0, "test.mosquitto.org", 1883, NULL, 60, true, NULL, NULL, NULL, NULL); if(rc){ printf("Error: %s\n", mosquitto_strerror(rc)); mosquitto_lib_cleanup(); return rc; } for(i=0; i #include #include "mosquitto.h" int main(int argc, char *argv[]) { int rc; struct mosquitto_message *msg; mosquitto_lib_init(); rc = mosquitto_subscribe_simple( &msg, 1, true, "irc/#", 0, "test.mosquitto.org", 1883, NULL, 60, true, NULL, NULL, NULL, NULL); if(rc){ printf("Error: %s\n", mosquitto_strerror(rc)); mosquitto_lib_cleanup(); return rc; } printf("%s %s\n", msg->topic, (char *)msg->payload); mosquitto_message_free(&msg); mosquitto_lib_cleanup(); return 0; } mosquitto-2.0.18/examples/temperature_conversion/000077500000000000000000000000001450213760600222725ustar00rootroot00000000000000mosquitto-2.0.18/examples/temperature_conversion/Makefile000066400000000000000000000007111450213760600237310ustar00rootroot00000000000000CFLAGS=-Wall -ggdb -I../../lib -I../../lib/cpp LDFLAGS=-L../../lib ../../lib/cpp/libmosquittopp.so.1 ../../lib/libmosquitto.so.1 .PHONY: all clean all : mqtt_temperature_conversion mqtt_temperature_conversion : main.o temperature_conversion.o ${CXX} $^ -o $@ ${LDFLAGS} main.o : main.cpp ${CXX} -c $^ -o $@ ${CFLAGS} temperature_conversion.o : temperature_conversion.cpp ${CXX} -c $^ -o $@ ${CFLAGS} clean : -rm -f *.o mqtt_temperature_conversion mosquitto-2.0.18/examples/temperature_conversion/main.cpp000066400000000000000000000004111450213760600237160ustar00rootroot00000000000000#include "temperature_conversion.h" int main(int argc, char *argv[]) { class mqtt_tempconv *tempconv; int rc; mosqpp::lib_init(); tempconv = new mqtt_tempconv("tempconv", "localhost", 1883); tempconv->loop_forever(); mosqpp::lib_cleanup(); return 0; } mosquitto-2.0.18/examples/temperature_conversion/readme.txt000066400000000000000000000004601450213760600242700ustar00rootroot00000000000000This is a simple example of the C++ library mosquittopp. It is a client that subscribes to the topic temperature/celsius which should have temperature data in text form being published to it. It reads this data as a Celsius temperature, converts to Fahrenheit and republishes on temperature/fahrenheit. mosquitto-2.0.18/examples/temperature_conversion/temperature_conversion.cpp000066400000000000000000000023031450213760600275760ustar00rootroot00000000000000#include #include #include "temperature_conversion.h" #include mqtt_tempconv::mqtt_tempconv(const char *id, const char *host, int port) : mosquittopp(id) { int keepalive = 60; /* Connect immediately. This could also be done by calling * mqtt_tempconv->connect(). */ connect(host, port, keepalive); }; mqtt_tempconv::~mqtt_tempconv() { } void mqtt_tempconv::on_connect(int rc) { printf("Connected with code %d.\n", rc); if(rc == 0){ /* Only attempt to subscribe on a successful connect. */ subscribe(NULL, "temperature/celsius"); } } void mqtt_tempconv::on_message(const struct mosquitto_message *message) { double temp_celsius, temp_fahrenheit; char buf[51]; if(!strcmp(message->topic, "temperature/celsius")){ memset(buf, 0, 51*sizeof(char)); /* Copy N-1 bytes to ensure always 0 terminated. */ memcpy(buf, message->payload, 50*sizeof(char)); temp_celsius = atof(buf); temp_fahrenheit = temp_celsius*9.0/5.0 + 32.0; snprintf(buf, 50, "%f", temp_fahrenheit); publish(NULL, "temperature/fahrenheit", strlen(buf), buf); } } void mqtt_tempconv::on_subscribe(int mid, int qos_count, const int *granted_qos) { printf("Subscription succeeded.\n"); } mosquitto-2.0.18/examples/temperature_conversion/temperature_conversion.h000066400000000000000000000006221450213760600272450ustar00rootroot00000000000000#ifndef TEMPERATURE_CONVERSION_H #define TEMPERATURE_CONVERSION_H #include class mqtt_tempconv : public mosqpp::mosquittopp { public: mqtt_tempconv(const char *id, const char *host, int port); ~mqtt_tempconv(); void on_connect(int rc); void on_message(const struct mosquitto_message *message); void on_subscribe(int mid, int qos_count, const int *granted_qos); }; #endif mosquitto-2.0.18/include/000077500000000000000000000000001450213760600152755ustar00rootroot00000000000000mosquitto-2.0.18/include/mosquitto.h000066400000000000000000003661631450213760600175310ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MOSQUITTO_H #define MOSQUITTO_H /* * File: mosquitto.h * * This header contains functions and definitions for use with libmosquitto, the Mosquitto client library. * * The definitions are also used in Mosquitto broker plugins, and some functions are available to plugins. */ #ifdef __cplusplus extern "C" { #endif #ifdef WIN32 # ifdef mosquitto_EXPORTS # define libmosq_EXPORT __declspec(dllexport) # else # ifndef LIBMOSQUITTO_STATIC # ifdef libmosquitto_EXPORTS # define libmosq_EXPORT __declspec(dllexport) # else # define libmosq_EXPORT __declspec(dllimport) # endif # else # define libmosq_EXPORT # endif # endif #else # define libmosq_EXPORT #endif #if defined(_MSC_VER) && _MSC_VER < 1900 && !defined(bool) # ifndef __cplusplus # define bool char # define true 1 # define false 0 # endif #else # ifndef __cplusplus # include # endif #endif #include #include #define LIBMOSQUITTO_MAJOR 2 #define LIBMOSQUITTO_MINOR 0 #define LIBMOSQUITTO_REVISION 18 /* LIBMOSQUITTO_VERSION_NUMBER looks like 1002001 for e.g. version 1.2.1. */ #define LIBMOSQUITTO_VERSION_NUMBER (LIBMOSQUITTO_MAJOR*1000000+LIBMOSQUITTO_MINOR*1000+LIBMOSQUITTO_REVISION) /* Log types */ #define MOSQ_LOG_NONE 0 #define MOSQ_LOG_INFO (1<<0) #define MOSQ_LOG_NOTICE (1<<1) #define MOSQ_LOG_WARNING (1<<2) #define MOSQ_LOG_ERR (1<<3) #define MOSQ_LOG_DEBUG (1<<4) #define MOSQ_LOG_SUBSCRIBE (1<<5) #define MOSQ_LOG_UNSUBSCRIBE (1<<6) #define MOSQ_LOG_WEBSOCKETS (1<<7) #define MOSQ_LOG_INTERNAL 0x80000000U #define MOSQ_LOG_ALL 0xFFFFFFFFU /* Enum: mosq_err_t * Integer values returned from many libmosquitto functions. */ enum mosq_err_t { MOSQ_ERR_AUTH_CONTINUE = -4, MOSQ_ERR_NO_SUBSCRIBERS = -3, MOSQ_ERR_SUB_EXISTS = -2, MOSQ_ERR_CONN_PENDING = -1, MOSQ_ERR_SUCCESS = 0, MOSQ_ERR_NOMEM = 1, MOSQ_ERR_PROTOCOL = 2, MOSQ_ERR_INVAL = 3, MOSQ_ERR_NO_CONN = 4, MOSQ_ERR_CONN_REFUSED = 5, MOSQ_ERR_NOT_FOUND = 6, MOSQ_ERR_CONN_LOST = 7, MOSQ_ERR_TLS = 8, MOSQ_ERR_PAYLOAD_SIZE = 9, MOSQ_ERR_NOT_SUPPORTED = 10, MOSQ_ERR_AUTH = 11, MOSQ_ERR_ACL_DENIED = 12, MOSQ_ERR_UNKNOWN = 13, MOSQ_ERR_ERRNO = 14, MOSQ_ERR_EAI = 15, MOSQ_ERR_PROXY = 16, MOSQ_ERR_PLUGIN_DEFER = 17, MOSQ_ERR_MALFORMED_UTF8 = 18, MOSQ_ERR_KEEPALIVE = 19, MOSQ_ERR_LOOKUP = 20, MOSQ_ERR_MALFORMED_PACKET = 21, MOSQ_ERR_DUPLICATE_PROPERTY = 22, MOSQ_ERR_TLS_HANDSHAKE = 23, MOSQ_ERR_QOS_NOT_SUPPORTED = 24, MOSQ_ERR_OVERSIZE_PACKET = 25, MOSQ_ERR_OCSP = 26, MOSQ_ERR_TIMEOUT = 27, MOSQ_ERR_RETAIN_NOT_SUPPORTED = 28, MOSQ_ERR_TOPIC_ALIAS_INVALID = 29, MOSQ_ERR_ADMINISTRATIVE_ACTION = 30, MOSQ_ERR_ALREADY_EXISTS = 31, }; /* Enum: mosq_opt_t * * Client options. * * See , , and . */ enum mosq_opt_t { MOSQ_OPT_PROTOCOL_VERSION = 1, MOSQ_OPT_SSL_CTX = 2, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS = 3, MOSQ_OPT_RECEIVE_MAXIMUM = 4, MOSQ_OPT_SEND_MAXIMUM = 5, MOSQ_OPT_TLS_KEYFORM = 6, MOSQ_OPT_TLS_ENGINE = 7, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1 = 8, MOSQ_OPT_TLS_OCSP_REQUIRED = 9, MOSQ_OPT_TLS_ALPN = 10, MOSQ_OPT_TCP_NODELAY = 11, MOSQ_OPT_BIND_ADDRESS = 12, MOSQ_OPT_TLS_USE_OS_CERTS = 13, }; /* MQTT specification restricts client ids to a maximum of 23 characters */ #define MOSQ_MQTT_ID_MAX_LENGTH 23 #define MQTT_PROTOCOL_V31 3 #define MQTT_PROTOCOL_V311 4 #define MQTT_PROTOCOL_V5 5 /* Struct: mosquitto_message * * Contains details of a PUBLISH message. * * int mid - the message/packet ID of the PUBLISH message, assuming this is a * QoS 1 or 2 message. Will be set to 0 for QoS 0 messages. * * char *topic - the topic the message was delivered on. * * void *payload - the message payload. This will be payloadlen bytes long, and * may be NULL if a zero length payload was sent. * * int payloadlen - the length of the payload, in bytes. * * int qos - the quality of service of the message, 0, 1, or 2. * * bool retain - set to true for stale retained messages. */ struct mosquitto_message{ int mid; char *topic; void *payload; int payloadlen; int qos; bool retain; }; struct mosquitto; typedef struct mqtt5__property mosquitto_property; /* * Topic: Threads * libmosquitto provides thread safe operation, with the exception of * which is not thread safe. * * If the library has been compiled without thread support it is *not* * guaranteed to be thread safe. * * If your application uses threads you must use to * tell the library this is the case, otherwise it makes some optimisations * for the single threaded case that may result in unexpected behaviour for * the multi threaded case. */ /*************************************************** * Important note * * The following functions that deal with network operations will return * MOSQ_ERR_SUCCESS on success, but this does not mean that the operation has * taken place. An attempt will be made to write the network data, but if the * socket is not available for writing at that time then the packet will not be * sent. To ensure the packet is sent, call mosquitto_loop() (which must also * be called to process incoming network data). * This is especially important when disconnecting a client that has a will. If * the broker does not receive the DISCONNECT command, it will assume that the * client has disconnected unexpectedly and send the will. * * mosquitto_connect() * mosquitto_disconnect() * mosquitto_subscribe() * mosquitto_unsubscribe() * mosquitto_publish() ***************************************************/ /* ====================================================================== * * Section: Library version, init, and cleanup * * ====================================================================== */ /* * Function: mosquitto_lib_version * * Can be used to obtain version information for the mosquitto library. * This allows the application to compare the library version against the * version it was compiled against by using the LIBMOSQUITTO_MAJOR, * LIBMOSQUITTO_MINOR and LIBMOSQUITTO_REVISION defines. * * Parameters: * major - an integer pointer. If not NULL, the major version of the * library will be returned in this variable. * minor - an integer pointer. If not NULL, the minor version of the * library will be returned in this variable. * revision - an integer pointer. If not NULL, the revision of the library will * be returned in this variable. * * Returns: * LIBMOSQUITTO_VERSION_NUMBER - which is a unique number based on the major, * minor and revision values. * See Also: * , */ libmosq_EXPORT int mosquitto_lib_version(int *major, int *minor, int *revision); /* * Function: mosquitto_lib_init * * Must be called before any other mosquitto functions. * * This function is *not* thread safe. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_UNKNOWN - on Windows, if sockets couldn't be initialized. * * See Also: * , */ libmosq_EXPORT int mosquitto_lib_init(void); /* * Function: mosquitto_lib_cleanup * * Call to free resources associated with the library. * * Returns: * MOSQ_ERR_SUCCESS - always * * See Also: * , */ libmosq_EXPORT int mosquitto_lib_cleanup(void); /* ====================================================================== * * Section: Client creation, destruction, and reinitialisation * * ====================================================================== */ /* * Function: mosquitto_new * * Create a new mosquitto client instance. * * Parameters: * id - String to use as the client id. If NULL, a random client id * will be generated. If id is NULL, clean_session must be true. * clean_session - set to true to instruct the broker to clean all messages * and subscriptions on disconnect, false to instruct it to * keep them. See the man page mqtt(7) for more details. * Note that a client will never discard its own outgoing * messages on disconnect. Calling or * will cause the messages to be resent. * Use to reset a client to its * original state. * Must be set to true if the id parameter is NULL. * obj - A user pointer that will be passed as an argument to any * callbacks that are specified. * * Returns: * Pointer to a struct mosquitto on success. * NULL on failure. Interrogate errno to determine the cause for the failure: * - ENOMEM on out of memory. * - EINVAL on invalid input parameters. * * See Also: * , , */ libmosq_EXPORT struct mosquitto *mosquitto_new(const char *id, bool clean_session, void *obj); /* * Function: mosquitto_destroy * * Use to free memory associated with a mosquitto client instance. * * Parameters: * mosq - a struct mosquitto pointer to free. * * See Also: * , */ libmosq_EXPORT void mosquitto_destroy(struct mosquitto *mosq); /* * Function: mosquitto_reinitialise * * This function allows an existing mosquitto client to be reused. Call on a * mosquitto instance to close any open network connections, free memory * and reinitialise the client with the new parameters. The end result is the * same as the output of . * * Parameters: * mosq - a valid mosquitto instance. * id - string to use as the client id. If NULL, a random client id * will be generated. If id is NULL, clean_session must be true. * clean_session - set to true to instruct the broker to clean all messages * and subscriptions on disconnect, false to instruct it to * keep them. See the man page mqtt(7) for more details. * Must be set to true if the id parameter is NULL. * obj - A user pointer that will be passed as an argument to any * callbacks that are specified. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_MALFORMED_UTF8 - if the client id is not valid UTF-8. * * See Also: * , */ libmosq_EXPORT int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_session, void *obj); /* ====================================================================== * * Section: Will * * ====================================================================== */ /* * Function: mosquitto_will_set * * Configure will information for a mosquitto instance. By default, clients do * not have a will. This must be called before calling . * * It is valid to use this function for clients using all MQTT protocol versions. * If you need to set MQTT v5 Will properties, use instead. * * Parameters: * mosq - a valid mosquitto instance. * topic - the topic on which to publish the will. * payloadlen - the size of the payload (bytes). Valid values are between 0 and * 268,435,455. * payload - pointer to the data to send. If payloadlen > 0 this must be a * valid memory location. * qos - integer value 0, 1 or 2 indicating the Quality of Service to be * used for the will. * retain - set to true to make the will a retained message. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. */ libmosq_EXPORT int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain); /* * Function: mosquitto_will_set_v5 * * Configure will information for a mosquitto instance, with attached * properties. By default, clients do not have a will. This must be called * before calling . * * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument * will be applied to the Will. For MQTT v3.1.1 and below, the `properties` * argument will be ignored. * * Set your client to use MQTT v5 immediately after it is created: * * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. * topic - the topic on which to publish the will. * payloadlen - the size of the payload (bytes). Valid values are between 0 and * 268,435,455. * payload - pointer to the data to send. If payloadlen > 0 this must be a * valid memory location. * qos - integer value 0, 1 or 2 indicating the Quality of Service to be * used for the will. * retain - set to true to make the will a retained message. * properties - list of MQTT 5 properties. Can be NULL. On success only, the * property list becomes the property of libmosquitto once this * function is called and will be freed by the library. The * property list must be freed by the application on error. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8. * MOSQ_ERR_NOT_SUPPORTED - if properties is not NULL and the client is not * using MQTT v5 * MOSQ_ERR_PROTOCOL - if a property is invalid for use with wills. * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. */ libmosq_EXPORT int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); /* * Function: mosquitto_will_clear * * Remove a previously configured will. This must be called before calling * . * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. */ libmosq_EXPORT int mosquitto_will_clear(struct mosquitto *mosq); /* ====================================================================== * * Section: Username and password * * ====================================================================== */ /* * Function: mosquitto_username_pw_set * * Configure username and password for a mosquitto instance. By default, no * username or password will be sent. For v3.1 and v3.1.1 clients, if username * is NULL, the password argument is ignored. * * This is must be called before calling . * * Parameters: * mosq - a valid mosquitto instance. * username - the username to send as a string, or NULL to disable * authentication. * password - the password to send as a string. Set to NULL when username is * valid in order to send just a username. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. */ libmosq_EXPORT int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password); /* ====================================================================== * * Section: Connecting, reconnecting, disconnecting * * ====================================================================== */ /* * Function: mosquitto_connect * * Connect to an MQTT broker. * * It is valid to use this function for clients using all MQTT protocol versions. * If you need to set MQTT v5 CONNECT properties, use * instead. * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. * keepalive - the number of seconds after which the client should send a PING * message to the broker if no other messages have been exchanged * in that time. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: * * mosq == NULL * * host == NULL * * port < 0 * * keepalive < 5 * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , , , */ libmosq_EXPORT int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive); /* * Function: mosquitto_connect_bind * * Connect to an MQTT broker. This extends the functionality of * by adding the bind_address parameter. Use this function * if you need to restrict network communication over a particular interface. * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. * keepalive - the number of seconds after which the client should send a PING * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, * set this to NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , */ libmosq_EXPORT int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); /* * Function: mosquitto_connect_bind_v5 * * Connect to an MQTT broker. This extends the functionality of * by adding the bind_address parameter and MQTT v5 * properties. Use this function if you need to restrict network communication * over a particular interface. * * Use e.g. and similar to create a list of * properties, then attach them to this publish. Properties need freeing with * . * * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument * will be applied to the CONNECT message. For MQTT v3.1.1 and below, the * `properties` argument will be ignored. * * Set your client to use MQTT v5 immediately after it is created: * * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. * keepalive - the number of seconds after which the client should send a PING * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, * set this to NULL. * properties - the MQTT 5 properties for the connect (not for the Will). * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: * * mosq == NULL * * host == NULL * * port < 0 * * keepalive < 5 * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. * MOSQ_ERR_PROTOCOL - if any property is invalid for use with CONNECT. * * See Also: * , , */ libmosq_EXPORT int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties); /* * Function: mosquitto_connect_async * * Connect to an MQTT broker. This is a non-blocking call. If you use * your client must use the threaded interface * . If you need to use , you must use * to connect the client. * * May be called before or after . * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. * keepalive - the number of seconds after which the client should send a PING * message to the broker if no other messages have been exchanged * in that time. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , , , */ libmosq_EXPORT int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive); /* * Function: mosquitto_connect_bind_async * * Connect to an MQTT broker. This is a non-blocking call. If you use * your client must use the threaded interface * . If you need to use , you must use * to connect the client. * * This extends the functionality of by adding the * bind_address parameter. Use this function if you need to restrict network * communication over a particular interface. * * May be called before or after . * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname or ip address of the broker to connect to. * port - the network port to connect to. Usually 1883. * keepalive - the number of seconds after which the client should send a PING * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, * set this to NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: * * mosq == NULL * * host == NULL * * port < 0 * * keepalive < 5 * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , */ libmosq_EXPORT int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address); /* * Function: mosquitto_connect_srv * * Connect to an MQTT broker. * * If you set `host` to `example.com`, then this call will attempt to retrieve * the DNS SRV record for `_secure-mqtt._tcp.example.com` or * `_mqtt._tcp.example.com` to discover which actual host to connect to. * * DNS SRV support is not usually compiled in to libmosquitto, use of this call * is not recommended. * * Parameters: * mosq - a valid mosquitto instance. * host - the hostname to search for an SRV record. * keepalive - the number of seconds after which the client should send a PING * message to the broker if no other messages have been exchanged * in that time. * bind_address - the hostname or ip address of the local network interface to * bind to. If you do not want to bind to a specific interface, * set this to NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid, which could be any of: * * mosq == NULL * * host == NULL * * port < 0 * * keepalive < 5 * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , */ libmosq_EXPORT int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepalive, const char *bind_address); /* * Function: mosquitto_reconnect * * Reconnect to a broker. * * This function provides an easy way of reconnecting to a broker after a * connection has been lost. It uses the values that were provided in the * call. It must not be called before * . * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , */ libmosq_EXPORT int mosquitto_reconnect(struct mosquitto *mosq); /* * Function: mosquitto_reconnect_async * * Reconnect to a broker. Non blocking version of . * * This function provides an easy way of reconnecting to a broker after a * connection has been lost. It uses the values that were provided in the * or calls. It must not be * called before . * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , */ libmosq_EXPORT int mosquitto_reconnect_async(struct mosquitto *mosq); /* * Function: mosquitto_disconnect * * Disconnect from the broker. * * It is valid to use this function for clients using all MQTT protocol versions. * If you need to set MQTT v5 DISCONNECT properties, use * instead. * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. */ libmosq_EXPORT int mosquitto_disconnect(struct mosquitto *mosq); /* * Function: mosquitto_disconnect_v5 * * Disconnect from the broker, with attached MQTT properties. * * Use e.g. and similar to create a list of * properties, then attach them to this publish. Properties need freeing with * . * * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument * will be applied to the DISCONNECT message. For MQTT v3.1.1 and below, the * `properties` argument will be ignored. * * Set your client to use MQTT v5 immediately after it is created: * * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. * reason_code - the disconnect reason code. * properties - a valid mosquitto_property list, or NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. * MOSQ_ERR_PROTOCOL - if any property is invalid for use with DISCONNECT. */ libmosq_EXPORT int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); /* ====================================================================== * * Section: Publishing, subscribing, unsubscribing * * ====================================================================== */ /* * Function: mosquitto_publish * * Publish a message on a given topic. * * It is valid to use this function for clients using all MQTT protocol versions. * If you need to set MQTT v5 PUBLISH properties, use * instead. * * Parameters: * mosq - a valid mosquitto instance. * mid - pointer to an int. If not NULL, the function will set this * to the message id of this particular message. This can be then * used with the publish callback to determine when the message * has been sent. * Note that although the MQTT protocol doesn't use message ids * for messages with QoS=0, libmosquitto assigns them message ids * so they can be tracked with this parameter. * topic - null terminated string of the topic to publish to. * payloadlen - the size of the payload (bytes). Valid values are between 0 and * 268,435,455. * payload - pointer to the data to send. If payloadlen > 0 this must be a * valid memory location. * qos - integer value 0, 1 or 2 indicating the Quality of Service to be * used for the message. * retain - set to true to make the message retained. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the * broker. * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by * the broker. * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. * * See Also: * */ libmosq_EXPORT int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain); /* * Function: mosquitto_publish_v5 * * Publish a message on a given topic, with attached MQTT properties. * * Use e.g. and similar to create a list of * properties, then attach them to this publish. Properties need freeing with * . * * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the * `properties` argument will be ignored. * * Set your client to use MQTT v5 immediately after it is created: * * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. * mid - pointer to an int. If not NULL, the function will set this * to the message id of this particular message. This can be then * used with the publish callback to determine when the message * has been sent. * Note that although the MQTT protocol doesn't use message ids * for messages with QoS=0, libmosquitto assigns them message ids * so they can be tracked with this parameter. * topic - null terminated string of the topic to publish to. * payloadlen - the size of the payload (bytes). Valid values are between 0 and * 268,435,455. * payload - pointer to the data to send. If payloadlen > 0 this must be a * valid memory location. * qos - integer value 0, 1 or 2 indicating the Quality of Service to be * used for the message. * retain - set to true to make the message retained. * properties - a valid mosquitto_property list, or NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the * broker. * MOSQ_ERR_PAYLOAD_SIZE - if payloadlen is too large. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. * MOSQ_ERR_PROTOCOL - if any property is invalid for use with PUBLISH. * MOSQ_ERR_QOS_NOT_SUPPORTED - if the QoS is greater than that supported by * the broker. * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. */ libmosq_EXPORT int mosquitto_publish_v5( struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties); /* * Function: mosquitto_subscribe * * Subscribe to a topic. * * It is valid to use this function for clients using all MQTT protocol versions. * If you need to set MQTT v5 SUBSCRIBE properties, use * instead. * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to * the message id of this particular message. This can be then used * with the subscribe callback to determine when the message has been * sent. * sub - the subscription pattern. * qos - the requested Quality of Service for this subscription. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. */ libmosq_EXPORT int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos); /* * Function: mosquitto_subscribe_v5 * * Subscribe to a topic, with attached MQTT properties. * * Use e.g. and similar to create a list of * properties, then attach them to this publish. Properties need freeing with * . * * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the * `properties` argument will be ignored. * * Set your client to use MQTT v5 immediately after it is created: * * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to * the message id of this particular message. This can be then used * with the subscribe callback to determine when the message has been * sent. * sub - the subscription pattern. * qos - the requested Quality of Service for this subscription. * options - options to apply to this subscription, OR'd together. Set to 0 to * use the default options, otherwise choose from list of * properties - a valid mosquitto_property list, or NULL. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. * MOSQ_ERR_PROTOCOL - if any property is invalid for use with SUBSCRIBE. * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. */ libmosq_EXPORT int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties); /* * Function: mosquitto_subscribe_multiple * * Subscribe to multiple topics. * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to * the message id of this particular message. This can be then used * with the subscribe callback to determine when the message has been * sent. * sub_count - the count of subscriptions to be made * sub - array of sub_count pointers, each pointing to a subscription string. * The "char *const *const" datatype ensures that neither the array of * pointers nor the strings that they point to are mutable. If you aren't * familiar with this, just think of it as a safer "char **", * equivalent to "const char *" for a simple string pointer. * qos - the requested Quality of Service for each subscription. * options - options to apply to this subscription, OR'd together. This * argument is not used for MQTT v3 susbcriptions. Set to 0 to use * the default options, otherwise choose from list of * properties - a valid mosquitto_property list, or NULL. Only used with MQTT * v5 clients. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. */ libmosq_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); /* * Function: mosquitto_unsubscribe * * Unsubscribe from a topic. * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to * the message id of this particular message. This can be then used * with the unsubscribe callback to determine when the message has been * sent. * sub - the unsubscription pattern. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. */ libmosq_EXPORT int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub); /* * Function: mosquitto_unsubscribe_v5 * * Unsubscribe from a topic, with attached MQTT properties. * * It is valid to use this function for clients using all MQTT protocol versions. * If you need to set MQTT v5 UNSUBSCRIBE properties, use * instead. * * Use e.g. and similar to create a list of * properties, then attach them to this publish. Properties need freeing with * . * * If the mosquitto instance `mosq` is using MQTT v5, the `properties` argument * will be applied to the PUBLISH message. For MQTT v3.1.1 and below, the * `properties` argument will be ignored. * * Set your client to use MQTT v5 immediately after it is created: * * mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to * the message id of this particular message. This can be then used * with the unsubscribe callback to determine when the message has been * sent. * sub - the unsubscription pattern. * properties - a valid mosquitto_property list, or NULL. Only used with MQTT * v5 clients. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. * MOSQ_ERR_PROTOCOL - if any property is invalid for use with UNSUBSCRIBE. * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. */ libmosq_EXPORT int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties); /* * Function: mosquitto_unsubscribe_multiple * * Unsubscribe from multiple topics. * * Parameters: * mosq - a valid mosquitto instance. * mid - a pointer to an int. If not NULL, the function will set this to * the message id of this particular message. This can be then used * with the subscribe callback to determine when the message has been * sent. * sub_count - the count of unsubscriptions to be made * sub - array of sub_count pointers, each pointing to an unsubscription string. * The "char *const *const" datatype ensures that neither the array of * pointers nor the strings that they point to are mutable. If you aren't * familiar with this, just think of it as a safer "char **", * equivalent to "const char *" for a simple string pointer. * properties - a valid mosquitto_property list, or NULL. Only used with MQTT * v5 clients. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_MALFORMED_UTF8 - if a topic is not valid UTF-8 * MOSQ_ERR_OVERSIZE_PACKET - if the resulting packet would be larger than * supported by the broker. */ libmosq_EXPORT int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties); /* ====================================================================== * * Section: Struct mosquitto_message helper functions * * ====================================================================== */ /* * Function: mosquitto_message_copy * * Copy the contents of a mosquitto message to another message. * Useful for preserving a message received in the on_message() callback. * * Parameters: * dst - a pointer to a valid mosquitto_message struct to copy to. * src - a pointer to a valid mosquitto_message struct to copy from. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * * See Also: * */ libmosq_EXPORT int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src); /* * Function: mosquitto_message_free * * Completely free a mosquitto_message struct. * * Parameters: * message - pointer to a mosquitto_message pointer to free. * * See Also: * , */ libmosq_EXPORT void mosquitto_message_free(struct mosquitto_message **message); /* * Function: mosquitto_message_free_contents * * Free a mosquitto_message struct contents, leaving the struct unaffected. * * Parameters: * message - pointer to a mosquitto_message struct to free its contents. * * See Also: * , */ libmosq_EXPORT void mosquitto_message_free_contents(struct mosquitto_message *message); /* ====================================================================== * * Section: Network loop (managed by libmosquitto) * * The internal network loop must be called at a regular interval. The two * recommended approaches are to use either or * . is a blocking call and is * suitable for the situation where you only want to handle incoming messages * in callbacks. is a non-blocking call, it creates a * separate thread to run the loop for you. Use this function when you have * other tasks you need to run at the same time as the MQTT client, e.g. * reading data from a sensor. * * ====================================================================== */ /* * Function: mosquitto_loop_forever * * This function call loop() for you in an infinite blocking loop. It is useful * for the case where you only want to run the MQTT client loop in your * program. * * It handles reconnecting in case server connection is lost. If you call * mosquitto_disconnect() in a callback it will return. * * Parameters: * mosq - a valid mosquitto instance. * timeout - Maximum number of milliseconds to wait for network activity * in the select() call before timing out. Set to 0 for instant * return. Set negative to use the default of 1000ms. * max_packets - this parameter is currently unused and should be set to 1 for * future compatibility. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the * broker. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , */ libmosq_EXPORT int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets); /* * Function: mosquitto_loop_start * * This is part of the threaded client interface. Call this once to start a new * thread to process network traffic. This provides an alternative to * repeatedly calling yourself. * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOT_SUPPORTED - if thread support is not available. * * See Also: * , , , */ libmosq_EXPORT int mosquitto_loop_start(struct mosquitto *mosq); /* * Function: mosquitto_loop_stop * * This is part of the threaded client interface. Call this once to stop the * network thread previously created with . This call * will block until the network thread finishes. For the network thread to end, * you must have previously called or have set the force * parameter to true. * * Parameters: * mosq - a valid mosquitto instance. * force - set to true to force thread cancellation. If false, * must have already been called. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOT_SUPPORTED - if thread support is not available. * * See Also: * , */ libmosq_EXPORT int mosquitto_loop_stop(struct mosquitto *mosq, bool force); /* * Function: mosquitto_loop * * The main network loop for the client. This must be called frequently * to keep communications between the client and broker working. This is * carried out by and , which * are the recommended ways of handling the network loop. You may also use this * function if you wish. It must not be called inside a callback. * * If incoming data is present it will then be processed. Outgoing commands, * from e.g. , are normally sent immediately that their * function is called, but this is not always possible. will * also attempt to send any remaining outgoing messages, which also includes * commands that are part of the flow for messages with QoS>0. * * This calls select() to monitor the client network socket. If you want to * integrate mosquitto client operation with your own select() call, use * , , and * . * * Threads: * * Parameters: * mosq - a valid mosquitto instance. * timeout - Maximum number of milliseconds to wait for network activity * in the select() call before timing out. Set to 0 for instant * return. Set negative to use the default of 1000ms. * max_packets - this parameter is currently unused and should be set to 1 for * future compatibility. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the * broker. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * See Also: * , , */ libmosq_EXPORT int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets); /* ====================================================================== * * Section: Network loop (for use in other event loops) * * ====================================================================== */ /* * Function: mosquitto_loop_read * * Carry out network read operations. * This should only be used if you are not using mosquitto_loop() and are * monitoring the client network socket for activity yourself. * * Parameters: * mosq - a valid mosquitto instance. * max_packets - this parameter is currently unused and should be set to 1 for * future compatibility. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the * broker. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , */ libmosq_EXPORT int mosquitto_loop_read(struct mosquitto *mosq, int max_packets); /* * Function: mosquitto_loop_write * * Carry out network write operations. * This should only be used if you are not using mosquitto_loop() and are * monitoring the client network socket for activity yourself. * * Parameters: * mosq - a valid mosquitto instance. * max_packets - this parameter is currently unused and should be set to 1 for * future compatibility. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * MOSQ_ERR_CONN_LOST - if the connection to the broker was lost. * MOSQ_ERR_PROTOCOL - if there is a protocol error communicating with the * broker. * MOSQ_ERR_ERRNO - if a system call returned an error. The variable errno * contains the error code, even on Windows. * Use strerror_r() where available or FormatMessage() on * Windows. * * See Also: * , , , */ libmosq_EXPORT int mosquitto_loop_write(struct mosquitto *mosq, int max_packets); /* * Function: mosquitto_loop_misc * * Carry out miscellaneous operations required as part of the network loop. * This should only be used if you are not using mosquitto_loop() and are * monitoring the client network socket for activity yourself. * * This function deals with handling PINGs and checking whether messages need * to be retried, so should be called fairly frequently, around once per second * is sufficient. * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NO_CONN - if the client isn't connected to a broker. * * See Also: * , , */ libmosq_EXPORT int mosquitto_loop_misc(struct mosquitto *mosq); /* ====================================================================== * * Section: Network loop (helper functions) * * ====================================================================== */ /* * Function: mosquitto_socket * * Return the socket handle for a mosquitto instance. Useful if you want to * include a mosquitto client in your own select() calls. * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * The socket for the mosquitto client or -1 on failure. */ libmosq_EXPORT int mosquitto_socket(struct mosquitto *mosq); /* * Function: mosquitto_want_write * * Returns true if there is data ready to be written on the socket. * * Parameters: * mosq - a valid mosquitto instance. * * See Also: * , , */ libmosq_EXPORT bool mosquitto_want_write(struct mosquitto *mosq); /* * Function: mosquitto_threaded_set * * Used to tell the library that your application is using threads, but not * using . The library operates slightly differently when * not in threaded mode in order to simplify its operation. If you are managing * your own threads and do not use this function you will experience crashes * due to race conditions. * * When using , this is set automatically. * * Parameters: * mosq - a valid mosquitto instance. * threaded - true if your application is using threads, false otherwise. */ libmosq_EXPORT int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded); /* ====================================================================== * * Section: Client options * * ====================================================================== */ /* * Function: mosquitto_opts_set * * Used to set options for the client. * * This function is deprecated, the replacement , * and functions should * be used instead. * * Parameters: * mosq - a valid mosquitto instance. * option - the option to set. * value - the option specific value. * * Options: * MOSQ_OPT_PROTOCOL_VERSION - Value must be an int, set to either * MQTT_PROTOCOL_V31 or MQTT_PROTOCOL_V311. Must be set * before the client connects. * Defaults to MQTT_PROTOCOL_V31. * * MOSQ_OPT_SSL_CTX - Pass an openssl SSL_CTX to be used when creating * TLS connections rather than libmosquitto creating its own. * This must be called before connecting to have any effect. * If you use this option, the onus is on you to ensure that * you are using secure settings. * Setting to NULL means that libmosquitto will use its own SSL_CTX * if TLS is to be used. * This option is only available for openssl 1.1.0 and higher. * * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS - Value must be an int set to 1 or 0. * If set to 1, then the user specified SSL_CTX passed in using * MOSQ_OPT_SSL_CTX will have the default options applied to it. * This means that you only need to change the values that are * relevant to you. If you use this option then you must configure * the TLS options as normal, i.e. you should use * to configure the cafile/capath as a minimum. * This option is only available for openssl 1.1.0 and higher. */ libmosq_EXPORT int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value); /* * Function: mosquitto_int_option * * Used to set integer options for the client. * * Parameters: * mosq - a valid mosquitto instance. * option - the option to set. * value - the option specific value. * * Options: * MOSQ_OPT_TCP_NODELAY - Set to 1 to disable Nagle's algorithm on client * sockets. This has the effect of reducing latency of individual * messages at the potential cost of increasing the number of * packets being sent. * Defaults to 0, which means Nagle remains enabled. * * MOSQ_OPT_PROTOCOL_VERSION - Value must be set to either MQTT_PROTOCOL_V31, * MQTT_PROTOCOL_V311, or MQTT_PROTOCOL_V5. Must be set before the * client connects. Defaults to MQTT_PROTOCOL_V311. * * MOSQ_OPT_RECEIVE_MAXIMUM - Value can be set between 1 and 65535 inclusive, * and represents the maximum number of incoming QoS 1 and QoS 2 * messages that this client wants to process at once. Defaults to * 20. This option is not valid for MQTT v3.1 or v3.1.1 clients. * Note that if the MQTT_PROP_RECEIVE_MAXIMUM property is in the * proplist passed to mosquitto_connect_v5(), then that property * will override this option. Using this option is the recommended * method however. * * MOSQ_OPT_SEND_MAXIMUM - Value can be set between 1 and 65535 inclusive, * and represents the maximum number of outgoing QoS 1 and QoS 2 * messages that this client will attempt to have "in flight" at * once. Defaults to 20. * This option is not valid for MQTT v3.1 or v3.1.1 clients. * Note that if the broker being connected to sends a * MQTT_PROP_RECEIVE_MAXIMUM property that has a lower value than * this option, then the broker provided value will be used. * * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS - If value is set to a non zero value, * then the user specified SSL_CTX passed in using MOSQ_OPT_SSL_CTX * will have the default options applied to it. This means that * you only need to change the values that are relevant to you. * If you use this option then you must configure the TLS options * as normal, i.e. you should use to * configure the cafile/capath as a minimum. * This option is only available for openssl 1.1.0 and higher. * * MOSQ_OPT_TLS_OCSP_REQUIRED - Set whether OCSP checking on TLS * connections is required. Set to 1 to enable checking, * or 0 (the default) for no checking. * * MOSQ_OPT_TLS_USE_OS_CERTS - Set to 1 to instruct the client to load and * trust OS provided CA certificates for use with TLS connections. * Set to 0 (the default) to only use manually specified CA certs. */ libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value); /* * Function: mosquitto_string_option * * Used to set const char* options for the client. * * Parameters: * mosq - a valid mosquitto instance. * option - the option to set. * value - the option specific value. * * Options: * MOSQ_OPT_TLS_ENGINE - Configure the client for TLS Engine support. * Pass a TLS Engine ID to be used when creating TLS * connections. Must be set before . * Must be a valid engine, and note that the string will not be used * until a connection attempt is made so this function will return * success even if an invalid engine string is passed. * * MOSQ_OPT_TLS_KEYFORM - Configure the client to treat the keyfile * differently depending on its type. Must be set * before . * Set as either "pem" or "engine", to determine from where the * private key for a TLS connection will be obtained. Defaults to * "pem", a normal private key file. * * MOSQ_OPT_TLS_KPASS_SHA1 - Where the TLS Engine requires the use of * a password to be accessed, this option allows a hex encoded * SHA1 hash of the private key password to be passed to the * engine directly. Must be set before . * * MOSQ_OPT_TLS_ALPN - If the broker being connected to has multiple * services available on a single TLS port, such as both MQTT * and WebSockets, use this option to configure the ALPN * option for the connection. * * MOSQ_OPT_BIND_ADDRESS - Set the hostname or ip address of the local network * interface to bind to when connecting. */ libmosq_EXPORT int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value); /* * Function: mosquitto_void_option * * Used to set void* options for the client. * * Parameters: * mosq - a valid mosquitto instance. * option - the option to set. * value - the option specific value. * * Options: * MOSQ_OPT_SSL_CTX - Pass an openssl SSL_CTX to be used when creating TLS * connections rather than libmosquitto creating its own. This must * be called before connecting to have any effect. If you use this * option, the onus is on you to ensure that you are using secure * settings. * Setting to NULL means that libmosquitto will use its own SSL_CTX * if TLS is to be used. * This option is only available for openssl 1.1.0 and higher. */ libmosq_EXPORT int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value); /* * Function: mosquitto_reconnect_delay_set * * Control the behaviour of the client when it has unexpectedly disconnected in * or after . The default * behaviour if this function is not used is to repeatedly attempt to reconnect * with a delay of 1 second until the connection succeeds. * * Use reconnect_delay parameter to change the delay between successive * reconnection attempts. You may also enable exponential backoff of the time * between reconnections by setting reconnect_exponential_backoff to true and * set an upper bound on the delay with reconnect_delay_max. * * Example 1: * delay=2, delay_max=10, exponential_backoff=False * Delays would be: 2, 4, 6, 8, 10, 10, ... * * Example 2: * delay=3, delay_max=30, exponential_backoff=True * Delays would be: 3, 6, 12, 24, 30, 30, ... * * Parameters: * mosq - a valid mosquitto instance. * reconnect_delay - the number of seconds to wait between * reconnects. * reconnect_delay_max - the maximum number of seconds to wait * between reconnects. * reconnect_exponential_backoff - use exponential backoff between * reconnect attempts. Set to true to enable * exponential backoff. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. */ libmosq_EXPORT int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); /* * Function: mosquitto_max_inflight_messages_set * * This function is deprected. Use the function with the * MOSQ_OPT_SEND_MAXIMUM option instead. * * Set the number of QoS 1 and 2 messages that can be "in flight" at one time. * An in flight message is part way through its delivery flow. Attempts to send * further messages with will result in the messages being * queued until the number of in flight messages reduces. * * A higher number here results in greater message throughput, but if set * higher than the maximum in flight messages on the broker may lead to * delays in the messages being acknowledged. * * Set to 0 for no maximum. * * Parameters: * mosq - a valid mosquitto instance. * max_inflight_messages - the maximum number of inflight messages. Defaults * to 20. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. */ libmosq_EXPORT int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages); /* * Function: mosquitto_message_retry_set * * This function now has no effect. */ libmosq_EXPORT void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry); /* * Function: mosquitto_user_data_set * * When is called, the pointer given as the "obj" parameter * will be passed to the callbacks as user data. The * function allows this obj parameter to be updated at any time. This function * will not modify the memory pointed to by the current user data pointer. If * it is dynamically allocated memory you must free it yourself. * * Parameters: * mosq - a valid mosquitto instance. * obj - A user pointer that will be passed as an argument to any callbacks * that are specified. */ libmosq_EXPORT void mosquitto_user_data_set(struct mosquitto *mosq, void *obj); /* Function: mosquitto_userdata * * Retrieve the "userdata" variable for a mosquitto client. * * Parameters: * mosq - a valid mosquitto instance. * * Returns: * A pointer to the userdata member variable. */ libmosq_EXPORT void *mosquitto_userdata(struct mosquitto *mosq); /* ====================================================================== * * Section: TLS support * * ====================================================================== */ /* * Function: mosquitto_tls_set * * Configure the client for certificate based SSL/TLS support. Must be called * before . * * Cannot be used in conjunction with . * * Define the Certificate Authority certificates to be trusted (ie. the server * certificate must be signed with one of these certificates) using cafile. * * If the server you are connecting to requires clients to provide a * certificate, define certfile and keyfile with your client certificate and * private key. If your private key is encrypted, provide a password callback * function or you will have to enter the password at the command line. * * Parameters: * mosq - a valid mosquitto instance. * cafile - path to a file containing the PEM encoded trusted CA * certificate files. Either cafile or capath must not be NULL. * capath - path to a directory containing the PEM encoded trusted CA * certificate files. See mosquitto.conf for more details on * configuring this directory. Either cafile or capath must not * be NULL. * certfile - path to a file containing the PEM encoded certificate file * for this client. If NULL, keyfile must also be NULL and no * client certificate will be used. * keyfile - path to a file containing the PEM encoded private key for * this client. If NULL, certfile must also be NULL and no * client certificate will be used. * pw_callback - if keyfile is encrypted, set pw_callback to allow your client * to pass the correct password for decryption. If set to NULL, * the password must be entered on the command line. * Your callback must write the password into "buf", which is * "size" bytes long. The return value must be the length of the * password. "userdata" will be set to the calling mosquitto * instance. The mosquitto userdata member variable can be * retrieved using . * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * * See Also: * , , * , */ libmosq_EXPORT int 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)); /* * Function: mosquitto_tls_insecure_set * * Configure verification of the server hostname in the server certificate. If * value is set to true, it is impossible to guarantee that the host you are * connecting to is not impersonating your server. This can be useful in * initial server testing, but makes it possible for a malicious third party to * impersonate your server through DNS spoofing, for example. * Do not use this function in a real system. Setting value to true makes the * connection encryption pointless. * Must be called before . * * Parameters: * mosq - a valid mosquitto instance. * value - if set to false, the default, certificate hostname checking is * performed. If set to true, no hostname checking is performed and * the connection is insecure. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * * See Also: * */ libmosq_EXPORT int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value); /* * Function: mosquitto_tls_opts_set * * Set advanced SSL/TLS options. Must be called before . * * Parameters: * mosq - a valid mosquitto instance. * cert_reqs - an integer defining the verification requirements the client * will impose on the server. This can be one of: * * SSL_VERIFY_NONE (0): the server will not be verified in any way. * * SSL_VERIFY_PEER (1): the server certificate will be verified * and the connection aborted if the verification fails. * The default and recommended value is SSL_VERIFY_PEER. Using * SSL_VERIFY_NONE provides no security. * tls_version - the version of the SSL/TLS protocol to use as a string. If NULL, * the default value is used. The default value and the * available values depend on the version of openssl that the * library was compiled against. For openssl >= 1.0.1, the * available options are tlsv1.2, tlsv1.1 and tlsv1, with tlv1.2 * as the default. For openssl < 1.0.1, only tlsv1 is available. * ciphers - a string describing the ciphers available for use. See the * "openssl ciphers" tool for more information. If NULL, the * default ciphers will be used. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * * See Also: * */ libmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers); /* * Function: mosquitto_tls_psk_set * * Configure the client for pre-shared-key based TLS support. Must be called * before . * * Cannot be used in conjunction with . * * Parameters: * mosq - a valid mosquitto instance. * psk - the pre-shared-key in hex format with no leading "0x". * identity - the identity of this client. May be used as the username * depending on the server settings. * ciphers - a string describing the PSK ciphers available for use. See the * "openssl ciphers" tool for more information. If NULL, the * default ciphers will be used. * * Returns: * MOSQ_ERR_SUCCESS - on success. * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * * See Also: * */ libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers); /* * Function: mosquitto_ssl_get * * Retrieve a pointer to the SSL structure used for TLS connections in this * client. This can be used in e.g. the connect callback to carry out * additional verification steps. * * Parameters: * mosq - a valid mosquitto instance * * Returns: * A valid pointer to an openssl SSL structure - if the client is using TLS. * NULL - if the client is not using TLS, or TLS support is not compiled in. */ libmosq_EXPORT void *mosquitto_ssl_get(struct mosquitto *mosq); /* ====================================================================== * * Section: Callbacks * * ====================================================================== */ /* * Function: mosquitto_connect_callback_set * * Set the connect callback. This is called when the library receives a CONNACK * message in response to a connection. * * Parameters: * mosq - a valid mosquitto instance. * on_connect - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int rc) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * rc - the return code of the connection response. The values are defined by * the MQTT protocol version in use. * 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 * 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 */ libmosq_EXPORT void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)); /* * Function: mosquitto_connect_with_flags_callback_set * * Set the connect callback. This is called when the library receives a CONNACK * message in response to a connection. * * Parameters: * mosq - a valid mosquitto instance. * on_connect - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int rc) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * rc - the return code of the connection response. The values are defined by * the MQTT protocol version in use. * 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 * 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 * flags - the connect flags. */ libmosq_EXPORT void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int)); /* * Function: mosquitto_connect_v5_callback_set * * Set the connect callback. This is called when the library receives a CONNACK * message in response to a connection. * * It is valid to set this callback for all MQTT protocol versions. If it is * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` * argument will always be NULL. * * Parameters: * mosq - a valid mosquitto instance. * on_connect - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int rc) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * rc - the return code of the connection response. The values are defined by * the MQTT protocol version in use. * 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 * 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 * flags - the connect flags. * props - list of MQTT 5 properties, or NULL * */ libmosq_EXPORT void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); /* * Function: mosquitto_disconnect_callback_set * * Set the disconnect callback. This is called when the broker has received the * DISCONNECT command and has disconnected the client. * * Parameters: * mosq - a valid mosquitto instance. * on_disconnect - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * rc - integer value indicating the reason for the disconnect. A value of 0 * means the client has called . Any other value * indicates that the disconnect is unexpected. */ libmosq_EXPORT void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)); /* * Function: mosquitto_disconnect_v5_callback_set * * Set the disconnect callback. This is called when the broker has received the * DISCONNECT command and has disconnected the client. * * It is valid to set this callback for all MQTT protocol versions. If it is * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` * argument will always be NULL. * * Parameters: * mosq - a valid mosquitto instance. * on_disconnect - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * rc - integer value indicating the reason for the disconnect. A value of 0 * means the client has called . Any other value * indicates that the disconnect is unexpected. * props - list of MQTT 5 properties, or NULL */ libmosq_EXPORT void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *props)); /* * Function: mosquitto_publish_callback_set * * Set the publish callback. This is called when a message initiated with * has been sent to the broker. "Sent" means different * things depending on the QoS of the message: * * QoS 0: The PUBLISH was passed to the local operating system for delivery, * there is no guarantee that it was delivered to the remote broker. * QoS 1: The PUBLISH was sent to the remote broker and the corresponding * PUBACK was received by the library. * QoS 2: The PUBLISH was sent to the remote broker and the corresponding * PUBCOMP was received by the library. * * Parameters: * mosq - a valid mosquitto instance. * on_publish - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int mid) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * mid - the message id of the sent message. */ libmosq_EXPORT void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)); /* * Function: mosquitto_publish_v5_callback_set * * Set the publish callback. This is called when a message initiated with * has been sent to the broker. This callback will be * called both if the message is sent successfully, or if the broker responded * with an error, which will be reflected in the reason_code parameter. * "Sent" means different things depending on the QoS of the message: * * QoS 0: The PUBLISH was passed to the local operating system for delivery, * there is no guarantee that it was delivered to the remote broker. * QoS 1: The PUBLISH was sent to the remote broker and the corresponding * PUBACK was received by the library. * QoS 2: The PUBLISH was sent to the remote broker and the corresponding * PUBCOMP was received by the library. * * * It is valid to set this callback for all MQTT protocol versions. If it is * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` * argument will always be NULL. * * Parameters: * mosq - a valid mosquitto instance. * on_publish - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int mid) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * mid - the message id of the sent message. * reason_code - the MQTT 5 reason code * props - list of MQTT 5 properties, or NULL */ libmosq_EXPORT void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props)); /* * Function: mosquitto_message_callback_set * * Set the message callback. This is called when a message is received from the * broker and the required QoS flow has completed. * * Parameters: * mosq - a valid mosquitto instance. * on_message - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * message - the message data. This variable and associated memory will be * freed by the library after the callback completes. The client * should make copies of any of the data it requires. * * See Also: * */ libmosq_EXPORT void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)); /* * Function: mosquitto_message_v5_callback_set * * Set the message callback. This is called when a message is received from the * broker and the required QoS flow has completed. * * It is valid to set this callback for all MQTT protocol versions. If it is * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` * argument will always be NULL. * * Parameters: * mosq - a valid mosquitto instance. * on_message - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * message - the message data. This variable and associated memory will be * freed by the library after the callback completes. The client * should make copies of any of the data it requires. * props - list of MQTT 5 properties, or NULL * * See Also: * */ libmosq_EXPORT void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props)); /* * Function: mosquitto_subscribe_callback_set * * Set the subscribe callback. This is called when the library receives a * SUBACK message in response to a SUBSCRIBE. * * Parameters: * mosq - a valid mosquitto instance. * on_subscribe - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * mid - the message id of the subscribe message. * qos_count - the number of granted subscriptions (size of granted_qos). * granted_qos - an array of integers indicating the granted QoS for each of * the subscriptions. */ libmosq_EXPORT void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)); /* * Function: mosquitto_subscribe_v5_callback_set * * Set the subscribe callback. This is called when the library receives a * SUBACK message in response to a SUBSCRIBE. * * It is valid to set this callback for all MQTT protocol versions. If it is * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` * argument will always be NULL. * * Parameters: * mosq - a valid mosquitto instance. * on_subscribe - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * mid - the message id of the subscribe message. * qos_count - the number of granted subscriptions (size of granted_qos). * granted_qos - an array of integers indicating the granted QoS for each of * the subscriptions. * props - list of MQTT 5 properties, or NULL */ libmosq_EXPORT void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props)); /* * Function: mosquitto_unsubscribe_callback_set * * Set the unsubscribe callback. This is called when the library receives a * UNSUBACK message in response to an UNSUBSCRIBE. * * Parameters: * mosq - a valid mosquitto instance. * on_unsubscribe - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int mid) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * mid - the message id of the unsubscribe message. */ libmosq_EXPORT void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)); /* * Function: mosquitto_unsubscribe_v5_callback_set * * Set the unsubscribe callback. This is called when the library receives a * UNSUBACK message in response to an UNSUBSCRIBE. * * It is valid to set this callback for all MQTT protocol versions. If it is * used with MQTT clients that use MQTT v3.1.1 or earlier, then the `props` * argument will always be NULL. * * Parameters: * mosq - a valid mosquitto instance. * on_unsubscribe - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int mid) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * mid - the message id of the unsubscribe message. * props - list of MQTT 5 properties, or NULL */ libmosq_EXPORT void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props)); /* * Function: mosquitto_log_callback_set * * Set the logging callback. This should be used if you want event logging * information from the client library. * * mosq - a valid mosquitto instance. * on_log - a callback function in the following form: * void callback(struct mosquitto *mosq, void *obj, int level, const char *str) * * Callback Parameters: * mosq - the mosquitto instance making the callback. * obj - the user data provided in * level - the log message level from the values: * MOSQ_LOG_INFO * MOSQ_LOG_NOTICE * MOSQ_LOG_WARNING * MOSQ_LOG_ERR * MOSQ_LOG_DEBUG * str - the message string. */ libmosq_EXPORT void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)); /* ============================================================================= * * Section: SOCKS5 proxy functions * * ============================================================================= */ /* * Function: mosquitto_socks5_set * * Configure the client to use a SOCKS5 proxy when connecting. Must be called * before connecting. "None" and "username/password" authentication is * supported. * * Parameters: * mosq - a valid mosquitto instance. * host - the SOCKS5 proxy host to connect to. * port - the SOCKS5 proxy port to use. * username - if not NULL, use this username when authenticating with the proxy. * password - if not NULL and username is not NULL, use this password when * authenticating with the proxy. */ libmosq_EXPORT int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password); /* ============================================================================= * * Section: Utility functions * * ============================================================================= */ /* * Function: mosquitto_strerror * * Call to obtain a const string description of a mosquitto error number. * * Parameters: * mosq_errno - a mosquitto error number. * * Returns: * A constant string describing the error. */ libmosq_EXPORT const char *mosquitto_strerror(int mosq_errno); /* * Function: mosquitto_connack_string * * Call to obtain a const string description of an MQTT connection result. * * Parameters: * connack_code - an MQTT connection result. * * Returns: * A constant string describing the result. */ libmosq_EXPORT const char *mosquitto_connack_string(int connack_code); /* * Function: mosquitto_reason_string * * Call to obtain a const string description of an MQTT reason code. * * Parameters: * reason_code - an MQTT reason code. * * Returns: * A constant string describing the reason. */ libmosq_EXPORT const char *mosquitto_reason_string(int reason_code); /* Function: mosquitto_string_to_command * * Take a string input representing an MQTT command and convert it to the * libmosquitto integer representation. * * Parameters: * str - the string to parse. * cmd - pointer to an int, for the result. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - on an invalid input. * * Example: * (start code) * mosquitto_string_to_command("CONNECT", &cmd); * // cmd == CMD_CONNECT * (end) */ libmosq_EXPORT int mosquitto_string_to_command(const char *str, int *cmd); /* * Function: mosquitto_sub_topic_tokenise * * Tokenise a topic or subscription string into an array of strings * representing the topic hierarchy. * * For example: * * subtopic: "a/deep/topic/hierarchy" * * Would result in: * * topics[0] = "a" * topics[1] = "deep" * topics[2] = "topic" * topics[3] = "hierarchy" * * and: * * subtopic: "/a/deep/topic/hierarchy/" * * Would result in: * * topics[0] = NULL * topics[1] = "a" * topics[2] = "deep" * topics[3] = "topic" * topics[4] = "hierarchy" * * Parameters: * subtopic - the subscription/topic to tokenise * topics - a pointer to store the array of strings * count - an int pointer to store the number of items in the topics array. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_NOMEM - if an out of memory condition occurred. * MOSQ_ERR_MALFORMED_UTF8 - if the topic is not valid UTF-8 * * Example: * * > char **topics; * > int topic_count; * > int i; * > * > mosquitto_sub_topic_tokenise("$SYS/broker/uptime", &topics, &topic_count); * > * > for(i=0; i printf("%d: %s\n", i, topics[i]); * > } * * See Also: * */ libmosq_EXPORT int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count); /* * Function: mosquitto_sub_topic_tokens_free * * Free memory that was allocated in . * * Parameters: * topics - pointer to string array. * count - count of items in string array. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if the input parameters were invalid. * * See Also: * */ libmosq_EXPORT int mosquitto_sub_topic_tokens_free(char ***topics, int count); /* * Function: mosquitto_topic_matches_sub * * Check whether a topic matches a subscription. * * For example: * * foo/bar would match the subscription foo/# or +/bar * non/matching would not match the subscription non/+/+ * * Parameters: * sub - subscription string to check topic against. * topic - topic to check. * result - bool pointer to hold result. Will be set to true if the topic * matches the subscription. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. */ libmosq_EXPORT int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result); /* * Function: mosquitto_topic_matches_sub2 * * Check whether a topic matches a subscription. * * For example: * * foo/bar would match the subscription foo/# or +/bar * non/matching would not match the subscription non/+/+ * * Parameters: * sub - subscription string to check topic against. * sublen - length in bytes of sub string * topic - topic to check. * topiclen - length in bytes of topic string * result - bool pointer to hold result. Will be set to true if the topic * matches the subscription. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if the input parameters were invalid. * MOSQ_ERR_NOMEM - if an out of memory condition occurred. */ libmosq_EXPORT int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result); /* * Function: mosquitto_pub_topic_check * * Check whether a topic to be used for publishing is valid. * * This searches for + or # in a topic and checks its length. * * This check is already carried out in and * , there is no need to call it directly before them. It * may be useful if you wish to check the validity of a topic in advance of * making a connection for example. * * Parameters: * topic - the topic to check * * Returns: * MOSQ_ERR_SUCCESS - for a valid topic * MOSQ_ERR_INVAL - if the topic contains a + or a #, or if it is too long. * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 * * See Also: * */ libmosq_EXPORT int mosquitto_pub_topic_check(const char *topic); /* * Function: mosquitto_pub_topic_check2 * * Check whether a topic to be used for publishing is valid. * * This searches for + or # in a topic and checks its length. * * This check is already carried out in and * , there is no need to call it directly before them. It * may be useful if you wish to check the validity of a topic in advance of * making a connection for example. * * Parameters: * topic - the topic to check * topiclen - length of the topic in bytes * * Returns: * MOSQ_ERR_SUCCESS - for a valid topic * MOSQ_ERR_INVAL - if the topic contains a + or a #, or if it is too long. * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 * * See Also: * */ libmosq_EXPORT int mosquitto_pub_topic_check2(const char *topic, size_t topiclen); /* * Function: mosquitto_sub_topic_check * * Check whether a topic to be used for subscribing is valid. * * This searches for + or # in a topic and checks that they aren't in invalid * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its * length. * * This check is already carried out in and * , there is no need to call it directly before them. * It may be useful if you wish to check the validity of a topic in advance of * making a connection for example. * * Parameters: * topic - the topic to check * * Returns: * MOSQ_ERR_SUCCESS - for a valid topic * MOSQ_ERR_INVAL - if the topic contains a + or a # that is in an * invalid position, or if it is too long. * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 * * See Also: * */ libmosq_EXPORT int mosquitto_sub_topic_check(const char *topic); /* * Function: mosquitto_sub_topic_check2 * * Check whether a topic to be used for subscribing is valid. * * This searches for + or # in a topic and checks that they aren't in invalid * positions, such as with foo/#/bar, foo/+bar or foo/bar#, and checks its * length. * * This check is already carried out in and * , there is no need to call it directly before them. * It may be useful if you wish to check the validity of a topic in advance of * making a connection for example. * * Parameters: * topic - the topic to check * topiclen - the length in bytes of the topic * * Returns: * MOSQ_ERR_SUCCESS - for a valid topic * MOSQ_ERR_INVAL - if the topic contains a + or a # that is in an * invalid position, or if it is too long. * MOSQ_ERR_MALFORMED_UTF8 - if topic is not valid UTF-8 * * See Also: * */ libmosq_EXPORT int mosquitto_sub_topic_check2(const char *topic, size_t topiclen); /* * Function: mosquitto_validate_utf8 * * Helper function to validate whether a UTF-8 string is valid, according to * the UTF-8 spec and the MQTT additions. * * Parameters: * str - a string to check * len - the length of the string in bytes * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if str is NULL or len<0 or len>65536 * MOSQ_ERR_MALFORMED_UTF8 - if str is not valid UTF-8 */ libmosq_EXPORT int mosquitto_validate_utf8(const char *str, int len); /* ============================================================================= * * Section: One line client helper functions * * ============================================================================= */ struct libmosquitto_will { char *topic; void *payload; int payloadlen; int qos; bool retain; }; struct libmosquitto_auth { char *username; char *password; }; struct libmosquitto_tls { char *cafile; char *capath; char *certfile; char *keyfile; char *ciphers; char *tls_version; int (*pw_callback)(char *buf, int size, int rwflag, void *userdata); int cert_reqs; }; /* * Function: mosquitto_subscribe_simple * * Helper function to make subscribing to a topic and retrieving some messages * very straightforward. * * This connects to a broker, subscribes to a topic, waits for msg_count * messages to be received, then returns after disconnecting cleanly. * * Parameters: * messages - pointer to a "struct mosquitto_message *". The received * messages will be returned here. On error, this will be set to * NULL. * msg_count - the number of messages to retrieve. * want_retained - if set to true, stale retained messages will be treated as * normal messages with regards to msg_count. If set to * false, they will be ignored. * topic - the subscription topic to use (wildcards are allowed). * qos - the qos to use for the subscription. * host - the broker to connect to. * port - the network port the broker is listening on. * client_id - the client id to use, or NULL if a random client id should be * generated. * keepalive - the MQTT keepalive value. * clean_session - the MQTT clean session flag. * username - the username string, or NULL for no username authentication. * password - the password string, or NULL for an empty password. * will - a libmosquitto_will struct containing will information, or NULL for * no will. * tls - a libmosquitto_tls struct containing TLS related parameters, or NULL * for no use of TLS. * * * Returns: * MOSQ_ERR_SUCCESS - on success * Greater than 0 - on error. */ libmosq_EXPORT int mosquitto_subscribe_simple( struct mosquitto_message **messages, int msg_count, bool want_retained, const char *topic, int qos, const char *host, int port, const char *client_id, int keepalive, bool clean_session, const char *username, const char *password, const struct libmosquitto_will *will, const struct libmosquitto_tls *tls); /* * Function: mosquitto_subscribe_callback * * Helper function to make subscribing to a topic and processing some messages * very straightforward. * * This connects to a broker, subscribes to a topic, then passes received * messages to a user provided callback. If the callback returns a 1, it then * disconnects cleanly and returns. * * Parameters: * callback - a callback function in the following form: * int callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) * Note that this is the same as the normal on_message callback, * except that it returns an int. * userdata - user provided pointer that will be passed to the callback. * topic - the subscription topic to use (wildcards are allowed). * qos - the qos to use for the subscription. * host - the broker to connect to. * port - the network port the broker is listening on. * client_id - the client id to use, or NULL if a random client id should be * generated. * keepalive - the MQTT keepalive value. * clean_session - the MQTT clean session flag. * username - the username string, or NULL for no username authentication. * password - the password string, or NULL for an empty password. * will - a libmosquitto_will struct containing will information, or NULL for * no will. * tls - a libmosquitto_tls struct containing TLS related parameters, or NULL * for no use of TLS. * * * Returns: * MOSQ_ERR_SUCCESS - on success * Greater than 0 - on error. */ libmosq_EXPORT int mosquitto_subscribe_callback( int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *), void *userdata, const char *topic, int qos, const char *host, int port, const char *client_id, int keepalive, bool clean_session, const char *username, const char *password, const struct libmosquitto_will *will, const struct libmosquitto_tls *tls); /* ============================================================================= * * Section: Properties * * ============================================================================= */ /* * Function: mosquitto_property_add_byte * * Add a new byte property to a property list. * * If *proplist == NULL, a new list will be created, otherwise the new property * will be appended to the list. * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - integer value for the new property * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL * MOSQ_ERR_NOMEM - on out of memory * * Example: * > mosquitto_property *proplist = NULL; * > mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_IDENTIFIER, 1); */ libmosq_EXPORT int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value); /* * Function: mosquitto_property_add_int16 * * Add a new int16 property to a property list. * * If *proplist == NULL, a new list will be created, otherwise the new property * will be appended to the list. * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_RECEIVE_MAXIMUM) * value - integer value for the new property * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL * MOSQ_ERR_NOMEM - on out of memory * * Example: * > mosquitto_property *proplist = NULL; * > mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1000); */ libmosq_EXPORT int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value); /* * Function: mosquitto_property_add_int32 * * Add a new int32 property to a property list. * * If *proplist == NULL, a new list will be created, otherwise the new property * will be appended to the list. * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_MESSAGE_EXPIRY_INTERVAL) * value - integer value for the new property * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL * MOSQ_ERR_NOMEM - on out of memory * * Example: * > mosquitto_property *proplist = NULL; * > mosquitto_property_add_int32(&proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 86400); */ libmosq_EXPORT int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value); /* * Function: mosquitto_property_add_varint * * Add a new varint property to a property list. * * If *proplist == NULL, a new list will be created, otherwise the new property * will be appended to the list. * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_SUBSCRIPTION_IDENTIFIER) * value - integer value for the new property * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL * MOSQ_ERR_NOMEM - on out of memory * * Example: * > mosquitto_property *proplist = NULL; * > mosquitto_property_add_varint(&proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 1); */ libmosq_EXPORT int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value); /* * Function: mosquitto_property_add_binary * * Add a new binary property to a property list. * * If *proplist == NULL, a new list will be created, otherwise the new property * will be appended to the list. * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - pointer to the property data * len - length of property data in bytes * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if identifier is invalid, or if proplist is NULL * MOSQ_ERR_NOMEM - on out of memory * * Example: * > mosquitto_property *proplist = NULL; * > mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len); */ libmosq_EXPORT int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len); /* * Function: mosquitto_property_add_string * * Add a new string property to a property list. * * If *proplist == NULL, a new list will be created, otherwise the new property * will be appended to the list. * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_CONTENT_TYPE) * value - string value for the new property, must be UTF-8 and zero terminated * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if identifier is invalid, if value is NULL, or if proplist is NULL * MOSQ_ERR_NOMEM - on out of memory * MOSQ_ERR_MALFORMED_UTF8 - value is not valid UTF-8. * * Example: * > mosquitto_property *proplist = NULL; * > mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); */ libmosq_EXPORT int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value); /* * Function: mosquitto_property_add_string_pair * * Add a new string pair property to a property list. * * If *proplist == NULL, a new list will be created, otherwise the new property * will be appended to the list. * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_USER_PROPERTY) * name - string name for the new property, must be UTF-8 and zero terminated * value - string value for the new property, must be UTF-8 and zero terminated * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if identifier is invalid, if name or value is NULL, or if proplist is NULL * MOSQ_ERR_NOMEM - on out of memory * MOSQ_ERR_MALFORMED_UTF8 - if name or value are not valid UTF-8. * * Example: * > mosquitto_property *proplist = NULL; * > mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "client", "mosquitto_pub"); */ libmosq_EXPORT int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value); /* * Function: mosquitto_property_identifier * * Return the property identifier for a single property. * * Parameters: * property - pointer to a valid mosquitto_property pointer. * * Returns: * A valid property identifier on success * 0 - on error */ libmosq_EXPORT int mosquitto_property_identifier(const mosquitto_property *property); /* * Function: mosquitto_property_next * * Return the next property in a property list. Use to iterate over a property * list, e.g.: * * (start code) * for(prop = proplist; prop != NULL; prop = mosquitto_property_next(prop)){ * if(mosquitto_property_identifier(prop) == MQTT_PROP_CONTENT_TYPE){ * ... * } * } * (end) * * Parameters: * proplist - pointer to mosquitto_property pointer, the list of properties * * Returns: * Pointer to the next item in the list * NULL, if proplist is NULL, or if there are no more items in the list. */ libmosq_EXPORT const mosquitto_property *mosquitto_property_next(const mosquitto_property *proplist); /* * Function: mosquitto_property_read_byte * * Attempt to read a byte property matching an identifier, from a property list * or single property. This function can search for multiple entries of the * same identifier by using the returned value and skip_first. Note however * that it is forbidden for most properties to be duplicated. * * If the property is not found, *value will not be modified, so it is safe to * pass a variable with a default value to be potentially overwritten: * * (start code) * uint16_t keepalive = 60; // default value * // Get value from property list, or keep default if not found. * mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &keepalive, false); * (end) * * Parameters: * proplist - mosquitto_property pointer, the list of properties or single property * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - pointer to store the value, or NULL if the value is not required. * skip_first - boolean that indicates whether the first item in the list * should be ignored or not. Should usually be set to false. * * Returns: * A valid property pointer if the property is found * NULL, if the property is not found, or proplist is NULL. * * Example: * (start code) * // proplist is obtained from a callback * mosquitto_property *prop; * prop = mosquitto_property_read_byte(proplist, identifier, &value, false); * while(prop){ * printf("value: %s\n", value); * prop = mosquitto_property_read_byte(prop, identifier, &value); * } * (end) */ libmosq_EXPORT const mosquitto_property *mosquitto_property_read_byte( const mosquitto_property *proplist, int identifier, uint8_t *value, bool skip_first); /* * Function: mosquitto_property_read_int16 * * Read an int16 property value from a property. * * Parameters: * property - property to read * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - pointer to store the value, or NULL if the value is not required. * skip_first - boolean that indicates whether the first item in the list * should be ignored or not. Should usually be set to false. * * Returns: * A valid property pointer if the property is found * NULL, if the property is not found, or proplist is NULL. * * Example: * See */ libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int16( const mosquitto_property *proplist, int identifier, uint16_t *value, bool skip_first); /* * Function: mosquitto_property_read_int32 * * Read an int32 property value from a property. * * Parameters: * property - pointer to mosquitto_property pointer, the list of properties * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - pointer to store the value, or NULL if the value is not required. * skip_first - boolean that indicates whether the first item in the list * should be ignored or not. Should usually be set to false. * * Returns: * A valid property pointer if the property is found * NULL, if the property is not found, or proplist is NULL. * * Example: * See */ libmosq_EXPORT const mosquitto_property *mosquitto_property_read_int32( const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first); /* * Function: mosquitto_property_read_varint * * Read a varint property value from a property. * * Parameters: * property - property to read * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - pointer to store the value, or NULL if the value is not required. * skip_first - boolean that indicates whether the first item in the list * should be ignored or not. Should usually be set to false. * * Returns: * A valid property pointer if the property is found * NULL, if the property is not found, or proplist is NULL. * * Example: * See */ libmosq_EXPORT const mosquitto_property *mosquitto_property_read_varint( const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first); /* * Function: mosquitto_property_read_binary * * Read a binary property value from a property. * * On success, value must be free()'d by the application. * * Parameters: * property - property to read * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - pointer to store the value, or NULL if the value is not required. * skip_first - boolean that indicates whether the first item in the list * should be ignored or not. Should usually be set to false. * * Returns: * A valid property pointer if the property is found * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. * * Example: * See */ libmosq_EXPORT const mosquitto_property *mosquitto_property_read_binary( const mosquitto_property *proplist, int identifier, void **value, uint16_t *len, bool skip_first); /* * Function: mosquitto_property_read_string * * Read a string property value from a property. * * On success, value must be free()'d by the application. * * Parameters: * property - property to read * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * value - pointer to char*, for the property data to be stored in, or NULL if * the value is not required. * skip_first - boolean that indicates whether the first item in the list * should be ignored or not. Should usually be set to false. * * Returns: * A valid property pointer if the property is found * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. * * Example: * See */ libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string( const mosquitto_property *proplist, int identifier, char **value, bool skip_first); /* * Function: mosquitto_property_read_string_pair * * Read a string pair property value pair from a property. * * On success, name and value must be free()'d by the application. * * Parameters: * property - property to read * identifier - property identifier (e.g. MQTT_PROP_PAYLOAD_FORMAT_INDICATOR) * name - pointer to char* for the name property data to be stored in, or NULL * if the name is not required. * value - pointer to char*, for the property data to be stored in, or NULL if * the value is not required. * skip_first - boolean that indicates whether the first item in the list * should be ignored or not. Should usually be set to false. * * Returns: * A valid property pointer if the property is found * NULL, if the property is not found, or proplist is NULL, or if an out of memory condition occurred. * * Example: * See */ libmosq_EXPORT const mosquitto_property *mosquitto_property_read_string_pair( const mosquitto_property *proplist, int identifier, char **name, char **value, bool skip_first); /* * Function: mosquitto_property_free_all * * Free all properties from a list of properties. Frees the list and sets *properties to NULL. * * Parameters: * properties - list of properties to free * * Example: * > mosquitto_properties *properties = NULL; * > // Add properties * > mosquitto_property_free_all(&properties); */ libmosq_EXPORT void mosquitto_property_free_all(mosquitto_property **properties); /* * Function: mosquitto_property_copy_all * * Parameters: * dest - pointer for new property list * src - property list * * Returns: * MOSQ_ERR_SUCCESS - on successful copy * MOSQ_ERR_INVAL - if dest is NULL * MOSQ_ERR_NOMEM - on out of memory (dest will be set to NULL) */ libmosq_EXPORT int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src); /* * Function: mosquitto_property_check_command * * Check whether a property identifier is valid for the given command. * * Parameters: * command - MQTT command (e.g. CMD_CONNECT) * identifier - MQTT property (e.g. MQTT_PROP_USER_PROPERTY) * * Returns: * MOSQ_ERR_SUCCESS - if the identifier is valid for command * MOSQ_ERR_PROTOCOL - if the identifier is not valid for use with command. */ libmosq_EXPORT int mosquitto_property_check_command(int command, int identifier); /* * Function: mosquitto_property_check_all * * Check whether a list of properties are valid for a particular command, * whether there are duplicates, and whether the values are valid where * possible. * * Note that this function is used internally in the library whenever * properties are passed to it, so in basic use this is not needed, but should * be helpful to check property lists *before* the point of using them. * * Parameters: * command - MQTT command (e.g. CMD_CONNECT) * properties - list of MQTT properties to check. * * Returns: * MOSQ_ERR_SUCCESS - if all properties are valid * MOSQ_ERR_DUPLICATE_PROPERTY - if a property is duplicated where it is forbidden. * MOSQ_ERR_PROTOCOL - if any property is invalid */ libmosq_EXPORT int mosquitto_property_check_all(int command, const mosquitto_property *properties); /* * Function: mosquitto_property_identifier_to_string * * Return the property name as a string for a property identifier. * The property name is as defined in the MQTT specification, with - as a * separator, for example: payload-format-indicator. * * Parameters: * identifier - valid MQTT property identifier integer * * Returns: * A const string to the property name on success * NULL on failure */ libmosq_EXPORT const char *mosquitto_property_identifier_to_string(int identifier); /* Function: mosquitto_string_to_property_info * * Parse a property name string and convert to a property identifier and data type. * The property name is as defined in the MQTT specification, with - as a * separator, for example: payload-format-indicator. * * Parameters: * propname - the string to parse * identifier - pointer to an int to receive the property identifier * type - pointer to an int to receive the property type * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if the string does not match a property * * Example: * (start code) * mosquitto_string_to_property_info("response-topic", &id, &type); * // id == MQTT_PROP_RESPONSE_TOPIC * // type == MQTT_PROP_TYPE_STRING * (end) */ libmosq_EXPORT int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type); #ifdef __cplusplus } #endif #endif mosquitto-2.0.18/include/mosquitto_broker.h000066400000000000000000000370021450213760600210600ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ /* * File: mosquitto_broker.h * * This header contains functions for use by plugins. */ #ifndef MOSQUITTO_BROKER_H #define MOSQUITTO_BROKER_H #ifdef __cplusplus extern "C" { #endif #if defined(WIN32) && defined(mosquitto_EXPORTS) # define mosq_EXPORT __declspec(dllexport) #else # define mosq_EXPORT #endif #include #include #include #include struct mosquitto; typedef struct mqtt5__property mosquitto_property; enum mosquitto_protocol { mp_mqtt, mp_mqttsn, mp_websockets }; /* ========================================================================= * * Section: Register callbacks. * * ========================================================================= */ /* Callback events */ enum mosquitto_plugin_event { MOSQ_EVT_RELOAD = 1, MOSQ_EVT_ACL_CHECK = 2, MOSQ_EVT_BASIC_AUTH = 3, MOSQ_EVT_EXT_AUTH_START = 4, MOSQ_EVT_EXT_AUTH_CONTINUE = 5, MOSQ_EVT_CONTROL = 6, MOSQ_EVT_MESSAGE = 7, MOSQ_EVT_PSK_KEY = 8, MOSQ_EVT_TICK = 9, MOSQ_EVT_DISCONNECT = 10, }; /* Data for the MOSQ_EVT_RELOAD event */ struct mosquitto_evt_reload { void *future; struct mosquitto_opt *options; int option_count; void *future2[4]; }; /* Data for the MOSQ_EVT_ACL_CHECK event */ struct mosquitto_evt_acl_check { void *future; struct mosquitto *client; const char *topic; const void *payload; mosquitto_property *properties; int access; uint32_t payloadlen; uint8_t qos; bool retain; void *future2[4]; }; /* Data for the MOSQ_EVT_BASIC_AUTH event */ struct mosquitto_evt_basic_auth { void *future; struct mosquitto *client; char *username; char *password; void *future2[4]; }; /* Data for the MOSQ_EVT_PSK_KEY event */ struct mosquitto_evt_psk_key { void *future; struct mosquitto *client; const char *hint; const char *identity; char *key; int max_key_len; void *future2[4]; }; /* Data for the MOSQ_EVT_EXTENDED_AUTH event */ struct mosquitto_evt_extended_auth { void *future; struct mosquitto *client; const void *data_in; void *data_out; uint16_t data_in_len; uint16_t data_out_len; const char *auth_method; void *future2[3]; }; /* Data for the MOSQ_EVT_CONTROL event */ struct mosquitto_evt_control { void *future; struct mosquitto *client; const char *topic; const void *payload; const mosquitto_property *properties; char *reason_string; uint32_t payloadlen; uint8_t qos; uint8_t reason_code; bool retain; void *future2[4]; }; /* Data for the MOSQ_EVT_MESSAGE event */ struct mosquitto_evt_message { void *future; struct mosquitto *client; char *topic; void *payload; mosquitto_property *properties; char *reason_string; uint32_t payloadlen; uint8_t qos; uint8_t reason_code; bool retain; void *future2[4]; }; /* Data for the MOSQ_EVT_TICK event */ struct mosquitto_evt_tick { void *future; long now_ns; long next_ns; time_t now_s; time_t next_s; void *future2[4]; }; /* Data for the MOSQ_EVT_DISCONNECT event */ struct mosquitto_evt_disconnect { void *future; struct mosquitto *client; int reason; void *future2[4]; }; /* Callback definition */ typedef int (*MOSQ_FUNC_generic_callback)(int, void *, void *); typedef struct mosquitto_plugin_id_t mosquitto_plugin_id_t; /* * Function: mosquitto_callback_register * * Register a callback for an event. * * Parameters: * identifier - the plugin identifier, as provided by . * event - the event to register a callback for. Can be one of: * * MOSQ_EVT_RELOAD * * MOSQ_EVT_ACL_CHECK * * MOSQ_EVT_BASIC_AUTH * * MOSQ_EVT_EXT_AUTH_START * * MOSQ_EVT_EXT_AUTH_CONTINUE * * MOSQ_EVT_CONTROL * * MOSQ_EVT_MESSAGE * * MOSQ_EVT_PSK_KEY * * MOSQ_EVT_TICK * * MOSQ_EVT_DISCONNECT * cb_func - the callback function * event_data - event specific data * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if cb_func is NULL * MOSQ_ERR_NOMEM - on out of memory * MOSQ_ERR_ALREADY_EXISTS - if cb_func has already been registered for this event * MOSQ_ERR_NOT_SUPPORTED - if the event is not supported */ mosq_EXPORT int mosquitto_callback_register( mosquitto_plugin_id_t *identifier, int event, MOSQ_FUNC_generic_callback cb_func, const void *event_data, void *userdata); /* * Function: mosquitto_callback_unregister * * Unregister a previously registered callback function. * * Parameters: * identifier - the plugin identifier, as provided by . * event - the event to register a callback for. Can be one of: * * MOSQ_EVT_RELOAD * * MOSQ_EVT_ACL_CHECK * * MOSQ_EVT_BASIC_AUTH * * MOSQ_EVT_EXT_AUTH_START * * MOSQ_EVT_EXT_AUTH_CONTINUE * * MOSQ_EVT_CONTROL * * MOSQ_EVT_MESSAGE * * MOSQ_EVT_PSK_KEY * * MOSQ_EVT_TICK * * MOSQ_EVT_DISCONNECT * cb_func - the callback function * event_data - event specific data * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if cb_func is NULL * MOSQ_ERR_NOT_FOUND - if cb_func was not registered for this event * MOSQ_ERR_NOT_SUPPORTED - if the event is not supported */ mosq_EXPORT int mosquitto_callback_unregister( mosquitto_plugin_id_t *identifier, int event, MOSQ_FUNC_generic_callback cb_func, const void *event_data); /* ========================================================================= * * Section: Memory allocation. * * Use these functions when allocating or freeing memory to have your memory * included in the memory tracking on the broker. * * ========================================================================= */ /* * Function: mosquitto_calloc */ mosq_EXPORT void *mosquitto_calloc(size_t nmemb, size_t size); /* * Function: mosquitto_free */ mosq_EXPORT void mosquitto_free(void *mem); /* * Function: mosquitto_malloc */ mosq_EXPORT void *mosquitto_malloc(size_t size); /* * Function: mosquitto_realloc */ mosq_EXPORT void *mosquitto_realloc(void *ptr, size_t size); /* * Function: mosquitto_strdup */ mosq_EXPORT char *mosquitto_strdup(const char *s); /* ========================================================================= * * Section: Utility Functions * * Use these functions from within your plugin. * * ========================================================================= */ /* * Function: mosquitto_log_printf * * Write a log message using the broker configured logging. * * Parameters: * level - Log message priority. Can currently be one of: * * * MOSQ_LOG_INFO * * MOSQ_LOG_NOTICE * * MOSQ_LOG_WARNING * * MOSQ_LOG_ERR * * MOSQ_LOG_DEBUG * * MOSQ_LOG_SUBSCRIBE (not recommended for use by plugins) * * MOSQ_LOG_UNSUBSCRIBE (not recommended for use by plugins) * * These values are defined in mosquitto.h. * * fmt, ... - printf style format and arguments. */ mosq_EXPORT void mosquitto_log_printf(int level, const char *fmt, ...); /* ========================================================================= * * Client Functions * * Use these functions to access client information. * * ========================================================================= */ /* * Function: mosquitto_client_address * * Retrieve the IP address of the client as a string. */ mosq_EXPORT const char *mosquitto_client_address(const struct mosquitto *client); /* * Function: mosquitto_client_clean_session * * Retrieve the clean session flag value for a client. */ mosq_EXPORT bool mosquitto_client_clean_session(const struct mosquitto *client); /* * Function: mosquitto_client_id * * Retrieve the client id associated with a client. */ mosq_EXPORT const char *mosquitto_client_id(const struct mosquitto *client); /* * Function: mosquitto_client_keepalive * * Retrieve the keepalive value for a client. */ mosq_EXPORT int mosquitto_client_keepalive(const struct mosquitto *client); /* * Function: mosquitto_client_certificate * * If TLS support is enabled, return the certificate provided by a client as an * X509 pointer from openssl. If the client did not provide a certificate, then * NULL will be returned. This function will only ever return a non-NULL value * if the `require_certificate` option is set to true. * * When you have finished with the x509 pointer, it must be freed using * X509_free(). * * If TLS is not supported, this function will always return NULL. */ mosq_EXPORT void *mosquitto_client_certificate(const struct mosquitto *client); /* * Function: mosquitto_client_protocol * * Retrieve the protocol with which the client has connected. Can be one of: * * mp_mqtt (MQTT over TCP) * mp_mqttsn (MQTT-SN) * mp_websockets (MQTT over Websockets) */ mosq_EXPORT int mosquitto_client_protocol(const struct mosquitto *client); /* * Function: mosquitto_client_protocol_version * * Retrieve the MQTT protocol version with which the client has connected. Can be one of: * * Returns: * 3 - for MQTT v3 / v3.1 * 4 - for MQTT v3.1.1 * 5 - for MQTT v5 */ mosq_EXPORT int mosquitto_client_protocol_version(const struct mosquitto *client); /* * Function: mosquitto_client_sub_count * * Retrieve the number of subscriptions that have been made by a client. */ mosq_EXPORT int mosquitto_client_sub_count(const struct mosquitto *client); /* * Function: mosquitto_client_username * * Retrieve the username associated with a client. */ mosq_EXPORT const char *mosquitto_client_username(const struct mosquitto *client); /* Function: mosquitto_set_username * * Set the username for a client. * * This removes and replaces the current username for a client and hence * updates its access. * * username can be NULL, in which case the client will become anonymous, but * must not be zero length. * * In the case of error, the client will be left with its original username. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if client is NULL, or if username is zero length * MOSQ_ERR_NOMEM - on out of memory */ mosq_EXPORT int mosquitto_set_username(struct mosquitto *client, const char *username); /* ========================================================================= * * Section: Client control * * ========================================================================= */ /* Function: mosquitto_kick_client_by_clientid * * Forcefully disconnect a client from the broker. * * If clientid != NULL, then the client with the matching client id is * disconnected from the broker. * If clientid == NULL, then all clients are disconnected from the broker. * * If with_will == true, then if the client has a Last Will and Testament * defined then this will be sent. If false, the LWT will not be sent. */ mosq_EXPORT int mosquitto_kick_client_by_clientid(const char *clientid, bool with_will); /* Function: mosquitto_kick_client_by_username * * Forcefully disconnect a client from the broker. * * If username != NULL, then all clients with a matching username are kicked * from the broker. * If username == NULL, then all clients that do not have a username are * kicked. * * If with_will == true, then if the client has a Last Will and Testament * defined then this will be sent. If false, the LWT will not be sent. */ mosq_EXPORT int mosquitto_kick_client_by_username(const char *username, bool with_will); /* ========================================================================= * * Section: Publishing functions * * ========================================================================= */ /* Function: mosquitto_broker_publish * * Publish a message from within a plugin. * * This function allows a plugin to publish a message. Messages published in * this way are treated as coming from the broker and so will not be passed to * `mosquitto_auth_acl_check(, MOSQ_ACL_WRITE, , )` for checking. Read access * will be enforced as normal for individual clients when they are due to * receive the message. * * It can be used to send messages to all clients that have a matching * subscription, or to a single client whether or not it has a matching * subscription. * * Parameters: * clientid - optional string. If set to NULL, the message is delivered to all * clients. If non-NULL, the message is delivered only to the * client with the corresponding client id. If the client id * specified is not connected, the message will be dropped. * topic - message topic * payloadlen - payload length in bytes. Can be 0 for an empty payload. * payload - payload bytes. If payloadlen > 0 this must not be NULL. Must * be allocated on the heap. Will be freed by mosquitto after use if the * function returns success. * qos - message QoS to use. * retain - should retain be set on the message. This does not apply if * clientid is non-NULL. * properties - MQTT v5 properties to attach to the message. If the function * returns success, then properties is owned by the broker and * will be freed at a later point. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if topic is NULL, if payloadlen < 0, if payloadlen > 0 * and payload is NULL, if qos is not 0, 1, or 2. * MOSQ_ERR_NOMEM - on out of memory */ mosq_EXPORT int mosquitto_broker_publish( const char *clientid, const char *topic, int payloadlen, void *payload, int qos, bool retain, mosquitto_property *properties); /* Function: mosquitto_broker_publish_copy * * Publish a message from within a plugin. * * This function is identical to mosquitto_broker_publish, except that a copy * of `payload` is taken. * * Parameters: * clientid - optional string. If set to NULL, the message is delivered to all * clients. If non-NULL, the message is delivered only to the * client with the corresponding client id. If the client id * specified is not connected, the message will be dropped. * topic - message topic * payloadlen - payload length in bytes. Can be 0 for an empty payload. * payload - payload bytes. If payloadlen > 0 this must not be NULL. * Memory remains the property of the calling function. * qos - message QoS to use. * retain - should retain be set on the message. This does not apply if * clientid is non-NULL. * properties - MQTT v5 properties to attach to the message. If the function * returns success, then properties is owned by the broker and * will be freed at a later point. * * Returns: * MOSQ_ERR_SUCCESS - on success * MOSQ_ERR_INVAL - if topic is NULL, if payloadlen < 0, if payloadlen > 0 * and payload is NULL, if qos is not 0, 1, or 2. * MOSQ_ERR_NOMEM - on out of memory */ mosq_EXPORT int mosquitto_broker_publish_copy( const char *clientid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); #ifdef __cplusplus } #endif #endif mosquitto-2.0.18/include/mosquitto_plugin.h000066400000000000000000000361011450213760600210710ustar00rootroot00000000000000/* Copyright (c) 2012-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MOSQUITTO_PLUGIN_H #define MOSQUITTO_PLUGIN_H /* * File: mosquitto_plugin.h * * This header contains function declarations for use when writing a Mosquitto plugin. */ #ifdef __cplusplus extern "C" { #endif /* The generic plugin interface starts at version 5 */ #define MOSQ_PLUGIN_VERSION 5 /* The old auth only interface stopped at version 4 */ #define MOSQ_AUTH_PLUGIN_VERSION 4 #define MOSQ_ACL_NONE 0x00 #define MOSQ_ACL_READ 0x01 #define MOSQ_ACL_WRITE 0x02 #define MOSQ_ACL_SUBSCRIBE 0x04 #define MOSQ_ACL_UNSUBSCRIBE 0x08 #include #include #include struct mosquitto; struct mosquitto_opt { char *key; char *value; }; struct mosquitto_auth_opt { char *key; char *value; }; struct mosquitto_acl_msg { const char *topic; const void *payload; long payloadlen; int qos; bool retain; }; #ifdef WIN32 # define mosq_plugin_EXPORT __declspec(dllexport) #else # define mosq_plugin_EXPORT #endif /* * To create an authentication plugin you must include this file then implement * the functions listed in the "Plugin Functions" section below. The resulting * code should then be compiled as a shared library. Using gcc this can be * achieved as follows: * * gcc -I -fPIC -shared plugin.c -o plugin.so * * On Mac OS X: * * gcc -I -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so * * Authentication plugins can implement one or both of authentication and * access control. If your plugin does not wish to handle either of * authentication or access control it should return MOSQ_ERR_PLUGIN_DEFER. In * this case, the next plugin will handle it. If all plugins return * MOSQ_ERR_PLUGIN_DEFER, the request will be denied. * * For each check, the following flow happens: * * * The default password file and/or acl file checks are made. If either one * of these is not defined, then they are considered to be deferred. If either * one accepts the check, no further checks are made. If an error occurs, the * check is denied * * The first plugin does the check, if it returns anything other than * MOSQ_ERR_PLUGIN_DEFER, then the check returns immediately. If the plugin * returns MOSQ_ERR_PLUGIN_DEFER then the next plugin runs its check. * * If the final plugin returns MOSQ_ERR_PLUGIN_DEFER, then access will be * denied. */ /* ========================================================================= * * Helper Functions * * ========================================================================= */ /* There are functions that are available for plugin developers to use in * mosquitto_broker.h, including logging and accessor functions. */ /* ========================================================================= * * Section: Plugin Functions v5 * * This is the plugin version 5 interface, which covers authentication, access * control, the $CONTROL topic space handling, and message inspection and * modification. * * This interface is available from v2.0 onwards. * * There are just three functions to implement in your plugin. You should * register callbacks to handle different events in your * mosquitto_plugin_init() function. See mosquitto_broker.h for the events and * callback registering functions. * * ========================================================================= */ /* * Function: mosquitto_plugin_version * * The broker will attempt to call this function immediately after loading the * plugin to check it is a supported plugin version. Your code must simply * return the plugin interface version you support, i.e. 5. * * The supported_versions array tells you which plugin versions the broker supports. * * If the broker does not support the version that you require, return -1 to * indicate failure. */ mosq_plugin_EXPORT int mosquitto_plugin_version(int supported_version_count, const int *supported_versions); /* * Function: mosquitto_plugin_init * * Called after the plugin has been loaded and * has been called. This will only ever be called once and can be used to * initialise the plugin. * * Parameters: * * identifier - This is a pointer to an opaque structure which you must * save and use when registering/unregistering callbacks. * user_data - The pointer set here will be passed to the other plugin * functions. Use to hold connection information for example. * opts - Pointer to an array of struct mosquitto_opt, which * provides the plugin options defined in the configuration file. * opt_count - The number of elements in the opts array. * * Return value: * Return 0 on success * Return >0 on failure. */ mosq_plugin_EXPORT int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **userdata, struct mosquitto_opt *options, int option_count); /* * Function: mosquitto_plugin_cleanup * * Called when the broker is shutting down. This will only ever be called once * per plugin. * * Parameters: * * user_data - The pointer provided in . * opts - Pointer to an array of struct mosquitto_opt, which * provides the plugin options defined in the configuration file. * opt_count - The number of elements in the opts array. * * Return value: * Return 0 on success * Return >0 on failure. */ mosq_plugin_EXPORT int mosquitto_plugin_cleanup(void *userdata, struct mosquitto_opt *options, int option_count); /* ========================================================================= * * Section: Plugin Functions v4 * * This is the plugin version 4 interface, which is exclusively for * authentication and access control, and which is still supported for existing * plugins. If you are developing a new plugin, please use the v5 interface. * * You must implement these functions in your plugin. * * ========================================================================= */ /* * Function: mosquitto_auth_plugin_version * * The broker will call this function immediately after loading the plugin to * check it is a supported plugin version. Your code must simply return * the version of the plugin interface you support, i.e. 4. */ mosq_plugin_EXPORT int mosquitto_auth_plugin_version(void); /* * Function: mosquitto_auth_plugin_init * * Called after the plugin has been loaded and * has been called. This will only ever be called once and can be used to * initialise the plugin. * * Parameters: * * user_data - The pointer set here will be passed to the other plugin * functions. Use to hold connection information for example. * opts - Pointer to an array of struct mosquitto_opt, which * provides the plugin options defined in the configuration file. * opt_count - The number of elements in the opts array. * * Return value: * Return 0 on success * Return >0 on failure. */ mosq_plugin_EXPORT int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *opts, int opt_count); /* * Function: mosquitto_auth_plugin_cleanup * * Called when the broker is shutting down. This will only ever be called once * per plugin. * Note that will be called directly before * this function. * * Parameters: * * user_data - The pointer provided in . * opts - Pointer to an array of struct mosquitto_opt, which * provides the plugin options defined in the configuration file. * opt_count - The number of elements in the opts array. * * Return value: * Return 0 on success * Return >0 on failure. */ mosq_plugin_EXPORT int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count); /* * Function: mosquitto_auth_security_init * * This function is called in two scenarios: * * 1. When the broker starts up. * 2. If the broker is requested to reload its configuration whilst running. In * this case, will be called first, then * this function will be called. In this situation, the reload parameter * will be true. * * Parameters: * * user_data - The pointer provided in . * opts - Pointer to an array of struct mosquitto_opt, which * provides the plugin options defined in the configuration file. * opt_count - The number of elements in the opts array. * reload - If set to false, this is the first time the function has * been called. If true, the broker has received a signal * asking to reload its configuration. * * Return value: * Return 0 on success * Return >0 on failure. */ mosq_plugin_EXPORT int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload); /* * Function: mosquitto_auth_security_cleanup * * This function is called in two scenarios: * * 1. When the broker is shutting down. * 2. If the broker is requested to reload its configuration whilst running. In * this case, this function will be called, followed by * . In this situation, the reload parameter * will be true. * * Parameters: * * user_data - The pointer provided in . * opts - Pointer to an array of struct mosquitto_opt, which * provides the plugin options defined in the configuration file. * opt_count - The number of elements in the opts array. * reload - If set to false, this is the first time the function has * been called. If true, the broker has received a signal * asking to reload its configuration. * * Return value: * Return 0 on success * Return >0 on failure. */ mosq_plugin_EXPORT int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count, bool reload); /* * Function: mosquitto_auth_acl_check * * Called by the broker when topic access must be checked. access will be one * of: * MOSQ_ACL_SUBSCRIBE when a client is asking to subscribe to a topic string. * This differs from MOSQ_ACL_READ in that it allows you to * deny access to topic strings rather than by pattern. For * example, you may use MOSQ_ACL_SUBSCRIBE to deny * subscriptions to '#', but allow all topics in * MOSQ_ACL_READ. This allows clients to subscribe to any * topic they want, but not discover what topics are in use * on the server. * MOSQ_ACL_READ when a message is about to be sent to a client (i.e. whether * it can read that topic or not). * MOSQ_ACL_WRITE when a message has been received from a client (i.e. whether * it can write to that topic or not). * * Return: * MOSQ_ERR_SUCCESS if access was granted. * MOSQ_ERR_ACL_DENIED if access was not granted. * MOSQ_ERR_UNKNOWN for an application specific error. * MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. */ mosq_plugin_EXPORT int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg); /* * Function: mosquitto_auth_unpwd_check * * This function is OPTIONAL. Only include this function in your plugin if you * are making basic username/password checks. * * Called by the broker when a username/password must be checked. * * Return: * MOSQ_ERR_SUCCESS if the user is authenticated. * MOSQ_ERR_AUTH if authentication failed. * MOSQ_ERR_UNKNOWN for an application specific error. * MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. */ mosq_plugin_EXPORT int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password); /* * Function: mosquitto_psk_key_get * * This function is OPTIONAL. Only include this function in your plugin if you * are making TLS-PSK checks. * * Called by the broker when a client connects to a listener using TLS/PSK. * This is used to retrieve the pre-shared-key associated with a client * identity. * * Examine hint and identity to determine the required PSK (which must be a * hexadecimal string with no leading "0x") and copy this string into key. * * Parameters: * user_data - the pointer provided in . * hint - the psk_hint for the listener the client is connecting to. * identity - the identity string provided by the client * key - a string where the hex PSK should be copied * max_key_len - the size of key * * Return value: * Return 0 on success. * Return >0 on failure. * Return MOSQ_ERR_PLUGIN_DEFER if your plugin does not wish to handle this check. */ mosq_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); /* * Function: mosquitto_auth_start * * This function is OPTIONAL. Only include this function in your plugin if you * are making extended authentication checks. * * Parameters: * user_data - the pointer provided in . * method - the authentication method * reauth - this is set to false if this is the first authentication attempt * on a connection, set to true if the client is attempting to * reauthenticate. * data_in - pointer to authentication data, or NULL * data_in_len - length of data_in, in bytes * data_out - if your plugin wishes to send authentication data back to the * client, allocate some memory using malloc or friends and set * data_out. The broker will free the memory after use. * data_out_len - Set the length of data_out in bytes. * * Return value: * Return MOSQ_ERR_SUCCESS if authentication was successful. * Return MOSQ_ERR_AUTH_CONTINUE if the authentication is a multi step process and can continue. * Return MOSQ_ERR_AUTH if authentication was valid but did not succeed. * Return any other relevant positive integer MOSQ_ERR_* to produce an error. */ mosq_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); mosq_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); #ifdef __cplusplus } #endif #endif mosquitto-2.0.18/include/mqtt_protocol.h000066400000000000000000000270221450213760600203570ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MQTT_PROTOCOL_H #define MQTT_PROTOCOL_H /* * File: mqtt_protocol.h * * This header contains definitions of MQTT values as defined in the specifications. */ #define PROTOCOL_NAME_v31 "MQIsdp" #define PROTOCOL_VERSION_v31 3 #define PROTOCOL_NAME "MQTT" #define PROTOCOL_VERSION_v311 4 #define PROTOCOL_VERSION_v5 5 /* Message types */ #define CMD_CONNECT 0x10U #define CMD_CONNACK 0x20U #define CMD_PUBLISH 0x30U #define CMD_PUBACK 0x40U #define CMD_PUBREC 0x50U #define CMD_PUBREL 0x60U #define CMD_PUBCOMP 0x70U #define CMD_SUBSCRIBE 0x80U #define CMD_SUBACK 0x90U #define CMD_UNSUBSCRIBE 0xA0U #define CMD_UNSUBACK 0xB0U #define CMD_PINGREQ 0xC0U #define CMD_PINGRESP 0xD0U #define CMD_DISCONNECT 0xE0U #define CMD_AUTH 0xF0U /* Mosquitto only: for distinguishing CONNECT and WILL properties */ #define CMD_WILL 0x100 /* Enum: mqtt311_connack_codes * * The CONNACK results for MQTT v3.1.1, and v3.1. * * Values: * CONNACK_ACCEPTED - 0 * CONNACK_REFUSED_PROTOCOL_VERSION - 1 * CONNACK_REFUSED_IDENTIFIER_REJECTED - 2 * CONNACK_REFUSED_SERVER_UNAVAILABLE - 3 * CONNACK_REFUSED_BAD_USERNAME_PASSWORD - 4 * CONNACK_REFUSED_NOT_AUTHORIZED - 5 */ enum mqtt311_connack_codes { CONNACK_ACCEPTED = 0, CONNACK_REFUSED_PROTOCOL_VERSION = 1, CONNACK_REFUSED_IDENTIFIER_REJECTED = 2, CONNACK_REFUSED_SERVER_UNAVAILABLE = 3, CONNACK_REFUSED_BAD_USERNAME_PASSWORD = 4, CONNACK_REFUSED_NOT_AUTHORIZED = 5, }; /* Enum: mqtt5_return_codes * The reason codes returned in various MQTT commands. * * Values: * MQTT_RC_SUCCESS - 0 * MQTT_RC_NORMAL_DISCONNECTION - 0 * MQTT_RC_GRANTED_QOS0 - 0 * MQTT_RC_GRANTED_QOS1 - 1 * MQTT_RC_GRANTED_QOS2 - 2 * MQTT_RC_DISCONNECT_WITH_WILL_MSG - 4 * MQTT_RC_NO_MATCHING_SUBSCRIBERS - 16 * MQTT_RC_NO_SUBSCRIPTION_EXISTED - 17 * MQTT_RC_CONTINUE_AUTHENTICATION - 24 * MQTT_RC_REAUTHENTICATE - 25 * MQTT_RC_UNSPECIFIED - 128 * MQTT_RC_MALFORMED_PACKET - 129 * MQTT_RC_PROTOCOL_ERROR - 130 * MQTT_RC_IMPLEMENTATION_SPECIFIC - 131 * MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION - 132 * MQTT_RC_CLIENTID_NOT_VALID - 133 * MQTT_RC_BAD_USERNAME_OR_PASSWORD - 134 * MQTT_RC_NOT_AUTHORIZED - 135 * MQTT_RC_SERVER_UNAVAILABLE - 136 * MQTT_RC_SERVER_BUSY - 137 * MQTT_RC_BANNED - 138 * MQTT_RC_SERVER_SHUTTING_DOWN - 139 * MQTT_RC_BAD_AUTHENTICATION_METHOD - 140 * MQTT_RC_KEEP_ALIVE_TIMEOUT - 141 * MQTT_RC_SESSION_TAKEN_OVER - 142 * MQTT_RC_TOPIC_FILTER_INVALID - 143 * MQTT_RC_TOPIC_NAME_INVALID - 144 * MQTT_RC_PACKET_ID_IN_USE - 145 * MQTT_RC_PACKET_ID_NOT_FOUND - 146 * MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED - 147 * MQTT_RC_TOPIC_ALIAS_INVALID - 148 * MQTT_RC_PACKET_TOO_LARGE - 149 * MQTT_RC_MESSAGE_RATE_TOO_HIGH - 150 * MQTT_RC_QUOTA_EXCEEDED - 151 * MQTT_RC_ADMINISTRATIVE_ACTION - 152 * MQTT_RC_PAYLOAD_FORMAT_INVALID - 153 * MQTT_RC_RETAIN_NOT_SUPPORTED - 154 * MQTT_RC_QOS_NOT_SUPPORTED - 155 * MQTT_RC_USE_ANOTHER_SERVER - 156 * MQTT_RC_SERVER_MOVED - 157 * MQTT_RC_SHARED_SUBS_NOT_SUPPORTED - 158 * MQTT_RC_CONNECTION_RATE_EXCEEDED - 159 * MQTT_RC_MAXIMUM_CONNECT_TIME - 160 * MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED - 161 * MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED - 162 */ enum mqtt5_return_codes { MQTT_RC_SUCCESS = 0, /* CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH */ MQTT_RC_NORMAL_DISCONNECTION = 0, /* DISCONNECT */ MQTT_RC_GRANTED_QOS0 = 0, /* SUBACK */ MQTT_RC_GRANTED_QOS1 = 1, /* SUBACK */ MQTT_RC_GRANTED_QOS2 = 2, /* SUBACK */ MQTT_RC_DISCONNECT_WITH_WILL_MSG = 4, /* DISCONNECT */ MQTT_RC_NO_MATCHING_SUBSCRIBERS = 16, /* PUBACK, PUBREC */ MQTT_RC_NO_SUBSCRIPTION_EXISTED = 17, /* UNSUBACK */ MQTT_RC_CONTINUE_AUTHENTICATION = 24, /* AUTH */ MQTT_RC_REAUTHENTICATE = 25, /* AUTH */ MQTT_RC_UNSPECIFIED = 128, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ MQTT_RC_MALFORMED_PACKET = 129, /* CONNACK, DISCONNECT */ MQTT_RC_PROTOCOL_ERROR = 130, /* DISCONNECT */ MQTT_RC_IMPLEMENTATION_SPECIFIC = 131, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION = 132, /* CONNACK */ MQTT_RC_CLIENTID_NOT_VALID = 133, /* CONNACK */ MQTT_RC_BAD_USERNAME_OR_PASSWORD = 134, /* CONNACK */ MQTT_RC_NOT_AUTHORIZED = 135, /* CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT */ MQTT_RC_SERVER_UNAVAILABLE = 136, /* CONNACK */ MQTT_RC_SERVER_BUSY = 137, /* CONNACK, DISCONNECT */ MQTT_RC_BANNED = 138, /* CONNACK */ MQTT_RC_SERVER_SHUTTING_DOWN = 139, /* DISCONNECT */ MQTT_RC_BAD_AUTHENTICATION_METHOD = 140, /* CONNACK */ MQTT_RC_KEEP_ALIVE_TIMEOUT = 141, /* DISCONNECT */ MQTT_RC_SESSION_TAKEN_OVER = 142, /* DISCONNECT */ MQTT_RC_TOPIC_FILTER_INVALID = 143, /* SUBACK, UNSUBACK, DISCONNECT */ MQTT_RC_TOPIC_NAME_INVALID = 144, /* CONNACK, PUBACK, PUBREC, DISCONNECT */ MQTT_RC_PACKET_ID_IN_USE = 145, /* PUBACK, SUBACK, UNSUBACK */ MQTT_RC_PACKET_ID_NOT_FOUND = 146, /* PUBREL, PUBCOMP */ MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED = 147, /* DISCONNECT */ MQTT_RC_TOPIC_ALIAS_INVALID = 148, /* DISCONNECT */ MQTT_RC_PACKET_TOO_LARGE = 149, /* CONNACK, PUBACK, PUBREC, DISCONNECT */ MQTT_RC_MESSAGE_RATE_TOO_HIGH = 150, /* DISCONNECT */ MQTT_RC_QUOTA_EXCEEDED = 151, /* PUBACK, PUBREC, SUBACK, DISCONNECT */ MQTT_RC_ADMINISTRATIVE_ACTION = 152, /* DISCONNECT */ MQTT_RC_PAYLOAD_FORMAT_INVALID = 153, /* CONNACK, DISCONNECT */ MQTT_RC_RETAIN_NOT_SUPPORTED = 154, /* CONNACK, DISCONNECT */ MQTT_RC_QOS_NOT_SUPPORTED = 155, /* CONNACK, DISCONNECT */ MQTT_RC_USE_ANOTHER_SERVER = 156, /* CONNACK, DISCONNECT */ MQTT_RC_SERVER_MOVED = 157, /* CONNACK, DISCONNECT */ MQTT_RC_SHARED_SUBS_NOT_SUPPORTED = 158, /* SUBACK, DISCONNECT */ MQTT_RC_CONNECTION_RATE_EXCEEDED = 159, /* CONNACK, DISCONNECT */ MQTT_RC_MAXIMUM_CONNECT_TIME = 160, /* DISCONNECT */ MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161, /* SUBACK, DISCONNECT */ MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED = 162, /* SUBACK, DISCONNECT */ }; /* Enum: mqtt5_property * Options for use with MQTTv5 properties. * Options: * * MQTT_PROP_PAYLOAD_FORMAT_INDICATOR - property option. * MQTT_PROP_MESSAGE_EXPIRY_INTERVAL - property option. * MQTT_PROP_CONTENT_TYPE - property option. * MQTT_PROP_RESPONSE_TOPIC - property option. * MQTT_PROP_CORRELATION_DATA - property option. * MQTT_PROP_SUBSCRIPTION_IDENTIFIER - property option. * MQTT_PROP_SESSION_EXPIRY_INTERVAL - property option. * MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER - property option. * MQTT_PROP_SERVER_KEEP_ALIVE - property option. * MQTT_PROP_AUTHENTICATION_METHOD - property option. * MQTT_PROP_AUTHENTICATION_DATA - property option. * MQTT_PROP_REQUEST_PROBLEM_INFORMATION - property option. * MQTT_PROP_WILL_DELAY_INTERVAL - property option. * MQTT_PROP_REQUEST_RESPONSE_INFORMATION - property option. * MQTT_PROP_RESPONSE_INFORMATION - property option. * MQTT_PROP_SERVER_REFERENCE - property option. * MQTT_PROP_REASON_STRING - property option. * MQTT_PROP_RECEIVE_MAXIMUM - property option. * MQTT_PROP_TOPIC_ALIAS_MAXIMUM - property option. * MQTT_PROP_TOPIC_ALIAS - property option. * MQTT_PROP_MAXIMUM_QOS - property option. * MQTT_PROP_RETAIN_AVAILABLE - property option. * MQTT_PROP_USER_PROPERTY - property option. * MQTT_PROP_MAXIMUM_PACKET_SIZE - property option. * MQTT_PROP_WILDCARD_SUB_AVAILABLE - property option. * MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE - property option. * MQTT_PROP_SHARED_SUB_AVAILABLE - property option. */ enum mqtt5_property { MQTT_PROP_PAYLOAD_FORMAT_INDICATOR = 1, /* Byte : PUBLISH, Will Properties */ MQTT_PROP_MESSAGE_EXPIRY_INTERVAL = 2, /* 4 byte int : PUBLISH, Will Properties */ MQTT_PROP_CONTENT_TYPE = 3, /* UTF-8 string : PUBLISH, Will Properties */ MQTT_PROP_RESPONSE_TOPIC = 8, /* UTF-8 string : PUBLISH, Will Properties */ MQTT_PROP_CORRELATION_DATA = 9, /* Binary Data : PUBLISH, Will Properties */ MQTT_PROP_SUBSCRIPTION_IDENTIFIER = 11, /* Variable byte int : PUBLISH, SUBSCRIBE */ MQTT_PROP_SESSION_EXPIRY_INTERVAL = 17, /* 4 byte int : CONNECT, CONNACK, DISCONNECT */ MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER = 18, /* UTF-8 string : CONNACK */ MQTT_PROP_SERVER_KEEP_ALIVE = 19, /* 2 byte int : CONNACK */ MQTT_PROP_AUTHENTICATION_METHOD = 21, /* UTF-8 string : CONNECT, CONNACK, AUTH */ MQTT_PROP_AUTHENTICATION_DATA = 22, /* Binary Data : CONNECT, CONNACK, AUTH */ MQTT_PROP_REQUEST_PROBLEM_INFORMATION = 23, /* Byte : CONNECT */ MQTT_PROP_WILL_DELAY_INTERVAL = 24, /* 4 byte int : Will properties */ MQTT_PROP_REQUEST_RESPONSE_INFORMATION = 25,/* Byte : CONNECT */ MQTT_PROP_RESPONSE_INFORMATION = 26, /* UTF-8 string : CONNACK */ MQTT_PROP_SERVER_REFERENCE = 28, /* UTF-8 string : CONNACK, DISCONNECT */ MQTT_PROP_REASON_STRING = 31, /* UTF-8 string : All except Will properties */ MQTT_PROP_RECEIVE_MAXIMUM = 33, /* 2 byte int : CONNECT, CONNACK */ MQTT_PROP_TOPIC_ALIAS_MAXIMUM = 34, /* 2 byte int : CONNECT, CONNACK */ MQTT_PROP_TOPIC_ALIAS = 35, /* 2 byte int : PUBLISH */ MQTT_PROP_MAXIMUM_QOS = 36, /* Byte : CONNACK */ MQTT_PROP_RETAIN_AVAILABLE = 37, /* Byte : CONNACK */ MQTT_PROP_USER_PROPERTY = 38, /* UTF-8 string pair : All */ MQTT_PROP_MAXIMUM_PACKET_SIZE = 39, /* 4 byte int : CONNECT, CONNACK */ MQTT_PROP_WILDCARD_SUB_AVAILABLE = 40, /* Byte : CONNACK */ MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE = 41, /* Byte : CONNACK */ MQTT_PROP_SHARED_SUB_AVAILABLE = 42, /* Byte : CONNACK */ }; enum mqtt5_property_type { MQTT_PROP_TYPE_BYTE = 1, MQTT_PROP_TYPE_INT16 = 2, MQTT_PROP_TYPE_INT32 = 3, MQTT_PROP_TYPE_VARINT = 4, MQTT_PROP_TYPE_BINARY = 5, MQTT_PROP_TYPE_STRING = 6, MQTT_PROP_TYPE_STRING_PAIR = 7 }; /* Enum: mqtt5_sub_options * Options for use with MQTTv5 subscriptions. * * MQTT_SUB_OPT_NO_LOCAL - with this option set, if this client publishes to * a topic to which it is subscribed, the broker will not publish the * message back to the client. * * MQTT_SUB_OPT_RETAIN_AS_PUBLISHED - with this option set, messages * published for this subscription will keep the retain flag as was set by * the publishing client. The default behaviour without this option set has * the retain flag indicating whether a message is fresh/stale. * * MQTT_SUB_OPT_SEND_RETAIN_ALWAYS - with this option set, pre-existing * retained messages are sent as soon as the subscription is made, even * if the subscription already exists. This is the default behaviour, so * it is not necessary to set this option. * * MQTT_SUB_OPT_SEND_RETAIN_NEW - with this option set, pre-existing retained * messages for this subscription will be sent when the subscription is made, * but only if the subscription does not already exist. * * MQTT_SUB_OPT_SEND_RETAIN_NEVER - with this option set, pre-existing * retained messages will never be sent for this subscription. */ enum mqtt5_sub_options { MQTT_SUB_OPT_NO_LOCAL = 0x04, MQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08, MQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00, MQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10, MQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20, }; #define MQTT_MAX_PAYLOAD 268435455U #endif mosquitto-2.0.18/installer/000077500000000000000000000000001450213760600156475ustar00rootroot00000000000000mosquitto-2.0.18/installer/mosquitto.nsi000066400000000000000000000121001450213760600204200ustar00rootroot00000000000000; NSIS installer script for mosquitto !include "MUI2.nsh" !include "nsDialogs.nsh" !include "LogicLib.nsh" ; For environment variable code !include "WinMessages.nsh" !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" !define VERSION 2.0.18 OutFile "mosquitto-${VERSION}-install-windows-x86.exe" InstallDir "$PROGRAMFILES\mosquitto" ;-------------------------------- ; Installer pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH ;-------------------------------- ; Uninstaller pages !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH ;-------------------------------- ; Languages !insertmacro MUI_LANGUAGE "English" ;-------------------------------- ; Installer sections Section "Files" SecInstall SectionIn RO SetOutPath "$INSTDIR" File "..\build\src\Release\mosquitto.exe" File "..\build\apps\mosquitto_passwd\Release\mosquitto_passwd.exe" File "..\build\apps\mosquitto_ctrl\Release\mosquitto_ctrl.exe" File "..\build\client\Release\mosquitto_pub.exe" File "..\build\client\Release\mosquitto_sub.exe" File "..\build\client\Release\mosquitto_rr.exe" File "..\build\lib\Release\mosquitto.dll" File "..\build\lib\cpp\Release\mosquittopp.dll" File "..\build\plugins\dynamic-security\Release\mosquitto_dynamic_security.dll" File "..\aclfile.example" File "..\ChangeLog.txt" File "..\mosquitto.conf" File "..\NOTICE.md" File "..\pwfile.example" File "..\README.md" File "..\README-windows.txt" File "..\README-letsencrypt.md" ;File "C:\pthreads\Pre-built.2\dll\x86\pthreadVC2.dll" File "C:\OpenSSL-Win32\bin\libssl-1_1.dll" File "C:\OpenSSL-Win32\bin\libcrypto-1_1.dll" File "..\edl-v10" File "..\epl-v20" SetOutPath "$INSTDIR\devel" File "..\build\lib\Release\mosquitto.lib" File "..\build\lib\cpp\Release\mosquittopp.lib" File "..\include\mosquitto.h" File "..\include\mosquitto_broker.h" File "..\include\mosquitto_plugin.h" File "..\include\mqtt_protocol.h" File "..\lib\cpp\mosquittopp.h" WriteUninstaller "$INSTDIR\Uninstall.exe" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "DisplayName" "Eclipse Mosquitto MQTT broker" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "UninstallString" "$\"$INSTDIR\Uninstall.exe$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "QuietUninstallString" "$\"$INSTDIR\Uninstall.exe$\" /S" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "HelpLink" "https://mosquitto.org/" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "URLInfoAbout" "https://mosquitto.org/" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "DisplayVersion" "${VERSION}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" "NoRepair" "1" WriteRegExpandStr ${env_hklm} MOSQUITTO_DIR $INSTDIR SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 SectionEnd Section "Service" SecService ExecWait '"$INSTDIR\mosquitto.exe" install' SectionEnd Section "Uninstall" ExecWait '"$INSTDIR\mosquitto.exe" uninstall' Delete "$INSTDIR\mosquitto.exe" Delete "$INSTDIR\mosquitto_ctrl.exe" Delete "$INSTDIR\mosquitto_passwd.exe" Delete "$INSTDIR\mosquitto_pub.exe" Delete "$INSTDIR\mosquitto_sub.exe" Delete "$INSTDIR\mosquitto_rr.exe" Delete "$INSTDIR\mosquitto.dll" Delete "$INSTDIR\mosquittopp.dll" Delete "$INSTDIR\mosquitto_dynamic_security.dll" Delete "$INSTDIR\aclfile.example" Delete "$INSTDIR\ChangeLog.txt" Delete "$INSTDIR\mosquitto.conf" Delete "$INSTDIR\pwfile.example" Delete "$INSTDIR\README.md" Delete "$INSTDIR\README-windows.txt" Delete "$INSTDIR\README-letsencrypt.md" ;Delete "$INSTDIR\pthreadVC2.dll" Delete "$INSTDIR\libssl-1_1.dll" Delete "$INSTDIR\libcrypto-1_1.dll" Delete "$INSTDIR\edl-v10" Delete "$INSTDIR\epl-v20" Delete "$INSTDIR\devel\mosquitto.h" Delete "$INSTDIR\devel\mosquitto.lib" Delete "$INSTDIR\devel\mosquitto_broker.h" Delete "$INSTDIR\devel\mosquitto_plugin.h" Delete "$INSTDIR\devel\mosquittopp.h" Delete "$INSTDIR\devel\mosquittopp.lib" Delete "$INSTDIR\devel\mqtt_protocol.h" RMDir "$INSTDIR\devel" Delete "$INSTDIR\Uninstall.exe" RMDir "$INSTDIR" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto" DeleteRegValue ${env_hklm} MOSQUITTO_DIR SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 SectionEnd LangString DESC_SecInstall ${LANG_ENGLISH} "The main installation." LangString DESC_SecService ${LANG_ENGLISH} "Install mosquitto as a Windows service?" !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall) !insertmacro MUI_DESCRIPTION_TEXT ${SecService} $(DESC_SecService) !insertmacro MUI_FUNCTION_DESCRIPTION_END mosquitto-2.0.18/installer/mosquitto64.nsi000066400000000000000000000123021450213760600205760ustar00rootroot00000000000000; NSIS installer script for mosquitto !include "MUI2.nsh" !include "nsDialogs.nsh" !include "LogicLib.nsh" ; For environment variable code !include "WinMessages.nsh" !define env_hklm 'HKLM "SYSTEM\CurrentControlSet\Control\Session Manager\Environment"' Name "Eclipse Mosquitto" !define VERSION 2.0.18 OutFile "mosquitto-${VERSION}-install-windows-x64.exe" !include "x64.nsh" InstallDir "$PROGRAMFILES64\mosquitto" ;-------------------------------- ; Installer pages !insertmacro MUI_PAGE_WELCOME !insertmacro MUI_PAGE_COMPONENTS !insertmacro MUI_PAGE_DIRECTORY !insertmacro MUI_PAGE_INSTFILES !insertmacro MUI_PAGE_FINISH ;-------------------------------- ; Uninstaller pages !insertmacro MUI_UNPAGE_WELCOME !insertmacro MUI_UNPAGE_CONFIRM !insertmacro MUI_UNPAGE_INSTFILES !insertmacro MUI_UNPAGE_FINISH ;-------------------------------- ; Languages !insertmacro MUI_LANGUAGE "English" ;-------------------------------- ; Installer sections Section "Files" SecInstall SectionIn RO SetOutPath "$INSTDIR" File "..\build64\src\Release\mosquitto.exe" File "..\build64\apps\mosquitto_ctrl\Release\mosquitto_ctrl.exe" File "..\build64\apps\mosquitto_passwd\Release\mosquitto_passwd.exe" File "..\build64\client\Release\mosquitto_pub.exe" File "..\build64\client\Release\mosquitto_sub.exe" File "..\build64\client\Release\mosquitto_rr.exe" File "..\build64\lib\Release\mosquitto.dll" File "..\build64\lib\cpp\Release\mosquittopp.dll" File "..\build64\plugins\dynamic-security\Release\mosquitto_dynamic_security.dll" File "..\aclfile.example" File "..\ChangeLog.txt" File "..\mosquitto.conf" File "..\NOTICE.md" File "..\pwfile.example" File "..\README.md" File "..\README-windows.txt" File "..\README-letsencrypt.md" ;File "C:\pthreads\Pre-built.2\dll\x64\pthreadVC2.dll" File "C:\OpenSSL-Win64\bin\libssl-1_1-x64.dll" File "C:\OpenSSL-Win64\bin\libcrypto-1_1-x64.dll" File "..\edl-v10" File "..\epl-v20" SetOutPath "$INSTDIR\devel" File "..\build64\lib\Release\mosquitto.lib" File "..\build64\lib\cpp\Release\mosquittopp.lib" File "..\include\mosquitto.h" File "..\include\mosquitto_broker.h" File "..\include\mosquitto_plugin.h" File "..\include\mqtt_protocol.h" File "..\lib\cpp\mosquittopp.h" WriteUninstaller "$INSTDIR\Uninstall.exe" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "DisplayName" "Eclipse Mosquitto MQTT broker (64 bit)" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "UninstallString" "$\"$INSTDIR\Uninstall.exe$\"" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "QuietUninstallString" "$\"$INSTDIR\Uninstall.exe$\" /S" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "HelpLink" "https://mosquitto.org/" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "URLInfoAbout" "https://mosquitto.org/" WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "DisplayVersion" "${VERSION}" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "NoModify" "1" WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" "NoRepair" "1" WriteRegExpandStr ${env_hklm} MOSQUITTO_DIR $INSTDIR SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 SectionEnd Section "Service" SecService ExecWait '"$INSTDIR\mosquitto.exe" install' SectionEnd Section "Uninstall" ExecWait '"$INSTDIR\mosquitto.exe" uninstall' Delete "$INSTDIR\mosquitto.exe" Delete "$INSTDIR\mosquitto_ctrl.exe" Delete "$INSTDIR\mosquitto_passwd.exe" Delete "$INSTDIR\mosquitto_pub.exe" Delete "$INSTDIR\mosquitto_sub.exe" Delete "$INSTDIR\mosquitto_rr.exe" Delete "$INSTDIR\mosquitto.dll" Delete "$INSTDIR\mosquittopp.dll" Delete "$INSTDIR\mosquitto_dynamic_security.dll" Delete "$INSTDIR\aclfile.example" Delete "$INSTDIR\ChangeLog.txt" Delete "$INSTDIR\mosquitto.conf" Delete "$INSTDIR\pwfile.example" Delete "$INSTDIR\README.md" Delete "$INSTDIR\README-windows.txt" Delete "$INSTDIR\README-letsencrypt.md" ;Delete "$INSTDIR\pthreadVC2.dll" Delete "$INSTDIR\libssl-1_1-x64.dll" Delete "$INSTDIR\libcrypto-1_1-x64.dll" Delete "$INSTDIR\edl-v10" Delete "$INSTDIR\epl-v20" Delete "$INSTDIR\devel\mosquitto.h" Delete "$INSTDIR\devel\mosquitto.lib" Delete "$INSTDIR\devel\mosquitto_broker.h" Delete "$INSTDIR\devel\mosquitto_plugin.h" Delete "$INSTDIR\devel\mosquitto_plugin.h" Delete "$INSTDIR\devel\mosquittopp.h" Delete "$INSTDIR\devel\mosquittopp.lib" Delete "$INSTDIR\devel\mqtt_protocol.h" RMDir "$INSTDIR\devel" Delete "$INSTDIR\Uninstall.exe" RMDir "$INSTDIR" DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\Mosquitto64" DeleteRegValue ${env_hklm} MOSQUITTO_DIR SendMessage ${HWND_BROADCAST} ${WM_WININICHANGE} 0 "STR:Environment" /TIMEOUT=5000 SectionEnd LangString DESC_SecInstall ${LANG_ENGLISH} "The main installation." LangString DESC_SecService ${LANG_ENGLISH} "Install mosquitto as a Windows service?" !insertmacro MUI_FUNCTION_DESCRIPTION_BEGIN !insertmacro MUI_DESCRIPTION_TEXT ${SecInstall} $(DESC_SecInstall) !insertmacro MUI_DESCRIPTION_TEXT ${SecService} $(DESC_SecService) !insertmacro MUI_FUNCTION_DESCRIPTION_END mosquitto-2.0.18/lib/000077500000000000000000000000001450213760600144205ustar00rootroot00000000000000mosquitto-2.0.18/lib/CMakeLists.txt000066400000000000000000000060361450213760600171650ustar00rootroot00000000000000option(WITH_LIB_CPP "Build C++ library?" ON) if (WITH_LIB_CPP) add_subdirectory(cpp) endif (WITH_LIB_CPP) include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/include ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${OPENSSL_INCLUDE_DIR} ${PTHREAD_INCLUDE_DIR}) link_directories(${mosquitto_SOURCE_DIR}/lib) if (WITH_BUNDLED_DEPS) include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/deps) endif (WITH_BUNDLED_DEPS) set(C_SRC actions.c callbacks.c connect.c handle_auth.c handle_connack.c handle_disconnect.c handle_ping.c handle_pubackcomp.c handle_publish.c handle_pubrec.c handle_pubrel.c handle_suback.c handle_unsuback.c helpers.c logging_mosq.c logging_mosq.h loop.c memory_mosq.c memory_mosq.h messages_mosq.c messages_mosq.h misc_mosq.c misc_mosq.h mosquitto.c ../include/mosquitto.h mosquitto_internal.h ../include/mqtt_protocol.h net_mosq_ocsp.c net_mosq.c net_mosq.h options.c packet_datatypes.c packet_mosq.c packet_mosq.h property_mosq.c property_mosq.h read_handle.c read_handle.h send_connect.c send_disconnect.c send_mosq.c send_publish.c send_subscribe.c send_unsubscribe.c send_mosq.c send_mosq.h socks_mosq.c srv_mosq.c strings_mosq.c thread_mosq.c time_mosq.c tls_mosq.c utf8_mosq.c util_mosq.c util_topic.c util_mosq.h will_mosq.c will_mosq.h) set (LIBRARIES ${OPENSSL_LIBRARIES} ${PTHREAD_LIBRARIES}) if (UNIX AND NOT APPLE AND NOT ANDROID) find_library(LIBRT rt) if (LIBRT) set (LIBRARIES ${LIBRARIES} rt) endif (LIBRT) endif (UNIX AND NOT APPLE AND NOT ANDROID) if (WIN32) set (LIBRARIES ${LIBRARIES} ws2_32) endif (WIN32) if (WITH_SRV) # Simple detect c-ares find_path(ARES_HEADER ares.h) if (ARES_HEADER) add_definitions("-DWITH_SRV") set (LIBRARIES ${LIBRARIES} cares) else (ARES_HEADER) message(WARNING "c-ares library not found.") endif (ARES_HEADER) endif (WITH_SRV) add_library(libmosquitto SHARED ${C_SRC}) set_target_properties(libmosquitto PROPERTIES POSITION_INDEPENDENT_CODE 1 ) target_link_libraries(libmosquitto ${LIBRARIES}) set_target_properties(libmosquitto PROPERTIES OUTPUT_NAME mosquitto VERSION ${VERSION} SOVERSION 1 ) install(TARGETS libmosquitto RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") if (WITH_STATIC_LIBRARIES) add_library(libmosquitto_static STATIC ${C_SRC}) if (WITH_PIC) set_target_properties(libmosquitto_static PROPERTIES POSITION_INDEPENDENT_CODE 1 ) endif (WITH_PIC) target_link_libraries(libmosquitto_static ${LIBRARIES}) set_target_properties(libmosquitto_static PROPERTIES OUTPUT_NAME mosquitto_static VERSION ${VERSION} ) target_compile_definitions(libmosquitto_static PUBLIC "LIBMOSQUITTO_STATIC") install(TARGETS libmosquitto_static ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") endif (WITH_STATIC_LIBRARIES) install(FILES ../include/mosquitto.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") install(FILES ../include/mqtt_protocol.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") mosquitto-2.0.18/lib/Makefile000066400000000000000000000163351450213760600160700ustar00rootroot00000000000000include ../config.mk .PHONY : really clean install MOSQ_OBJS=mosquitto.o \ actions.o \ callbacks.o \ connect.o \ handle_auth.o \ handle_connack.o \ handle_disconnect.o \ handle_ping.o \ handle_pubackcomp.o \ handle_publish.o \ handle_pubrec.o \ handle_pubrel.o \ handle_suback.o \ handle_unsuback.o \ helpers.o \ logging_mosq.o \ loop.o \ memory_mosq.o \ messages_mosq.o \ misc_mosq.o \ net_mosq_ocsp.o \ net_mosq.o \ options.o \ packet_datatypes.o \ packet_mosq.o \ property_mosq.o \ read_handle.o \ send_connect.o \ send_disconnect.o \ send_mosq.o \ send_publish.o \ send_subscribe.o \ send_unsubscribe.o \ socks_mosq.o \ srv_mosq.o \ strings_mosq.o \ thread_mosq.o \ time_mosq.o \ tls_mosq.o \ utf8_mosq.o \ util_mosq.o \ util_topic.o \ will_mosq.o ALL_DEPS:= ifeq ($(WITH_SHARED_LIBRARIES),yes) ALL_DEPS+=libmosquitto.so.${SOVERSION} endif ifeq ($(WITH_STATIC_LIBRARIES),yes) ALL_DEPS+=libmosquitto.a endif all : ${ALL_DEPS} ifeq ($(WITH_SHARED_LIBRARIES),yes) $(MAKE) -C cpp endif install : all $(INSTALL) -d "${DESTDIR}${libdir}/" ifeq ($(WITH_SHARED_LIBRARIES),yes) $(INSTALL) ${STRIP_OPTS} libmosquitto.so.${SOVERSION} "${DESTDIR}${libdir}/libmosquitto.so.${SOVERSION}" ln -sf libmosquitto.so.${SOVERSION} "${DESTDIR}${libdir}/libmosquitto.so" endif ifeq ($(WITH_STATIC_LIBRARIES),yes) $(INSTALL) ${STRIP_OPTS} libmosquitto.a "${DESTDIR}${libdir}/libmosquitto.a" endif $(INSTALL) -d "${DESTDIR}${incdir}/" $(INSTALL) ../include/mosquitto.h "${DESTDIR}${incdir}/mosquitto.h" $(INSTALL) ../include/mqtt_protocol.h "${DESTDIR}${incdir}/mqtt_protocol.h" $(INSTALL) -d "${DESTDIR}${libdir}/pkgconfig" $(INSTALL) -m644 ../libmosquitto.pc.in "${DESTDIR}${libdir}/pkgconfig/libmosquitto.pc" sed ${SEDINPLACE} -e "s#@CMAKE_INSTALL_PREFIX@#${prefix}#" -e "s#@VERSION@#${VERSION}#" "${DESTDIR}${libdir}/pkgconfig/libmosquitto.pc" ifeq ($(WITH_SHARED_LIBRARIES),yes) $(MAKE) -C cpp install endif uninstall : -rm -f "${DESTDIR}${libdir}/libmosquitto.so.${SOVERSION}" -rm -f "${DESTDIR}${libdir}/libmosquitto.so" -rm -f "${DESTDIR}${libdir}/libmosquitto.a" -rm -f "${DESTDIR}${incdir}/mosquitto.h" reallyclean : clean clean : -rm -f *.o libmosquitto.so.${SOVERSION} libmosquitto.so libmosquitto.a *.gcno *.gcda $(MAKE) -C cpp clean libmosquitto.so.${SOVERSION} : ${MOSQ_OBJS} ${CROSS_COMPILE}$(CC) -shared $(LIB_LDFLAGS) $^ -o $@ ${LIB_LIBADD} libmosquitto.a : ${MOSQ_OBJS} ${CROSS_COMPILE}$(AR) cr $@ $^ mosquitto.o : mosquitto.c ../include/mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ actions.o : actions.c ../include/mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ callbacks.o : callbacks.c ../include/mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ connect.o : connect.c ../include/mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_auth.o : handle_auth.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_connack.o : handle_connack.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_disconnect.o : handle_disconnect.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_publish.o : handle_publish.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_ping.o : handle_ping.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_pubackcomp.o : handle_pubackcomp.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_pubrec.o : handle_pubrec.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_pubrel.o : handle_pubrel.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_suback.o : handle_suback.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ handle_unsuback.o : handle_unsuback.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ helpers.o : helpers.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ logging_mosq.o : logging_mosq.c logging_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ loop.o : loop.c ../include/mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ messages_mosq.o : messages_mosq.c messages_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ memory_mosq.o : memory_mosq.c memory_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ misc_mosq.o : misc_mosq.c misc_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ net_mosq_ocsp.o : net_mosq_ocsp.c net_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ net_mosq.o : net_mosq.c net_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ options.o : options.c ../include/mosquitto.h mosquitto_internal.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ packet_datatypes.o : packet_datatypes.c packet_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ packet_mosq.o : packet_mosq.c packet_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ property_mosq.o : property_mosq.c property_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ read_handle.o : read_handle.c read_handle.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ send_connect.o : send_connect.c send_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ send_disconnect.o : send_disconnect.c send_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ send_mosq.o : send_mosq.c send_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ send_publish.o : send_publish.c send_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ send_subscribe.o : send_subscribe.c send_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ send_unsubscribe.o : send_unsubscribe.c send_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ socks_mosq.o : socks_mosq.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ srv_mosq.o : srv_mosq.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ strings_mosq.o : strings_mosq.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ thread_mosq.o : thread_mosq.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ time_mosq.o : time_mosq.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ tls_mosq.o : tls_mosq.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ utf8_mosq.o : utf8_mosq.c ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ util_mosq.o : util_mosq.c util_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ util_topic.o : util_topic.c util_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ will_mosq.o : will_mosq.c will_mosq.h ${CROSS_COMPILE}$(CC) $(LIB_CPPFLAGS) $(LIB_CFLAGS) -c $< -o $@ mosquitto-2.0.18/lib/actions.c000066400000000000000000000207231450213760600162300ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include "mosquitto.h" #include "mosquitto_internal.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "send_mosq.h" #include "util_mosq.h" int mosquitto_publish(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain) { return mosquitto_publish_v5(mosq, mid, topic, payloadlen, payload, qos, retain, NULL); } int mosquitto_publish_v5(struct mosquitto *mosq, int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, const mosquitto_property *properties) { struct mosquitto_message_all *message; uint16_t local_mid; const mosquitto_property *p; const mosquitto_property *outgoing_properties = NULL; mosquitto_property *properties_copy = NULL; mosquitto_property local_property; bool have_topic_alias; int rc; size_t tlen = 0; uint32_t remaining_length; if(!mosq || qos<0 || qos>2) return MOSQ_ERR_INVAL; if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; if(qos > mosq->max_qos) return MOSQ_ERR_QOS_NOT_SUPPORTED; if(!mosq->retain_available){ retain = false; } if(properties){ if(properties->client_generated){ outgoing_properties = properties; }else{ memcpy(&local_property, properties, sizeof(mosquitto_property)); local_property.client_generated = true; local_property.next = NULL; outgoing_properties = &local_property; } rc = mosquitto_property_check_all(CMD_PUBLISH, outgoing_properties); if(rc) return rc; } if(!topic || STREMPTY(topic)){ if(topic) topic = NULL; if(mosq->protocol == mosq_p_mqtt5){ p = outgoing_properties; have_topic_alias = false; while(p){ if(p->identifier == MQTT_PROP_TOPIC_ALIAS){ have_topic_alias = true; break; } p = p->next; } if(have_topic_alias == false){ return MOSQ_ERR_INVAL; } }else{ return MOSQ_ERR_INVAL; } }else{ tlen = strlen(topic); if(mosquitto_validate_utf8(topic, (int)tlen)) return MOSQ_ERR_MALFORMED_UTF8; if(payloadlen < 0 || payloadlen > (int)MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; if(mosquitto_pub_topic_check(topic) != MOSQ_ERR_SUCCESS){ return MOSQ_ERR_INVAL; } } if(mosq->maximum_packet_size > 0){ remaining_length = 1 + 2+(uint32_t)tlen + (uint32_t)payloadlen + property__get_length_all(outgoing_properties); if(qos > 0){ remaining_length++; } if(packet__check_oversize(mosq, remaining_length)){ return MOSQ_ERR_OVERSIZE_PACKET; } } local_mid = mosquitto__mid_generate(mosq); if(mid){ *mid = local_mid; } if(qos == 0){ return send__publish(mosq, local_mid, topic, (uint32_t)payloadlen, payload, (uint8_t)qos, retain, false, outgoing_properties, NULL, 0); }else{ if(outgoing_properties){ rc = mosquitto_property_copy_all(&properties_copy, outgoing_properties); if(rc) return rc; } message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!message){ mosquitto_property_free_all(&properties_copy); return MOSQ_ERR_NOMEM; } message->next = NULL; message->timestamp = mosquitto_time(); message->msg.mid = local_mid; if(topic){ message->msg.topic = mosquitto__strdup(topic); if(!message->msg.topic){ message__cleanup(&message); mosquitto_property_free_all(&properties_copy); return MOSQ_ERR_NOMEM; } } if(payloadlen){ message->msg.payloadlen = payloadlen; message->msg.payload = mosquitto__malloc((unsigned int)payloadlen*sizeof(uint8_t)); if(!message->msg.payload){ message__cleanup(&message); mosquitto_property_free_all(&properties_copy); return MOSQ_ERR_NOMEM; } memcpy(message->msg.payload, payload, (uint32_t)payloadlen*sizeof(uint8_t)); }else{ message->msg.payloadlen = 0; message->msg.payload = NULL; } message->msg.qos = (uint8_t)qos; message->msg.retain = retain; message->dup = false; message->properties = properties_copy; pthread_mutex_lock(&mosq->msgs_out.mutex); message->state = mosq_ms_invalid; rc = message__queue(mosq, message, mosq_md_out); pthread_mutex_unlock(&mosq->msgs_out.mutex); return rc; } } int mosquitto_subscribe(struct mosquitto *mosq, int *mid, const char *sub, int qos) { return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, 0, NULL); } int mosquitto_subscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, int qos, int options, const mosquitto_property *properties) { return mosquitto_subscribe_multiple(mosq, mid, 1, (char *const *const)&sub, qos, options, properties); } int mosquitto_subscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, int qos, int options, const mosquitto_property *properties) { const mosquitto_property *outgoing_properties = NULL; mosquitto_property local_property; int i; int rc; uint32_t remaining_length = 0; int slen; if(!mosq || !sub_count || !sub) return MOSQ_ERR_INVAL; if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; if(qos < 0 || qos > 2) return MOSQ_ERR_INVAL; if((options & 0x30) == 0x30 || (options & 0xC0) != 0) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; if(properties){ if(properties->client_generated){ outgoing_properties = properties; }else{ memcpy(&local_property, properties, sizeof(mosquitto_property)); local_property.client_generated = true; local_property.next = NULL; outgoing_properties = &local_property; } rc = mosquitto_property_check_all(CMD_SUBSCRIBE, outgoing_properties); if(rc) return rc; } for(i=0; imaximum_packet_size > 0){ remaining_length += 2 + property__get_length_all(outgoing_properties); if(packet__check_oversize(mosq, remaining_length)){ return MOSQ_ERR_OVERSIZE_PACKET; } } if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){ options = 0; } return send__subscribe(mosq, mid, sub_count, sub, qos|options, outgoing_properties); } int mosquitto_unsubscribe(struct mosquitto *mosq, int *mid, const char *sub) { return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, NULL); } int mosquitto_unsubscribe_v5(struct mosquitto *mosq, int *mid, const char *sub, const mosquitto_property *properties) { return mosquitto_unsubscribe_multiple(mosq, mid, 1, (char *const *const)&sub, properties); } int mosquitto_unsubscribe_multiple(struct mosquitto *mosq, int *mid, int sub_count, char *const *const sub, const mosquitto_property *properties) { const mosquitto_property *outgoing_properties = NULL; mosquitto_property local_property; int rc; int i; uint32_t remaining_length = 0; int slen; if(!mosq) return MOSQ_ERR_INVAL; if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; if(properties){ if(properties->client_generated){ outgoing_properties = properties; }else{ memcpy(&local_property, properties, sizeof(mosquitto_property)); local_property.client_generated = true; local_property.next = NULL; outgoing_properties = &local_property; } rc = mosquitto_property_check_all(CMD_UNSUBSCRIBE, outgoing_properties); if(rc) return rc; } for(i=0; imaximum_packet_size > 0){ remaining_length += 2U + property__get_length_all(outgoing_properties); if(packet__check_oversize(mosq, remaining_length)){ return MOSQ_ERR_OVERSIZE_PACKET; } } return send__unsubscribe(mosq, mid, sub_count, sub, outgoing_properties); } mosquitto-2.0.18/lib/alias_mosq.c000066400000000000000000000040271450213760600167170ustar00rootroot00000000000000/* Copyright (c) 2019-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto.h" #include "alias_mosq.h" #include "memory_mosq.h" int alias__add(struct mosquitto *mosq, const char *topic, uint16_t alias) { int i; struct mosquitto__alias *aliases; for(i=0; ialias_count; i++){ if(mosq->aliases[i].alias == alias){ mosquitto__free(mosq->aliases[i].topic); mosq->aliases[i].topic = mosquitto__strdup(topic); if(mosq->aliases[i].topic){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOMEM; } } } /* New alias */ aliases = mosquitto__realloc(mosq->aliases, sizeof(struct mosquitto__alias)*(size_t)(mosq->alias_count+1)); if(!aliases) return MOSQ_ERR_NOMEM; mosq->aliases = aliases; mosq->aliases[mosq->alias_count].alias = alias; mosq->aliases[mosq->alias_count].topic = mosquitto__strdup(topic); if(!mosq->aliases[mosq->alias_count].topic){ return MOSQ_ERR_NOMEM; } mosq->alias_count++; return MOSQ_ERR_SUCCESS; } int alias__find(struct mosquitto *mosq, char **topic, uint16_t alias) { int i; for(i=0; ialias_count; i++){ if(mosq->aliases[i].alias == alias){ *topic = mosquitto__strdup(mosq->aliases[i].topic); if(*topic){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOMEM; } } } return MOSQ_ERR_INVAL; } void alias__free_all(struct mosquitto *mosq) { int i; for(i=0; ialias_count; i++){ mosquitto__free(mosq->aliases[i].topic); } mosquitto__free(mosq->aliases); mosq->aliases = NULL; mosq->alias_count = 0; } mosquitto-2.0.18/lib/alias_mosq.h000066400000000000000000000015351450213760600167250ustar00rootroot00000000000000/* Copyright (c) 2019-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef ALIAS_MOSQ_H #define ALIAS_MOSQ_H #include "mosquitto_internal.h" int alias__add(struct mosquitto *mosq, const char *topic, uint16_t alias); int alias__find(struct mosquitto *mosq, char **topic, uint16_t alias); void alias__free_all(struct mosquitto *mosq); #endif mosquitto-2.0.18/lib/callbacks.c000066400000000000000000000105501450213760600165040ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto.h" #include "mosquitto_internal.h" void mosquitto_connect_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_connect = on_connect; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_connect_with_flags_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_connect_with_flags = on_connect; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_connect_v5_callback_set(struct mosquitto *mosq, void (*on_connect)(struct mosquitto *, void *, int, int, const mosquitto_property *)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_connect_v5 = on_connect; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_disconnect_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_disconnect = on_disconnect; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_disconnect_v5_callback_set(struct mosquitto *mosq, void (*on_disconnect)(struct mosquitto *, void *, int, const mosquitto_property *)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_disconnect_v5 = on_disconnect; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_publish_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_publish = on_publish; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_publish_v5_callback_set(struct mosquitto *mosq, void (*on_publish)(struct mosquitto *, void *, int, int, const mosquitto_property *props)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_publish_v5 = on_publish; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_message_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_message = on_message; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_message_v5_callback_set(struct mosquitto *mosq, void (*on_message)(struct mosquitto *, void *, const struct mosquitto_message *, const mosquitto_property *props)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_message_v5 = on_message; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_subscribe_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_subscribe = on_subscribe; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_subscribe_v5_callback_set(struct mosquitto *mosq, void (*on_subscribe)(struct mosquitto *, void *, int, int, const int *, const mosquitto_property *props)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_subscribe_v5 = on_subscribe; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_unsubscribe_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_unsubscribe = on_unsubscribe; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_unsubscribe_v5_callback_set(struct mosquitto *mosq, void (*on_unsubscribe)(struct mosquitto *, void *, int, const mosquitto_property *props)) { pthread_mutex_lock(&mosq->callback_mutex); mosq->on_unsubscribe_v5 = on_unsubscribe; pthread_mutex_unlock(&mosq->callback_mutex); } void mosquitto_log_callback_set(struct mosquitto *mosq, void (*on_log)(struct mosquitto *, void *, int, const char *)) { pthread_mutex_lock(&mosq->log_callback_mutex); mosq->on_log = on_log; pthread_mutex_unlock(&mosq->log_callback_mutex); } mosquitto-2.0.18/lib/connect.c000066400000000000000000000203651450213760600162230ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "messages_mosq.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "send_mosq.h" #include "socks_mosq.h" #include "util_mosq.h" static char alphanum[] = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking); static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive); static int mosquitto__connect_init(struct mosquitto *mosq, const char *host, int port, int keepalive) { int i; int rc; if(!mosq) return MOSQ_ERR_INVAL; if(!host || port < 0 || port > UINT16_MAX) return MOSQ_ERR_INVAL; if(keepalive != 0 && (keepalive < 5 || keepalive > UINT16_MAX)) return MOSQ_ERR_INVAL; /* Only MQTT v3.1 requires a client id to be sent */ if(mosq->id == NULL && (mosq->protocol == mosq_p_mqtt31)){ mosq->id = (char *)mosquitto__calloc(24, sizeof(char)); if(!mosq->id){ return MOSQ_ERR_NOMEM; } mosq->id[0] = 'm'; mosq->id[1] = 'o'; mosq->id[2] = 's'; mosq->id[3] = 'q'; mosq->id[4] = '-'; rc = util__random_bytes(&mosq->id[5], 18); if(rc) return rc; for(i=5; i<23; i++){ mosq->id[i] = alphanum[(mosq->id[i]&0x7F)%(sizeof(alphanum)-1)]; } } mosquitto__free(mosq->host); mosq->host = mosquitto__strdup(host); if(!mosq->host) return MOSQ_ERR_NOMEM; mosq->port = (uint16_t)port; mosq->keepalive = (uint16_t)keepalive; mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum; mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; mosq->retain_available = 1; mosquitto__set_request_disconnect(mosq, false); return MOSQ_ERR_SUCCESS; } int mosquitto_connect(struct mosquitto *mosq, const char *host, int port, int keepalive) { return mosquitto_connect_bind(mosq, host, port, keepalive, NULL); } int mosquitto_connect_bind(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) { return mosquitto_connect_bind_v5(mosq, host, port, keepalive, bind_address, NULL); } int mosquitto_connect_bind_v5(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address, const mosquitto_property *properties) { int rc; if(bind_address){ rc = mosquitto_string_option(mosq, MOSQ_OPT_BIND_ADDRESS, bind_address); if(rc) return rc; } mosquitto_property_free_all(&mosq->connect_properties); if(properties){ rc = mosquitto_property_check_all(CMD_CONNECT, properties); if(rc) return rc; rc = mosquitto_property_copy_all(&mosq->connect_properties, properties); if(rc) return rc; mosq->connect_properties->client_generated = true; } rc = mosquitto__connect_init(mosq, host, port, keepalive); if(rc) return rc; mosquitto__set_state(mosq, mosq_cs_new); return mosquitto__reconnect(mosq, true); } int mosquitto_connect_async(struct mosquitto *mosq, const char *host, int port, int keepalive) { return mosquitto_connect_bind_async(mosq, host, port, keepalive, NULL); } int mosquitto_connect_bind_async(struct mosquitto *mosq, const char *host, int port, int keepalive, const char *bind_address) { int rc; if(bind_address){ rc = mosquitto_string_option(mosq, MOSQ_OPT_BIND_ADDRESS, bind_address); if(rc) return rc; } rc = mosquitto__connect_init(mosq, host, port, keepalive); if(rc) return rc; return mosquitto__reconnect(mosq, false); } int mosquitto_reconnect_async(struct mosquitto *mosq) { return mosquitto__reconnect(mosq, false); } int mosquitto_reconnect(struct mosquitto *mosq) { return mosquitto__reconnect(mosq, true); } static int mosquitto__reconnect(struct mosquitto *mosq, bool blocking) { const mosquitto_property *outgoing_properties = NULL; mosquitto_property local_property; int rc; if(!mosq) return MOSQ_ERR_INVAL; if(!mosq->host) return MOSQ_ERR_INVAL; if(mosq->connect_properties){ if(mosq->protocol != mosq_p_mqtt5) return MOSQ_ERR_NOT_SUPPORTED; if(mosq->connect_properties->client_generated){ outgoing_properties = mosq->connect_properties; }else{ memcpy(&local_property, mosq->connect_properties, sizeof(mosquitto_property)); local_property.client_generated = true; local_property.next = NULL; outgoing_properties = &local_property; } rc = mosquitto_property_check_all(CMD_CONNECT, outgoing_properties); if(rc) return rc; } pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = mosquitto_time(); mosq->next_msg_out = mosq->last_msg_in + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); mosq->ping_t = 0; packet__cleanup(&mosq->in_packet); packet__cleanup_all(mosq); message__reconnect_reset(mosq, false); if(mosq->sock != INVALID_SOCKET){ net__socket_close(mosq); } #ifdef WITH_SOCKS if(mosq->socks5_host){ rc = net__socket_connect(mosq, mosq->socks5_host, mosq->socks5_port, mosq->bind_address, blocking); }else #endif { rc = net__socket_connect(mosq, mosq->host, mosq->port, mosq->bind_address, blocking); } if(rc>0){ mosquitto__set_state(mosq, mosq_cs_connect_pending); return rc; } #ifdef WITH_SOCKS if(mosq->socks5_host){ mosquitto__set_state(mosq, mosq_cs_socks5_new); return socks5__send(mosq); }else #endif { mosquitto__set_state(mosq, mosq_cs_connected); rc = send__connect(mosq, mosq->keepalive, mosq->clean_start, outgoing_properties); if(rc){ packet__cleanup_all(mosq); net__socket_close(mosq); mosquitto__set_state(mosq, mosq_cs_new); } return rc; } } int mosquitto_disconnect(struct mosquitto *mosq) { return mosquitto_disconnect_v5(mosq, 0, NULL); } int mosquitto_disconnect_v5(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties) { const mosquitto_property *outgoing_properties = NULL; mosquitto_property local_property; int rc; if(!mosq) return MOSQ_ERR_INVAL; if(mosq->protocol != mosq_p_mqtt5 && properties) return MOSQ_ERR_NOT_SUPPORTED; if(reason_code < 0 || reason_code > UINT8_MAX) return MOSQ_ERR_INVAL; if(properties){ if(properties->client_generated){ outgoing_properties = properties; }else{ memcpy(&local_property, properties, sizeof(mosquitto_property)); local_property.client_generated = true; local_property.next = NULL; outgoing_properties = &local_property; } rc = mosquitto_property_check_all(CMD_DISCONNECT, outgoing_properties); if(rc) return rc; } mosquitto__set_state(mosq, mosq_cs_disconnected); mosquitto__set_request_disconnect(mosq, true); if(mosq->sock == INVALID_SOCKET){ return MOSQ_ERR_NO_CONN; }else{ return send__disconnect(mosq, (uint8_t)reason_code, outgoing_properties); } } void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties) { mosquitto__set_state(mosq, mosq_cs_disconnected); net__socket_close(mosq); /* Free data and reset values */ pthread_mutex_lock(&mosq->out_packet_mutex); mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } mosq->out_packet_count--; } pthread_mutex_unlock(&mosq->out_packet_mutex); pthread_mutex_lock(&mosq->msgtime_mutex); mosq->next_msg_out = mosquitto_time() + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, reason_code); mosq->in_callback = false; } if(mosq->on_disconnect_v5){ mosq->in_callback = true; mosq->on_disconnect_v5(mosq, mosq->userdata, reason_code, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); pthread_mutex_unlock(&mosq->current_out_packet_mutex); } mosquitto-2.0.18/lib/cpp/000077500000000000000000000000001450213760600152025ustar00rootroot00000000000000mosquitto-2.0.18/lib/cpp/CMakeLists.txt000066400000000000000000000024351450213760600177460ustar00rootroot00000000000000include_directories(${mosquitto_SOURCE_DIR}/lib ${mosquitto_SOURCE_DIR}/lib/cpp ${mosquitto_SOURCE_DIR}/include ${STDBOOL_H_PATH} ${STDINT_H_PATH}) link_directories(${mosquitto_BINARY_DIR}/lib) set(CPP_SRC mosquittopp.cpp mosquittopp.h) add_library(mosquittopp SHARED ${CPP_SRC}) set_target_properties(mosquittopp PROPERTIES POSITION_INDEPENDENT_CODE 1 ) target_link_libraries(mosquittopp libmosquitto) set_target_properties(mosquittopp PROPERTIES VERSION ${VERSION} SOVERSION 1 ) install(TARGETS mosquittopp RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") if (WITH_STATIC_LIBRARIES) add_library(mosquittopp_static STATIC ${C_SRC} ${CPP_SRC} ) if (WITH_PIC) set_target_properties(mosquittopp_static PROPERTIES POSITION_INDEPENDENT_CODE 1 ) endif (WITH_PIC) target_link_libraries(mosquittopp_static ${LIBRARIES}) set_target_properties(mosquittopp_static PROPERTIES OUTPUT_NAME mosquittopp_static VERSION ${VERSION} ) target_compile_definitions(mosquittopp_static PUBLIC "LIBMOSQUITTO_STATIC") install(TARGETS mosquittopp_static ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") endif (WITH_STATIC_LIBRARIES) install(FILES mosquittopp.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") mosquitto-2.0.18/lib/cpp/Makefile000066400000000000000000000032611450213760600166440ustar00rootroot00000000000000include ../../config.mk ifneq ($(UNAME),SunOS) LIB_LDFLAGS:=$(LDFLAGS) -Wl,-soname,libmosquittopp.so.${SOVERSION} endif .PHONY : clean install ALL_DEPS=libmosquittopp.so.${SOVERSION} ifeq ($(WITH_STATIC_LIBRARIES),yes) ALL_DEPS+=libmosquittopp.a endif all : ${ALL_DEPS} install : all $(INSTALL) -d "${DESTDIR}${libdir}/" $(INSTALL) ${STRIP_OPTS} libmosquittopp.so.${SOVERSION} "${DESTDIR}${libdir}/libmosquittopp.so.${SOVERSION}" ln -sf libmosquittopp.so.${SOVERSION} "${DESTDIR}${libdir}/libmosquittopp.so" ifeq ($(WITH_STATIC_LIBRARIES),yes) $(INSTALL) libmosquittopp.a "${DESTDIR}${libdir}/libmosquittopp.a" ${CROSS_COMPILE}${STRIP} -g --strip-unneeded "${DESTDIR}${libdir}/libmosquittopp.a" endif $(INSTALL) -d "${DESTDIR}${incdir}/" $(INSTALL) mosquittopp.h "${DESTDIR}${incdir}/mosquittopp.h" $(INSTALL) -d "${DESTDIR}${libdir}/pkgconfig/" $(INSTALL) -m644 ../../libmosquittopp.pc.in "${DESTDIR}${libdir}/pkgconfig/libmosquittopp.pc" sed ${SEDINPLACE} -e "s#@CMAKE_INSTALL_PREFIX@#${prefix}#" -e "s#@VERSION@#${VERSION}#" "${DESTDIR}${libdir}/pkgconfig/libmosquittopp.pc" uninstall : -rm -f "${DESTDIR}${libdir}/libmosquittopp.so.${SOVERSION}" -rm -f "${DESTDIR}${libdir}/libmosquittopp.so" -rm -f "${DESTDIR}${libdir}/libmosquittopp.a" -rm -f "${DESTDIR}${incdir}/mosquittopp.h" clean : -rm -f *.o libmosquittopp.so.${SOVERSION} libmosquittopp.a libmosquittopp.so.${SOVERSION} : mosquittopp.o ${CROSS_COMPILE}$(CXX) -shared $(LIB_LDFLAGS) $< -o $@ ../libmosquitto.so.${SOVERSION} $(LIB_LIDADD) libmosquittopp.a : mosquittopp.o ${CROSS_COMPILE}$(AR) cr $@ $^ mosquittopp.o : mosquittopp.cpp mosquittopp.h ${CROSS_COMPILE}$(CXX) $(LIB_CPPFLAGS) $(LIB_CXXFLAGS) -c $< -o $@ mosquitto-2.0.18/lib/cpp/mosquittopp.cpp000066400000000000000000000233451450213760600203210ustar00rootroot00000000000000/* Copyright (c) 2010-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. Contributors: Roger Light - initial implementation and documentation. */ #include #include #include #define UNUSED(A) (void)(A) namespace mosqpp { static void on_connect_wrapper(struct mosquitto *mosq, void *userdata, int rc) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_connect(rc); } static void on_connect_with_flags_wrapper(struct mosquitto *mosq, void *userdata, int rc, int flags) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_connect_with_flags(rc, flags); } static void on_disconnect_wrapper(struct mosquitto *mosq, void *userdata, int rc) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_disconnect(rc); } static void on_publish_wrapper(struct mosquitto *mosq, void *userdata, int mid) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_publish(mid); } static void on_message_wrapper(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_message(message); } static void on_subscribe_wrapper(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_subscribe(mid, qos_count, granted_qos); } static void on_unsubscribe_wrapper(struct mosquitto *mosq, void *userdata, int mid) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_unsubscribe(mid); } static void on_log_wrapper(struct mosquitto *mosq, void *userdata, int level, const char *str) { class mosquittopp *m = (class mosquittopp *)userdata; UNUSED(mosq); m->on_log(level, str); } int lib_version(int *major, int *minor, int *revision) { if(major) *major = LIBMOSQUITTO_MAJOR; if(minor) *minor = LIBMOSQUITTO_MINOR; if(revision) *revision = LIBMOSQUITTO_REVISION; return LIBMOSQUITTO_VERSION_NUMBER; } int lib_init() { return mosquitto_lib_init(); } int lib_cleanup() { return mosquitto_lib_cleanup(); } const char* strerror(int mosq_errno) { return mosquitto_strerror(mosq_errno); } const char* connack_string(int connack_code) { return mosquitto_connack_string(connack_code); } int sub_topic_tokenise(const char *subtopic, char ***topics, int *count) { return mosquitto_sub_topic_tokenise(subtopic, topics, count); } int sub_topic_tokens_free(char ***topics, int count) { return mosquitto_sub_topic_tokens_free(topics, count); } int topic_matches_sub(const char *sub, const char *topic, bool *result) { return mosquitto_topic_matches_sub(sub, topic, result); } int validate_utf8(const char *str, int len) { return mosquitto_validate_utf8(str, len); } int subscribe_simple( struct mosquitto_message **messages, int msg_count, bool retained, const char *topic, int qos, const char *host, int port, const char *client_id, int keepalive, bool clean_session, const char *username, const char *password, const struct libmosquitto_will *will, const struct libmosquitto_tls *tls) { return mosquitto_subscribe_simple( messages, msg_count, retained, topic, qos, host, port, client_id, keepalive, clean_session, username, password, will, tls); } mosqpp_EXPORT int subscribe_callback( int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *), void *userdata, const char *topic, int qos, const char *host, int port, const char *client_id, int keepalive, bool clean_session, const char *username, const char *password, const struct libmosquitto_will *will, const struct libmosquitto_tls *tls) { return mosquitto_subscribe_callback( callback, userdata, topic, qos, host, port, client_id, keepalive, clean_session, username, password, will, tls); } mosquittopp::mosquittopp(const char *id, bool clean_session) { m_mosq = mosquitto_new(id, clean_session, this); mosquitto_connect_callback_set(m_mosq, on_connect_wrapper); mosquitto_connect_with_flags_callback_set(m_mosq, on_connect_with_flags_wrapper); mosquitto_disconnect_callback_set(m_mosq, on_disconnect_wrapper); mosquitto_publish_callback_set(m_mosq, on_publish_wrapper); mosquitto_message_callback_set(m_mosq, on_message_wrapper); mosquitto_subscribe_callback_set(m_mosq, on_subscribe_wrapper); mosquitto_unsubscribe_callback_set(m_mosq, on_unsubscribe_wrapper); mosquitto_log_callback_set(m_mosq, on_log_wrapper); } mosquittopp::~mosquittopp() { mosquitto_destroy(m_mosq); } int mosquittopp::reinitialise(const char *id, bool clean_session) { int rc; rc = mosquitto_reinitialise(m_mosq, id, clean_session, this); if(rc == MOSQ_ERR_SUCCESS){ mosquitto_connect_callback_set(m_mosq, on_connect_wrapper); mosquitto_connect_with_flags_callback_set(m_mosq, on_connect_with_flags_wrapper); mosquitto_disconnect_callback_set(m_mosq, on_disconnect_wrapper); mosquitto_publish_callback_set(m_mosq, on_publish_wrapper); mosquitto_message_callback_set(m_mosq, on_message_wrapper); mosquitto_subscribe_callback_set(m_mosq, on_subscribe_wrapper); mosquitto_unsubscribe_callback_set(m_mosq, on_unsubscribe_wrapper); mosquitto_log_callback_set(m_mosq, on_log_wrapper); } return rc; } int mosquittopp::connect(const char *host, int port, int keepalive) { return mosquitto_connect(m_mosq, host, port, keepalive); } int mosquittopp::connect(const char *host, int port, int keepalive, const char *bind_address) { return mosquitto_connect_bind(m_mosq, host, port, keepalive, bind_address); } int mosquittopp::connect_async(const char *host, int port, int keepalive) { return mosquitto_connect_async(m_mosq, host, port, keepalive); } int mosquittopp::connect_async(const char *host, int port, int keepalive, const char *bind_address) { return mosquitto_connect_bind_async(m_mosq, host, port, keepalive, bind_address); } int mosquittopp::reconnect() { return mosquitto_reconnect(m_mosq); } int mosquittopp::reconnect_async() { return mosquitto_reconnect_async(m_mosq); } int mosquittopp::disconnect() { return mosquitto_disconnect(m_mosq); } int mosquittopp::socket() { return mosquitto_socket(m_mosq); } int mosquittopp::will_set(const char *topic, int payloadlen, const void *payload, int qos, bool retain) { return mosquitto_will_set(m_mosq, topic, payloadlen, payload, qos, retain); } int mosquittopp::will_clear() { return mosquitto_will_clear(m_mosq); } int mosquittopp::username_pw_set(const char *username, const char *password) { return mosquitto_username_pw_set(m_mosq, username, password); } int mosquittopp::publish(int *mid, const char *topic, int payloadlen, const void *payload, int qos, bool retain) { return mosquitto_publish(m_mosq, mid, topic, payloadlen, payload, qos, retain); } void mosquittopp::reconnect_delay_set(unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff) { mosquitto_reconnect_delay_set(m_mosq, reconnect_delay, reconnect_delay_max, reconnect_exponential_backoff); } int mosquittopp::max_inflight_messages_set(unsigned int max_inflight_messages) { return mosquitto_max_inflight_messages_set(m_mosq, max_inflight_messages); } void mosquittopp::message_retry_set(unsigned int message_retry) { mosquitto_message_retry_set(m_mosq, message_retry); } int mosquittopp::subscribe(int *mid, const char *sub, int qos) { return mosquitto_subscribe(m_mosq, mid, sub, qos); } int mosquittopp::unsubscribe(int *mid, const char *sub) { return mosquitto_unsubscribe(m_mosq, mid, sub); } int mosquittopp::loop(int timeout, int max_packets) { return mosquitto_loop(m_mosq, timeout, max_packets); } int mosquittopp::loop_misc() { return mosquitto_loop_misc(m_mosq); } int mosquittopp::loop_read(int max_packets) { return mosquitto_loop_read(m_mosq, max_packets); } int mosquittopp::loop_write(int max_packets) { return mosquitto_loop_write(m_mosq, max_packets); } int mosquittopp::loop_forever(int timeout, int max_packets) { return mosquitto_loop_forever(m_mosq, timeout, max_packets); } int mosquittopp::loop_start() { return mosquitto_loop_start(m_mosq); } int mosquittopp::loop_stop(bool force) { return mosquitto_loop_stop(m_mosq, force); } bool mosquittopp::want_write() { return mosquitto_want_write(m_mosq); } int mosquittopp::opts_set(enum mosq_opt_t option, void *value) { return mosquitto_opts_set(m_mosq, option, value); } int mosquittopp::threaded_set(bool threaded) { return mosquitto_threaded_set(m_mosq, threaded); } void mosquittopp::user_data_set(void *userdata) { mosquitto_user_data_set(m_mosq, userdata); } int mosquittopp::socks5_set(const char *host, int port, const char *username, const char *password) { return mosquitto_socks5_set(m_mosq, host, port, username, password); } int 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)) { return mosquitto_tls_set(m_mosq, cafile, capath, certfile, keyfile, pw_callback); } int mosquittopp::tls_opts_set(int cert_reqs, const char *tls_version, const char *ciphers) { return mosquitto_tls_opts_set(m_mosq, cert_reqs, tls_version, ciphers); } int mosquittopp::tls_insecure_set(bool value) { return mosquitto_tls_insecure_set(m_mosq, value); } int mosquittopp::tls_psk_set(const char *psk, const char *identity, const char *ciphers) { return mosquitto_tls_psk_set(m_mosq, psk, identity, ciphers); } } mosquitto-2.0.18/lib/cpp/mosquittopp.h000066400000000000000000000123041450213760600177570ustar00rootroot00000000000000/* Copyright (c) 2010-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. Contributors: Roger Light - initial implementation and documentation. */ #ifndef MOSQUITTOPP_H #define MOSQUITTOPP_H #if defined(_WIN32) && !defined(LIBMOSQUITTO_STATIC) # ifdef mosquittopp_EXPORTS # define mosqpp_EXPORT __declspec(dllexport) # else # define mosqpp_EXPORT __declspec(dllimport) # endif #else # define mosqpp_EXPORT #endif #include #include #include namespace mosqpp { mosqpp_EXPORT const char * strerror(int mosq_errno); mosqpp_EXPORT const char * connack_string(int connack_code); mosqpp_EXPORT int sub_topic_tokenise(const char *subtopic, char ***topics, int *count); mosqpp_EXPORT int sub_topic_tokens_free(char ***topics, int count); mosqpp_EXPORT int lib_version(int *major, int *minor, int *revision); mosqpp_EXPORT int lib_init(); mosqpp_EXPORT int lib_cleanup(); mosqpp_EXPORT int topic_matches_sub(const char *sub, const char *topic, bool *result); mosqpp_EXPORT int validate_utf8(const char *str, int len); mosqpp_EXPORT int subscribe_simple( struct mosquitto_message **messages, int msg_count, bool retained, const char *topic, int qos=0, const char *host="localhost", int port=1883, const char *client_id=NULL, int keepalive=60, bool clean_session=true, const char *username=NULL, const char *password=NULL, const struct libmosquitto_will *will=NULL, const struct libmosquitto_tls *tls=NULL); mosqpp_EXPORT int subscribe_callback( int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *), void *userdata, const char *topic, int qos=0, const char *host="localhost", int port=1883, const char *client_id=NULL, int keepalive=60, bool clean_session=true, const char *username=NULL, const char *password=NULL, const struct libmosquitto_will *will=NULL, const struct libmosquitto_tls *tls=NULL); /* * Class: mosquittopp * * A mosquitto client class. This is a C++ wrapper class for the mosquitto C * library. Please see mosquitto.h for details of the functions. */ class mosqpp_EXPORT mosquittopp { private: struct mosquitto *m_mosq; public: mosquittopp(const char *id=NULL, bool clean_session=true); virtual ~mosquittopp(); int reinitialise(const char *id, bool clean_session); int socket(); int will_set(const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false); int will_clear(); int username_pw_set(const char *username, const char *password=NULL); int connect(const char *host, int port=1883, int keepalive=60); int connect_async(const char *host, int port=1883, int keepalive=60); int connect(const char *host, int port, int keepalive, const char *bind_address); int connect_async(const char *host, int port, int keepalive, const char *bind_address); int reconnect(); int reconnect_async(); int disconnect(); int publish(int *mid, const char *topic, int payloadlen=0, const void *payload=NULL, int qos=0, bool retain=false); int subscribe(int *mid, const char *sub, int qos=0); int unsubscribe(int *mid, const char *sub); void reconnect_delay_set(unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff); int max_inflight_messages_set(unsigned int max_inflight_messages); void message_retry_set(unsigned int message_retry); void user_data_set(void *userdata); int 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); int tls_opts_set(int cert_reqs, const char *tls_version=NULL, const char *ciphers=NULL); int tls_insecure_set(bool value); int tls_psk_set(const char *psk, const char *identity, const char *ciphers=NULL); int opts_set(enum mosq_opt_t option, void *value); int loop(int timeout=-1, int max_packets=1); int loop_misc(); int loop_read(int max_packets=1); int loop_write(int max_packets=1); int loop_forever(int timeout=-1, int max_packets=1); int loop_start(); int loop_stop(bool force=false); bool want_write(); int threaded_set(bool threaded=true); int socks5_set(const char *host, int port=1080, const char *username=NULL, const char *password=NULL); // names in the functions commented to prevent unused parameter warning virtual void on_connect(int /*rc*/) {return;} virtual void on_connect_with_flags(int /*rc*/, int /*flags*/) {return;} virtual void on_disconnect(int /*rc*/) {return;} virtual void on_publish(int /*mid*/) {return;} virtual void on_message(const struct mosquitto_message * /*message*/) {return;} virtual void on_subscribe(int /*mid*/, int /*qos_count*/, const int * /*granted_qos*/) {return;} virtual void on_unsubscribe(int /*mid*/) {return;} virtual void on_log(int /*level*/, const char * /*str*/) {return;} virtual void on_error() {return;} }; } #endif mosquitto-2.0.18/lib/dummypthread.h000066400000000000000000000004551450213760600173000ustar00rootroot00000000000000#ifndef DUMMYPTHREAD_H #define DUMMYPTHREAD_H #define pthread_create(A, B, C, D) #define pthread_join(A, B) #define pthread_cancel(A) #define pthread_testcancel() #define pthread_mutex_init(A, B) #define pthread_mutex_destroy(A) #define pthread_mutex_lock(A) #define pthread_mutex_unlock(A) #endif mosquitto-2.0.18/lib/handle_auth.c000066400000000000000000000027021450213760600170410ustar00rootroot00000000000000/* Copyright (c) 2018-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "logging_mosq.h" #include "mosquitto_internal.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" #include "read_handle.h" int handle__auth(struct mosquitto *mosq) { int rc = 0; uint8_t reason_code; mosquitto_property *properties = NULL; if(!mosq) return MOSQ_ERR_INVAL; log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received AUTH", SAFE_PRINT(mosq->id)); if(mosq->protocol != mosq_p_mqtt5){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.command != CMD_AUTH){ return MOSQ_ERR_MALFORMED_PACKET; } if(packet__read_byte(&mosq->in_packet, &reason_code)) return 1; rc = property__read_all(CMD_AUTH, &mosq->in_packet, &properties); if(rc) return rc; mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/handle_connack.c000066400000000000000000000100721450213760600175130ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "read_handle.h" static void connack_callback(struct mosquitto *mosq, uint8_t reason_code, uint8_t connect_flags, const mosquitto_property *properties) { log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received CONNACK (%d)", SAFE_PRINT(mosq->id), reason_code); if(reason_code == MQTT_RC_SUCCESS){ mosq->reconnects = 0; } pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_connect){ mosq->in_callback = true; mosq->on_connect(mosq, mosq->userdata, reason_code); mosq->in_callback = false; } if(mosq->on_connect_with_flags){ mosq->in_callback = true; mosq->on_connect_with_flags(mosq, mosq->userdata, reason_code, connect_flags); mosq->in_callback = false; } if(mosq->on_connect_v5){ mosq->in_callback = true; mosq->on_connect_v5(mosq, mosq->userdata, reason_code, connect_flags, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); } int handle__connack(struct mosquitto *mosq) { uint8_t connect_flags; uint8_t reason_code; int rc; mosquitto_property *properties = NULL; char *clientid = NULL; assert(mosq); if(mosq->in_packet.command != CMD_CONNACK){ return MOSQ_ERR_MALFORMED_PACKET; } rc = packet__read_byte(&mosq->in_packet, &connect_flags); if(rc) return rc; rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; if(mosq->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_CONNACK, &mosq->in_packet, &properties); if(rc == MOSQ_ERR_PROTOCOL && reason_code == CONNACK_REFUSED_PROTOCOL_VERSION){ /* This could occur because we are connecting to a v3.x broker and * it has replied with "unacceptable protocol version", but with a * v3 CONNACK. */ connack_callback(mosq, MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION, connect_flags, NULL); return rc; }else if(rc){ return rc; } } mosquitto_property_read_string(properties, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, &clientid, false); if(clientid){ if(mosq->id){ /* We've been sent a client identifier but already have one. This * shouldn't happen. */ free(clientid); mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; }else{ mosq->id = clientid; clientid = NULL; } } mosquitto_property_read_byte(properties, MQTT_PROP_RETAIN_AVAILABLE, &mosq->retain_available, false); mosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &mosq->max_qos, false); mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &mosq->msgs_out.inflight_maximum, false); mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &mosq->keepalive, false); mosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE, &mosq->maximum_packet_size, false); mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; message__reconnect_reset(mosq, true); connack_callback(mosq, reason_code, connect_flags, properties); mosquitto_property_free_all(&properties); switch(reason_code){ case 0: pthread_mutex_lock(&mosq->state_mutex); if(mosq->state != mosq_cs_disconnecting){ mosq->state = mosq_cs_active; } pthread_mutex_unlock(&mosq->state_mutex); message__retry_check(mosq); return MOSQ_ERR_SUCCESS; case 1: case 2: case 3: case 4: case 5: return MOSQ_ERR_CONN_REFUSED; default: return MOSQ_ERR_PROTOCOL; } } mosquitto-2.0.18/lib/handle_disconnect.c000066400000000000000000000031701450213760600202310ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "logging_mosq.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" int handle__disconnect(struct mosquitto *mosq) { int rc; uint8_t reason_code; mosquitto_property *properties = NULL; if(!mosq){ return MOSQ_ERR_INVAL; } if(mosq->protocol != mosq_p_mqtt5){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.command != CMD_DISCONNECT){ return MOSQ_ERR_MALFORMED_PACKET; } rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; if(mosq->in_packet.remaining_length > 2){ rc = property__read_all(CMD_DISCONNECT, &mosq->in_packet, &properties); if(rc) return rc; mosquitto_property_free_all(&properties); } log__printf(mosq, MOSQ_LOG_DEBUG, "Received DISCONNECT (%d)", reason_code); do_client_disconnect(mosq, reason_code, properties); mosquitto_property_free_all(&properties); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/handle_ping.c000066400000000000000000000035401450213760600170360ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" int handle__pingreq(struct mosquitto *mosq) { assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.command != CMD_PINGREQ){ return MOSQ_ERR_MALFORMED_PACKET; } #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGREQ from %s", SAFE_PRINT(mosq->id)); #else return MOSQ_ERR_PROTOCOL; #endif return send__pingresp(mosq); } int handle__pingresp(struct mosquitto *mosq) { assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } mosq->ping_t = 0; /* No longer waiting for a PINGRESP. */ #ifdef WITH_BROKER if(mosq->bridge == NULL){ return MOSQ_ERR_PROTOCOL; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received PINGRESP from %s", SAFE_PRINT(mosq->id)); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PINGRESP", SAFE_PRINT(mosq->id)); #endif return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/handle_pubackcomp.c000066400000000000000000000110001450213760600202130ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" int handle__pubackcomp(struct mosquitto *mosq, const char *type) { uint8_t reason_code = 0; uint16_t mid; int rc; mosquitto_property *properties = NULL; int qos; assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(mosq->protocol != mosq_p_mqtt31){ if((mosq->in_packet.command&0x0F) != 0x00){ return MOSQ_ERR_MALFORMED_PACKET; } } pthread_mutex_lock(&mosq->msgs_out.mutex); util__increment_send_quota(mosq); pthread_mutex_unlock(&mosq->msgs_out.mutex); rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; if(type[3] == 'A'){ /* pubAck or pubComp */ if(mosq->in_packet.command != CMD_PUBACK){ return MOSQ_ERR_MALFORMED_PACKET; } qos = 1; }else{ if(mosq->in_packet.command != CMD_PUBCOMP){ return MOSQ_ERR_MALFORMED_PACKET; } qos = 2; } if(mid == 0){ return MOSQ_ERR_PROTOCOL; } if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc){ return rc; } if(mosq->in_packet.remaining_length > 3){ rc = property__read_all(CMD_PUBACK, &mosq->in_packet, &properties); if(rc) return rc; } if(type[3] == 'A'){ /* pubAck or pubComp */ if(reason_code != MQTT_RC_SUCCESS && reason_code != MQTT_RC_NO_MATCHING_SUBSCRIBERS && reason_code != MQTT_RC_UNSPECIFIED && reason_code != MQTT_RC_IMPLEMENTATION_SPECIFIC && reason_code != MQTT_RC_NOT_AUTHORIZED && reason_code != MQTT_RC_TOPIC_NAME_INVALID && reason_code != MQTT_RC_PACKET_ID_IN_USE && reason_code != MQTT_RC_QUOTA_EXCEEDED && reason_code != MQTT_RC_PAYLOAD_FORMAT_INVALID ){ mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; } }else{ if(reason_code != MQTT_RC_SUCCESS && reason_code != MQTT_RC_PACKET_ID_NOT_FOUND ){ mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; } } } if(mosq->in_packet.pos < mosq->in_packet.remaining_length){ mosquitto_property_free_all(&properties); return MOSQ_ERR_MALFORMED_PACKET; } #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Received %s from %s (Mid: %d, RC:%d)", type, SAFE_PRINT(mosq->id), mid, reason_code); /* Immediately free, we don't do anything with Reason String or User Property at the moment */ mosquitto_property_free_all(&properties); rc = db__message_delete_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, qos); if(rc == MOSQ_ERR_NOT_FOUND){ log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received %s from %s for an unknown packet identifier %d.", type, SAFE_PRINT(mosq->id), mid); return MOSQ_ERR_SUCCESS; }else{ return rc; } #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received %s (Mid: %d, RC:%d)", SAFE_PRINT(mosq->id), type, mid, reason_code); rc = message__delete(mosq, mid, mosq_md_out, qos); if(rc == MOSQ_ERR_SUCCESS){ /* Only inform the client the message has been sent once. */ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_publish){ mosq->in_callback = true; mosq->on_publish(mosq, mosq->userdata, mid); mosq->in_callback = false; } if(mosq->on_publish_v5){ mosq->in_callback = true; mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); mosquitto_property_free_all(&properties); }else if(rc != MOSQ_ERR_NOT_FOUND){ return rc; } pthread_mutex_lock(&mosq->msgs_out.mutex); message__release_to_inflight(mosq, mosq_md_out); pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_SUCCESS; #endif } mosquitto-2.0.18/lib/handle_publish.c000066400000000000000000000112741450213760600175520ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "messages_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "time_mosq.h" #include "util_mosq.h" int handle__publish(struct mosquitto *mosq) { uint8_t header; struct mosquitto_message_all *message; int rc = 0; uint16_t mid = 0; uint16_t slen; mosquitto_property *properties = NULL; assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } message = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!message) return MOSQ_ERR_NOMEM; header = mosq->in_packet.command; message->dup = (header & 0x08)>>3; message->msg.qos = (header & 0x06)>>1; message->msg.retain = (header & 0x01); rc = packet__read_string(&mosq->in_packet, &message->msg.topic, &slen); if(rc){ message__cleanup(&message); return rc; } if(!slen){ message__cleanup(&message); return MOSQ_ERR_PROTOCOL; } if(message->msg.qos > 0){ if(mosq->protocol == mosq_p_mqtt5){ if(mosq->msgs_in.inflight_quota == 0){ message__cleanup(&message); /* FIXME - should send a DISCONNECT here */ return MOSQ_ERR_PROTOCOL; } } rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc){ message__cleanup(&message); return rc; } if(mid == 0){ message__cleanup(&message); return MOSQ_ERR_PROTOCOL; } message->msg.mid = (int)mid; } if(mosq->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_PUBLISH, &mosq->in_packet, &properties); if(rc){ message__cleanup(&message); return rc; } } message->msg.payloadlen = (int)(mosq->in_packet.remaining_length - mosq->in_packet.pos); if(message->msg.payloadlen){ message->msg.payload = mosquitto__calloc((size_t)message->msg.payloadlen+1, sizeof(uint8_t)); if(!message->msg.payload){ message__cleanup(&message); mosquitto_property_free_all(&properties); return MOSQ_ERR_NOMEM; } rc = packet__read_bytes(&mosq->in_packet, message->msg.payload, (uint32_t)message->msg.payloadlen); if(rc){ message__cleanup(&message); mosquitto_property_free_all(&properties); return rc; } } log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBLISH (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", SAFE_PRINT(mosq->id), message->dup, message->msg.qos, message->msg.retain, message->msg.mid, message->msg.topic, (long)message->msg.payloadlen); message->timestamp = mosquitto_time(); switch(message->msg.qos){ case 0: pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_message){ mosq->in_callback = true; mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } if(mosq->on_message_v5){ mosq->in_callback = true; mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); message__cleanup(&message); mosquitto_property_free_all(&properties); return MOSQ_ERR_SUCCESS; case 1: util__decrement_receive_quota(mosq); rc = send__puback(mosq, mid, 0, NULL); pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_message){ mosq->in_callback = true; mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } if(mosq->on_message_v5){ mosq->in_callback = true; mosq->on_message_v5(mosq, mosq->userdata, &message->msg, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); message__cleanup(&message); mosquitto_property_free_all(&properties); return rc; case 2: message->properties = properties; util__decrement_receive_quota(mosq); rc = send__pubrec(mosq, mid, 0, NULL); pthread_mutex_lock(&mosq->msgs_in.mutex); message->state = mosq_ms_wait_for_pubrel; message__queue(mosq, message, mosq_md_in); pthread_mutex_unlock(&mosq->msgs_in.mutex); return rc; default: message__cleanup(&message); mosquitto_property_free_all(&properties); return MOSQ_ERR_PROTOCOL; } } mosquitto-2.0.18/lib/handle_pubrec.c000066400000000000000000000074671450213760600173750ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" int handle__pubrec(struct mosquitto *mosq) { uint8_t reason_code = 0; uint16_t mid; int rc; mosquitto_property *properties = NULL; assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.command != CMD_PUBREC){ return MOSQ_ERR_MALFORMED_PACKET; } rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; if(mid == 0) return MOSQ_ERR_PROTOCOL; if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; if(reason_code != MQTT_RC_SUCCESS && reason_code != MQTT_RC_NO_MATCHING_SUBSCRIBERS && reason_code != MQTT_RC_UNSPECIFIED && reason_code != MQTT_RC_IMPLEMENTATION_SPECIFIC && reason_code != MQTT_RC_NOT_AUTHORIZED && reason_code != MQTT_RC_TOPIC_NAME_INVALID && reason_code != MQTT_RC_PACKET_ID_IN_USE && reason_code != MQTT_RC_QUOTA_EXCEEDED){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.remaining_length > 3){ rc = property__read_all(CMD_PUBREC, &mosq->in_packet, &properties); if(rc) return rc; /* Immediately free, we don't do anything with Reason String or User Property at the moment */ mosquitto_property_free_all(&properties); } } if(mosq->in_packet.pos < mosq->in_packet.remaining_length){ #ifdef WITH_BROKER mosquitto_property_free_all(&properties); #endif return MOSQ_ERR_MALFORMED_PACKET; } #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREC from %s (Mid: %d)", SAFE_PRINT(mosq->id), mid); if(reason_code < 0x80){ rc = db__message_update_outgoing(mosq, mid, mosq_ms_wait_for_pubcomp, 2); }else{ return db__message_delete_outgoing(mosq, mid, mosq_ms_wait_for_pubrec, 2); } #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREC (Mid: %d)", SAFE_PRINT(mosq->id), mid); if(reason_code < 0x80 || mosq->protocol != mosq_p_mqtt5){ rc = message__out_update(mosq, mid, mosq_ms_wait_for_pubcomp, 2); }else{ if(!message__delete(mosq, mid, mosq_md_out, 2)){ /* Only inform the client the message has been sent once. */ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_publish_v5){ mosq->in_callback = true; mosq->on_publish_v5(mosq, mosq->userdata, mid, reason_code, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); } util__increment_send_quota(mosq); pthread_mutex_lock(&mosq->msgs_out.mutex); message__release_to_inflight(mosq, mosq_md_out); pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_SUCCESS; } #endif if(rc == MOSQ_ERR_NOT_FOUND){ log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Received PUBREC from %s for an unknown packet identifier %d.", SAFE_PRINT(mosq->id), mid); }else if(rc != MOSQ_ERR_SUCCESS){ return rc; } rc = send__pubrel(mosq, mid, NULL); if(rc) return rc; return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/handle_pubrel.c000066400000000000000000000074371450213760600174030ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" int handle__pubrel(struct mosquitto *mosq) { uint8_t reason_code; uint16_t mid; #ifndef WITH_BROKER struct mosquitto_message_all *message = NULL; #endif int rc; mosquitto_property *properties = NULL; assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(mosq->protocol != mosq_p_mqtt31 && mosq->in_packet.command != (CMD_PUBREL|2)){ return MOSQ_ERR_MALFORMED_PACKET; } if(mosq->protocol != mosq_p_mqtt31){ if((mosq->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_PROTOCOL; } } rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; if(mid == 0) return MOSQ_ERR_PROTOCOL; if(mosq->protocol == mosq_p_mqtt5 && mosq->in_packet.remaining_length > 2){ rc = packet__read_byte(&mosq->in_packet, &reason_code); if(rc) return rc; if(reason_code != MQTT_RC_SUCCESS && reason_code != MQTT_RC_PACKET_ID_NOT_FOUND){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.remaining_length > 3){ rc = property__read_all(CMD_PUBREL, &mosq->in_packet, &properties); if(rc) return rc; } } if(mosq->in_packet.pos < mosq->in_packet.remaining_length){ #ifdef WITH_BROKER mosquitto_property_free_all(&properties); #endif return MOSQ_ERR_MALFORMED_PACKET; } #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBREL from %s (Mid: %d)", SAFE_PRINT(mosq->id), mid); /* Immediately free, we don't do anything with Reason String or User Property at the moment */ mosquitto_property_free_all(&properties); rc = db__message_release_incoming(mosq, mid); if(rc == MOSQ_ERR_NOT_FOUND){ /* Message not found. Still send a PUBCOMP anyway because this could be * due to a repeated PUBREL after a client has reconnected. */ }else if(rc != MOSQ_ERR_SUCCESS){ return rc; } rc = send__pubcomp(mosq, mid, NULL); if(rc) return rc; #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received PUBREL (Mid: %d)", SAFE_PRINT(mosq->id), mid); rc = send__pubcomp(mosq, mid, NULL); if(rc){ message__remove(mosq, mid, mosq_md_in, &message, 2); return rc; } rc = message__remove(mosq, mid, mosq_md_in, &message, 2); if(rc == MOSQ_ERR_SUCCESS){ /* Only pass the message on if we have removed it from the queue - this * prevents multiple callbacks for the same message. */ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_message){ mosq->in_callback = true; mosq->on_message(mosq, mosq->userdata, &message->msg); mosq->in_callback = false; } if(mosq->on_message_v5){ mosq->in_callback = true; mosq->on_message_v5(mosq, mosq->userdata, &message->msg, message->properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); mosquitto_property_free_all(&properties); message__cleanup(&message); }else if(rc == MOSQ_ERR_NOT_FOUND){ return MOSQ_ERR_SUCCESS; }else{ return rc; } #endif return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/handle_suback.c000066400000000000000000000060241450213760600173510ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" #include "read_handle.h" #include "util_mosq.h" int handle__suback(struct mosquitto *mosq) { uint16_t mid; uint8_t qos; int *granted_qos; int qos_count; int i = 0; int rc; mosquitto_property *properties = NULL; assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.command != CMD_SUBACK){ return MOSQ_ERR_MALFORMED_PACKET; } #ifdef WITH_BROKER if(mosq->bridge == NULL){ /* Client is not a bridge, so shouldn't be sending SUBACK */ return MOSQ_ERR_PROTOCOL; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBACK from %s", SAFE_PRINT(mosq->id)); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received SUBACK", SAFE_PRINT(mosq->id)); #endif rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; if(mid == 0) return MOSQ_ERR_PROTOCOL; if(mosq->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_SUBACK, &mosq->in_packet, &properties); if(rc) return rc; } qos_count = (int)(mosq->in_packet.remaining_length - mosq->in_packet.pos); granted_qos = mosquitto__malloc((size_t)qos_count*sizeof(int)); if(!granted_qos){ #ifdef WITH_BROKER mosquitto_property_free_all(&properties); #endif return MOSQ_ERR_NOMEM; } while(mosq->in_packet.pos < mosq->in_packet.remaining_length){ rc = packet__read_byte(&mosq->in_packet, &qos); if(rc){ mosquitto__free(granted_qos); #ifdef WITH_BROKER mosquitto_property_free_all(&properties); #endif return rc; } granted_qos[i] = (int)qos; i++; } #ifdef WITH_BROKER /* Immediately free, we don't do anything with Reason String or User Property at the moment */ mosquitto_property_free_all(&properties); #else pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_subscribe){ mosq->in_callback = true; mosq->on_subscribe(mosq, mosq->userdata, mid, qos_count, granted_qos); mosq->in_callback = false; } if(mosq->on_subscribe_v5){ mosq->in_callback = true; mosq->on_subscribe_v5(mosq, mosq->userdata, mid, qos_count, granted_qos, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); mosquitto_property_free_all(&properties); #endif mosquitto__free(granted_qos); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/handle_unsuback.c000066400000000000000000000047121450213760600177160ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" int handle__unsuback(struct mosquitto *mosq) { uint16_t mid; int rc; mosquitto_property *properties = NULL; assert(mosq); if(mosquitto__get_state(mosq) != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(mosq->in_packet.command != CMD_UNSUBACK){ return MOSQ_ERR_MALFORMED_PACKET; } #ifdef WITH_BROKER if(mosq->bridge == NULL){ /* Client is not a bridge, so shouldn't be sending SUBACK */ return MOSQ_ERR_PROTOCOL; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBACK from %s", SAFE_PRINT(mosq->id)); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s received UNSUBACK", SAFE_PRINT(mosq->id)); #endif rc = packet__read_uint16(&mosq->in_packet, &mid); if(rc) return rc; if(mid == 0) return MOSQ_ERR_PROTOCOL; if(mosq->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_UNSUBACK, &mosq->in_packet, &properties); if(rc) return rc; } #ifdef WITH_BROKER /* Immediately free, we don't do anything with Reason String or User Property at the moment */ mosquitto_property_free_all(&properties); #else pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_unsubscribe){ mosq->in_callback = true; mosq->on_unsubscribe(mosq, mosq->userdata, mid); mosq->in_callback = false; } if(mosq->on_unsubscribe_v5){ mosq->in_callback = true; mosq->on_unsubscribe_v5(mosq, mosq->userdata, mid, properties); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); mosquitto_property_free_all(&properties); #endif return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/helpers.c000066400000000000000000000115471450213760600162360ustar00rootroot00000000000000/* Copyright (c) 2016-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto.h" #include "mosquitto_internal.h" struct userdata__callback { const char *topic; int (*callback)(struct mosquitto *, void *, const struct mosquitto_message *); void *userdata; int qos; }; struct userdata__simple { struct mosquitto_message *messages; int max_msg_count; int message_count; bool want_retained; }; static void on_connect(struct mosquitto *mosq, void *obj, int rc) { struct userdata__callback *userdata = obj; UNUSED(rc); mosquitto_subscribe(mosq, NULL, userdata->topic, userdata->qos); } static void on_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) { int rc; struct userdata__callback *userdata = obj; rc = userdata->callback(mosq, userdata->userdata, message); if(rc){ mosquitto_disconnect(mosq); } } static int on_message_simple(struct mosquitto *mosq, void *obj, const struct mosquitto_message *message) { struct userdata__simple *userdata = obj; int rc; if(userdata->max_msg_count == 0){ return 0; } /* Don't process stale retained messages if 'want_retained' was false */ if(!userdata->want_retained && message->retain){ return 0; } userdata->max_msg_count--; rc = mosquitto_message_copy(&userdata->messages[userdata->message_count], message); if(rc){ return rc; } userdata->message_count++; if(userdata->max_msg_count == 0){ mosquitto_disconnect(mosq); } return 0; } libmosq_EXPORT int mosquitto_subscribe_simple( struct mosquitto_message **messages, int msg_count, bool want_retained, const char *topic, int qos, const char *host, int port, const char *client_id, int keepalive, bool clean_session, const char *username, const char *password, const struct libmosquitto_will *will, const struct libmosquitto_tls *tls) { struct userdata__simple userdata; int rc; int i; if(!topic || msg_count < 1 || !messages){ return MOSQ_ERR_INVAL; } *messages = NULL; userdata.messages = calloc(sizeof(struct mosquitto_message), (size_t)msg_count); if(!userdata.messages){ return MOSQ_ERR_NOMEM; } userdata.message_count = 0; userdata.max_msg_count = msg_count; userdata.want_retained = want_retained; rc = mosquitto_subscribe_callback( on_message_simple, &userdata, topic, qos, host, port, client_id, keepalive, clean_session, username, password, will, tls); if(!rc && userdata.max_msg_count == 0){ *messages = userdata.messages; return MOSQ_ERR_SUCCESS; }else{ for(i=0; itopic, will->payloadlen, will->payload, will->qos, will->retain); if(rc){ mosquitto_destroy(mosq); return rc; } } if(username){ rc = mosquitto_username_pw_set(mosq, username, password); if(rc){ mosquitto_destroy(mosq); return rc; } } if(tls){ rc = mosquitto_tls_set(mosq, tls->cafile, tls->capath, tls->certfile, tls->keyfile, tls->pw_callback); if(rc){ mosquitto_destroy(mosq); return rc; } rc = mosquitto_tls_opts_set(mosq, tls->cert_reqs, tls->tls_version, tls->ciphers); if(rc){ mosquitto_destroy(mosq); return rc; } } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_message_callback_set(mosq, on_message_callback); rc = mosquitto_connect(mosq, host, port, keepalive); if(rc){ mosquitto_destroy(mosq); return rc; } rc = mosquitto_loop_forever(mosq, -1, 1); mosquitto_destroy(mosq); return rc; } mosquitto-2.0.18/lib/linker.version000066400000000000000000000066551450213760600173270ustar00rootroot00000000000000/* Linker version script - currently used here primarily to control which * symbols are exported. */ MOSQ_1.0 { global: mosquitto_lib_version; mosquitto_lib_init; mosquitto_lib_cleanup; mosquitto_new; mosquitto_destroy; mosquitto_reinitialise; mosquitto_will_set; mosquitto_will_clear; mosquitto_username_pw_set; mosquitto_connect; mosquitto_connect_async; mosquitto_reconnect; mosquitto_disconnect; mosquitto_publish; mosquitto_subscribe; mosquitto_unsubscribe; mosquitto_message_copy; mosquitto_message_free; mosquitto_loop; mosquitto_socket; mosquitto_loop_start; mosquitto_loop_stop; mosquitto_loop_read; mosquitto_loop_write; mosquitto_loop_misc; mosquitto_connect_callback_set; mosquitto_disconnect_callback_set; mosquitto_publish_callback_set; mosquitto_message_callback_set; mosquitto_subscribe_callback_set; mosquitto_unsubscribe_callback_set; mosquitto_log_callback_set; mosquitto_message_retry_set; mosquitto_want_write; mosquitto_user_data_set; mosquitto_strerror; mosquitto_connack_string; mosquitto_tls_set; mosquitto_tls_opts_set; mosquitto_tls_psk_set; mosquitto_sub_topic_tokenise; mosquitto_sub_topic_tokens_free; mosquitto_topic_matches_sub; local: *; }; MOSQ_1.1 { global: mosquitto_loop_forever; } MOSQ_1.0; MOSQ_1.2 { global: mosquitto_connect_bind; mosquitto_connect_bind_async; mosquitto_max_inflight_messages_set; mosquitto_reconnect_delay_set; mosquitto_reconnect_async; mosquitto_tls_insecure_set; } MOSQ_1.1; MOSQ_1.3 { global: mosquitto_connect_srv; } MOSQ_1.2; MOSQ_1.4 { global: mosquitto_threaded_set; mosquitto_opts_set; mosquitto_pub_topic_check; mosquitto_sub_topic_check; mosquitto_socks5_set; } MOSQ_1.3; MOSQ_1.5 { global: mosquitto_subscribe_simple; mosquitto_subscribe_callback; mosquitto_message_free_contents; mosquitto_validate_utf8; mosquitto_userdata; mosquitto_pub_topic_check2; mosquitto_sub_topic_check2; mosquitto_topic_matches_sub2; mosquitto_connect_with_flags_callback_set; } MOSQ_1.4; MOSQ_1.6 { global: mosquitto_connect_bind_v5; mosquitto_connect_v5_callback_set; mosquitto_disconnect_v5; mosquitto_disconnect_v5_callback_set; mosquitto_int_option; mosquitto_message_v5_callback_set; mosquitto_property_add_binary; mosquitto_property_add_byte; mosquitto_property_add_int16; mosquitto_property_add_int32; mosquitto_property_add_string; mosquitto_property_add_string_pair; mosquitto_property_add_varint; mosquitto_property_check_all; mosquitto_property_check_command; mosquitto_property_copy_all; mosquitto_property_free_all; mosquitto_property_read_binary; mosquitto_property_read_byte; mosquitto_property_read_int16; mosquitto_property_read_int32; mosquitto_property_read_string; mosquitto_property_read_string_pair; mosquitto_property_read_varint; mosquitto_publish_v5; mosquitto_publish_v5_callback_set; mosquitto_reason_string; mosquitto_string_option; mosquitto_string_to_command; mosquitto_string_to_property_info; mosquitto_subscribe_multiple; mosquitto_subscribe_v5; mosquitto_subscribe_v5_callback_set; mosquitto_unsubscribe_multiple; mosquitto_unsubscribe_v5; mosquitto_unsubscribe_v5_callback_set; mosquitto_void_option; mosquitto_will_set_v5; } MOSQ_1.5; MOSQ_1.7 { global: mosquitto_property_identifier; mosquitto_property_identifier_to_string; mosquitto_property_next; mosquitto_ssl_get; } MOSQ_1.6; mosquitto-2.0.18/lib/logging_mosq.c000066400000000000000000000026701450213760600172560ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include "logging_mosq.h" #include "mosquitto_internal.h" #include "mosquitto.h" #include "memory_mosq.h" int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { va_list va; char *s; size_t len; assert(mosq); assert(fmt); pthread_mutex_lock(&mosq->log_callback_mutex); if(mosq->on_log){ len = strlen(fmt) + 500; s = mosquitto__malloc(len*sizeof(char)); if(!s){ pthread_mutex_unlock(&mosq->log_callback_mutex); return MOSQ_ERR_NOMEM; } va_start(va, fmt); vsnprintf(s, len, fmt, va); va_end(va); s[len-1] = '\0'; /* Ensure string is null terminated. */ mosq->on_log(mosq, mosq->userdata, (int)priority, s); mosquitto__free(s); } pthread_mutex_unlock(&mosq->log_callback_mutex); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/logging_mosq.h000066400000000000000000000015071450213760600172610ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef LOGGING_MOSQ_H #define LOGGING_MOSQ_H #include "mosquitto.h" #ifndef __GNUC__ #define __attribute__(attrib) #endif int log__printf(struct mosquitto *mosq, unsigned int level, const char *fmt, ...) __attribute__((format(printf, 3, 4))); #endif mosquitto-2.0.18/lib/loop.c000066400000000000000000000231671450213760600155460ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #ifndef WIN32 #include #include #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "net_mosq.h" #include "packet_mosq.h" #include "socks_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" #if !defined(WIN32) && !defined(__SYMBIAN32__) && !defined(__QNX__) #define HAVE_PSELECT #endif int mosquitto_loop(struct mosquitto *mosq, int timeout, int max_packets) { #ifdef HAVE_PSELECT struct timespec local_timeout; #else struct timeval local_timeout; #endif fd_set readfds, writefds; int fdcount; int rc; char pairbuf; int maxfd = 0; time_t now; time_t timeout_ms; if(!mosq || max_packets < 1) return MOSQ_ERR_INVAL; #ifndef WIN32 if(mosq->sock >= FD_SETSIZE || mosq->sockpairR >= FD_SETSIZE){ return MOSQ_ERR_INVAL; } #endif FD_ZERO(&readfds); FD_ZERO(&writefds); if(mosq->sock != INVALID_SOCKET){ maxfd = mosq->sock; FD_SET(mosq->sock, &readfds); if(mosq->want_write){ FD_SET(mosq->sock, &writefds); }else{ #ifdef WITH_TLS if(mosq->ssl == NULL || SSL_is_init_finished(mosq->ssl)) #endif { pthread_mutex_lock(&mosq->current_out_packet_mutex); pthread_mutex_lock(&mosq->out_packet_mutex); if(mosq->out_packet || mosq->current_out_packet){ FD_SET(mosq->sock, &writefds); } pthread_mutex_unlock(&mosq->out_packet_mutex); pthread_mutex_unlock(&mosq->current_out_packet_mutex); } } }else{ #ifdef WITH_SRV if(mosq->achan){ if(mosquitto__get_state(mosq) == mosq_cs_connect_srv){ rc = ares_fds(mosq->achan, &readfds, &writefds); if(rc > maxfd){ maxfd = rc; } }else{ return MOSQ_ERR_NO_CONN; } } #else return MOSQ_ERR_NO_CONN; #endif } if(mosq->sockpairR != INVALID_SOCKET){ /* sockpairR is used to break out of select() before the timeout, on a * call to publish() etc. */ FD_SET(mosq->sockpairR, &readfds); if((int)mosq->sockpairR > maxfd){ maxfd = mosq->sockpairR; } } timeout_ms = timeout; if(timeout_ms < 0){ timeout_ms = 1000; } now = mosquitto_time(); pthread_mutex_lock(&mosq->msgtime_mutex); if(mosq->next_msg_out && now + timeout_ms/1000 > mosq->next_msg_out){ timeout_ms = (mosq->next_msg_out - now)*1000; } pthread_mutex_unlock(&mosq->msgtime_mutex); if(timeout_ms < 0){ /* There has been a delay somewhere which means we should have already * sent a message. */ timeout_ms = 0; } local_timeout.tv_sec = timeout_ms/1000; #ifdef HAVE_PSELECT local_timeout.tv_nsec = (timeout_ms-local_timeout.tv_sec*1000)*1000000; #else local_timeout.tv_usec = (timeout_ms-local_timeout.tv_sec*1000)*1000; #endif #ifdef HAVE_PSELECT fdcount = pselect(maxfd+1, &readfds, &writefds, NULL, &local_timeout, NULL); #else fdcount = select(maxfd+1, &readfds, &writefds, NULL, &local_timeout); #endif if(fdcount == -1){ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EINTR){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ERRNO; } }else{ if(mosq->sock != INVALID_SOCKET){ if(FD_ISSET(mosq->sock, &readfds)){ rc = mosquitto_loop_read(mosq, max_packets); if(rc || mosq->sock == INVALID_SOCKET){ return rc; } } if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){ #ifndef WIN32 if(read(mosq->sockpairR, &pairbuf, 1) == 0){ } #else recv(mosq->sockpairR, &pairbuf, 1, 0); #endif /* Fake write possible, to stimulate output write even though * we didn't ask for it, because at that point the publish or * other command wasn't present. */ if(mosq->sock != INVALID_SOCKET) FD_SET(mosq->sock, &writefds); } if(mosq->sock != INVALID_SOCKET && FD_ISSET(mosq->sock, &writefds)){ rc = mosquitto_loop_write(mosq, max_packets); if(rc || mosq->sock == INVALID_SOCKET){ return rc; } } } #ifdef WITH_SRV if(mosq->achan){ ares_process(mosq->achan, &readfds, &writefds); } #endif } return mosquitto_loop_misc(mosq); } static int interruptible_sleep(struct mosquitto *mosq, time_t reconnect_delay) { #ifdef HAVE_PSELECT struct timespec local_timeout; #else struct timeval local_timeout; #endif fd_set readfds; int fdcount; char pairbuf; int maxfd = 0; #ifndef WIN32 while(mosq->sockpairR != INVALID_SOCKET && read(mosq->sockpairR, &pairbuf, 1) > 0); #else while(mosq->sockpairR != INVALID_SOCKET && recv(mosq->sockpairR, &pairbuf, 1, 0) > 0); #endif local_timeout.tv_sec = reconnect_delay; #ifdef HAVE_PSELECT local_timeout.tv_nsec = 0; #else local_timeout.tv_usec = 0; #endif FD_ZERO(&readfds); maxfd = 0; if(mosq->sockpairR != INVALID_SOCKET){ /* sockpairR is used to break out of select() before the * timeout, when mosquitto_loop_stop() is called */ FD_SET(mosq->sockpairR, &readfds); maxfd = mosq->sockpairR; } #ifdef HAVE_PSELECT fdcount = pselect(maxfd+1, &readfds, NULL, NULL, &local_timeout, NULL); #else fdcount = select(maxfd+1, &readfds, NULL, NULL, &local_timeout); #endif if(fdcount == -1){ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EINTR){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ERRNO; } }else if(mosq->sockpairR != INVALID_SOCKET && FD_ISSET(mosq->sockpairR, &readfds)){ #ifndef WIN32 if(read(mosq->sockpairR, &pairbuf, 1) == 0){ } #else recv(mosq->sockpairR, &pairbuf, 1, 0); #endif } return MOSQ_ERR_SUCCESS; } int mosquitto_loop_forever(struct mosquitto *mosq, int timeout, int max_packets) { int run = 1; int rc = MOSQ_ERR_SUCCESS; unsigned long reconnect_delay; if(!mosq) return MOSQ_ERR_INVAL; mosq->reconnects = 0; while(run){ do{ #ifdef HAVE_PTHREAD_CANCEL pthread_testcancel(); #endif rc = mosquitto_loop(mosq, timeout, max_packets); }while(run && rc == MOSQ_ERR_SUCCESS); /* Quit after fatal errors. */ switch(rc){ case MOSQ_ERR_NOMEM: case MOSQ_ERR_PROTOCOL: case MOSQ_ERR_INVAL: case MOSQ_ERR_NOT_FOUND: case MOSQ_ERR_TLS: case MOSQ_ERR_PAYLOAD_SIZE: case MOSQ_ERR_NOT_SUPPORTED: case MOSQ_ERR_AUTH: case MOSQ_ERR_ACL_DENIED: case MOSQ_ERR_UNKNOWN: case MOSQ_ERR_EAI: case MOSQ_ERR_PROXY: return rc; case MOSQ_ERR_ERRNO: break; } if(errno == EPROTO){ return rc; } do{ #ifdef HAVE_PTHREAD_CANCEL pthread_testcancel(); #endif rc = MOSQ_ERR_SUCCESS; if(mosquitto__get_request_disconnect(mosq)){ run = 0; }else{ if(mosq->reconnect_delay_max > mosq->reconnect_delay){ if(mosq->reconnect_exponential_backoff){ reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1)*(mosq->reconnects+1); }else{ reconnect_delay = mosq->reconnect_delay*(mosq->reconnects+1); } }else{ reconnect_delay = mosq->reconnect_delay; } if(reconnect_delay > mosq->reconnect_delay_max){ reconnect_delay = mosq->reconnect_delay_max; }else{ mosq->reconnects++; } rc = interruptible_sleep(mosq, (time_t)reconnect_delay); if(rc) return rc; if(mosquitto__get_request_disconnect(mosq)){ run = 0; }else{ rc = mosquitto_reconnect(mosq); } } }while(run && rc != MOSQ_ERR_SUCCESS); } return rc; } int mosquitto_loop_misc(struct mosquitto *mosq) { if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; return mosquitto__check_keepalive(mosq); } static int mosquitto__loop_rc_handle(struct mosquitto *mosq, int rc) { enum mosquitto_client_state state; if(rc){ net__socket_close(mosq); state = mosquitto__get_state(mosq); if(state == mosq_cs_disconnecting || state == mosq_cs_disconnected){ rc = MOSQ_ERR_SUCCESS; } pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, rc); mosq->in_callback = false; } if(mosq->on_disconnect_v5){ mosq->in_callback = true; mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); } return rc; } int mosquitto_loop_read(struct mosquitto *mosq, int max_packets) { int rc = MOSQ_ERR_SUCCESS; int i; if(max_packets < 1) return MOSQ_ERR_INVAL; pthread_mutex_lock(&mosq->msgs_out.mutex); max_packets = mosq->msgs_out.queue_len; pthread_mutex_unlock(&mosq->msgs_out.mutex); pthread_mutex_lock(&mosq->msgs_in.mutex); max_packets += mosq->msgs_in.queue_len; pthread_mutex_unlock(&mosq->msgs_in.mutex); if(max_packets < 1) max_packets = 1; /* Queue len here tells us how many messages are awaiting processing and * have QoS > 0. We should try to deal with that many in this loop in order * to keep up. */ for(i=0; isocks5_host){ rc = socks5__read(mosq); }else #endif { rc = packet__read(mosq); } if(rc || errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return mosquitto__loop_rc_handle(mosq, rc); } } return rc; } int mosquitto_loop_write(struct mosquitto *mosq, int max_packets) { int rc = MOSQ_ERR_SUCCESS; int i; if(max_packets < 1) return MOSQ_ERR_INVAL; for(i=0; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "memory_mosq.h" #ifdef REAL_WITH_MEMORY_TRACKING # if defined(__APPLE__) # include # define malloc_usable_size malloc_size # elif defined(__FreeBSD__) # include # else # include # endif #endif #ifdef REAL_WITH_MEMORY_TRACKING static unsigned long memcount = 0; static unsigned long max_memcount = 0; #endif #ifdef WITH_BROKER static size_t mem_limit = 0; void memory__set_limit(size_t lim) { mem_limit = lim; } #endif void *mosquitto__calloc(size_t nmemb, size_t size) { void *mem; #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + size > mem_limit){ return NULL; } #endif mem = calloc(nmemb, size); #ifdef REAL_WITH_MEMORY_TRACKING if(mem){ memcount += malloc_usable_size(mem); if(memcount > max_memcount){ max_memcount = memcount; } } #endif return mem; } void mosquitto__free(void *mem) { #ifdef REAL_WITH_MEMORY_TRACKING if(!mem){ return; } memcount -= malloc_usable_size(mem); #endif free(mem); } void *mosquitto__malloc(size_t size) { void *mem; #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + size > mem_limit){ return NULL; } #endif mem = malloc(size); #ifdef REAL_WITH_MEMORY_TRACKING if(mem){ memcount += malloc_usable_size(mem); if(memcount > max_memcount){ max_memcount = memcount; } } #endif return mem; } #ifdef REAL_WITH_MEMORY_TRACKING unsigned long mosquitto__memory_used(void) { return memcount; } unsigned long mosquitto__max_memory_used(void) { return max_memcount; } #endif void *mosquitto__realloc(void *ptr, size_t size) { void *mem; #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + size > mem_limit){ return NULL; } if(ptr){ memcount -= malloc_usable_size(ptr); } #endif mem = realloc(ptr, size); #ifdef REAL_WITH_MEMORY_TRACKING if(mem){ memcount += malloc_usable_size(mem); if(memcount > max_memcount){ max_memcount = memcount; } } #endif return mem; } char *mosquitto__strdup(const char *s) { char *str; #ifdef REAL_WITH_MEMORY_TRACKING if(mem_limit && memcount + strlen(s) > mem_limit){ return NULL; } #endif str = strdup(s); #ifdef REAL_WITH_MEMORY_TRACKING if(str){ memcount += malloc_usable_size(str); if(memcount > max_memcount){ max_memcount = memcount; } } #endif return str; } mosquitto-2.0.18/lib/memory_mosq.h000066400000000000000000000023711450213760600171430ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MEMORY_MOSQ_H #define MEMORY_MOSQ_H #include #include #if defined(WITH_MEMORY_TRACKING) && defined(WITH_BROKER) # if defined(__APPLE__) || defined(__FreeBSD__) || defined(__GLIBC__) # define REAL_WITH_MEMORY_TRACKING # endif #endif void *mosquitto__calloc(size_t nmemb, size_t size); void mosquitto__free(void *mem); void *mosquitto__malloc(size_t size); #ifdef REAL_WITH_MEMORY_TRACKING unsigned long mosquitto__memory_used(void); unsigned long mosquitto__max_memory_used(void); #endif void *mosquitto__realloc(void *ptr, size_t size); char *mosquitto__strdup(const char *s); #ifdef WITH_BROKER void memory__set_limit(size_t lim); #endif #endif mosquitto-2.0.18/lib/messages_mosq.c000066400000000000000000000217501450213760600174370ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include "mosquitto_internal.h" #include "mosquitto.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "send_mosq.h" #include "time_mosq.h" #include "util_mosq.h" void message__cleanup(struct mosquitto_message_all **message) { struct mosquitto_message_all *msg; if(!message || !*message) return; msg = *message; mosquitto__free(msg->msg.topic); mosquitto__free(msg->msg.payload); mosquitto_property_free_all(&msg->properties); mosquitto__free(msg); } void message__cleanup_all(struct mosquitto *mosq) { struct mosquitto_message_all *tail, *tmp; assert(mosq); DL_FOREACH_SAFE(mosq->msgs_in.inflight, tail, tmp){ DL_DELETE(mosq->msgs_in.inflight, tail); message__cleanup(&tail); } DL_FOREACH_SAFE(mosq->msgs_out.inflight, tail, tmp){ DL_DELETE(mosq->msgs_out.inflight, tail); message__cleanup(&tail); } } int mosquitto_message_copy(struct mosquitto_message *dst, const struct mosquitto_message *src) { if(!dst || !src) return MOSQ_ERR_INVAL; dst->mid = src->mid; dst->topic = mosquitto__strdup(src->topic); if(!dst->topic) return MOSQ_ERR_NOMEM; dst->qos = src->qos; dst->retain = src->retain; if(src->payloadlen){ dst->payload = mosquitto__calloc((unsigned int)src->payloadlen+1, sizeof(uint8_t)); if(!dst->payload){ mosquitto__free(dst->topic); return MOSQ_ERR_NOMEM; } memcpy(dst->payload, src->payload, (unsigned int)src->payloadlen); dst->payloadlen = src->payloadlen; }else{ dst->payloadlen = 0; dst->payload = NULL; } return MOSQ_ERR_SUCCESS; } int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos) { struct mosquitto_message_all *message; int rc; assert(mosq); rc = message__remove(mosq, mid, dir, &message, qos); if(rc == MOSQ_ERR_SUCCESS){ message__cleanup(&message); } return rc; } void mosquitto_message_free(struct mosquitto_message **message) { struct mosquitto_message *msg; if(!message || !*message) return; msg = *message; mosquitto__free(msg->topic); mosquitto__free(msg->payload); mosquitto__free(msg); } void mosquitto_message_free_contents(struct mosquitto_message *message) { if(!message) return; mosquitto__free(message->topic); mosquitto__free(message->payload); } int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir) { /* mosq->*_message_mutex should be locked before entering this function */ assert(mosq); assert(message); assert(message->msg.qos != 0); if(dir == mosq_md_out){ DL_APPEND(mosq->msgs_out.inflight, message); mosq->msgs_out.queue_len++; }else{ DL_APPEND(mosq->msgs_in.inflight, message); mosq->msgs_in.queue_len++; } return message__release_to_inflight(mosq, dir); } void message__reconnect_reset(struct mosquitto *mosq, bool update_quota_only) { struct mosquitto_message_all *message, *tmp; assert(mosq); pthread_mutex_lock(&mosq->msgs_in.mutex); mosq->msgs_in.inflight_quota = mosq->msgs_in.inflight_maximum; mosq->msgs_in.queue_len = 0; DL_FOREACH_SAFE(mosq->msgs_in.inflight, message, tmp){ mosq->msgs_in.queue_len++; message->timestamp = 0; if(message->msg.qos != 2){ DL_DELETE(mosq->msgs_in.inflight, message); message__cleanup(&message); }else{ /* Message state can be preserved here because it should match * whatever the client has got. */ util__decrement_receive_quota(mosq); } } pthread_mutex_unlock(&mosq->msgs_in.mutex); pthread_mutex_lock(&mosq->msgs_out.mutex); mosq->msgs_out.inflight_quota = mosq->msgs_out.inflight_maximum; mosq->msgs_out.queue_len = 0; DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){ mosq->msgs_out.queue_len++; message->timestamp = 0; if(mosq->msgs_out.inflight_quota != 0){ util__decrement_send_quota(mosq); if (update_quota_only == false){ if(message->msg.qos == 1){ message->state = mosq_ms_publish_qos1; }else if(message->msg.qos == 2){ if(message->state == mosq_ms_wait_for_pubrec){ message->state = mosq_ms_publish_qos2; }else if(message->state == mosq_ms_wait_for_pubcomp){ message->state = mosq_ms_resend_pubrel; } /* Should be able to preserve state. */ } } }else{ message->state = mosq_ms_invalid; } } pthread_mutex_unlock(&mosq->msgs_out.mutex); } int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir) { /* mosq->*_message_mutex should be locked before entering this function */ struct mosquitto_message_all *cur, *tmp; int rc = MOSQ_ERR_SUCCESS; if(dir == mosq_md_out){ DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){ if(mosq->msgs_out.inflight_quota > 0){ if(cur->msg.qos > 0 && cur->state == mosq_ms_invalid){ if(cur->msg.qos == 1){ cur->state = mosq_ms_wait_for_puback; }else if(cur->msg.qos == 2){ cur->state = mosq_ms_wait_for_pubrec; } rc = 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, cur->properties, NULL, 0); if(rc){ return rc; } util__decrement_send_quota(mosq); } }else{ return MOSQ_ERR_SUCCESS; } } } return rc; } int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos) { struct mosquitto_message_all *cur, *tmp; bool found = false; assert(mosq); assert(message); if(dir == mosq_md_out){ pthread_mutex_lock(&mosq->msgs_out.mutex); DL_FOREACH_SAFE(mosq->msgs_out.inflight, cur, tmp){ if(found == false && cur->msg.mid == mid){ if(cur->msg.qos != qos){ pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_PROTOCOL; } DL_DELETE(mosq->msgs_out.inflight, cur); *message = cur; mosq->msgs_out.queue_len--; found = true; break; } } pthread_mutex_unlock(&mosq->msgs_out.mutex); if(found){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOT_FOUND; } }else{ pthread_mutex_lock(&mosq->msgs_in.mutex); DL_FOREACH_SAFE(mosq->msgs_in.inflight, cur, tmp){ if(cur->msg.mid == mid){ if(cur->msg.qos != qos){ pthread_mutex_unlock(&mosq->msgs_in.mutex); return MOSQ_ERR_PROTOCOL; } DL_DELETE(mosq->msgs_in.inflight, cur); *message = cur; mosq->msgs_in.queue_len--; found = true; break; } } pthread_mutex_unlock(&mosq->msgs_in.mutex); if(found){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOT_FOUND; } } } void message__retry_check(struct mosquitto *mosq) { struct mosquitto_message_all *msg; time_t now = mosquitto_time(); assert(mosq); #ifdef WITH_THREADING pthread_mutex_lock(&mosq->msgs_out.mutex); #endif DL_FOREACH(mosq->msgs_out.inflight, msg){ switch(msg->state){ case mosq_ms_publish_qos1: case mosq_ms_publish_qos2: msg->timestamp = now; msg->dup = true; send__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, msg->properties, NULL, 0); break; case mosq_ms_wait_for_pubrel: msg->timestamp = now; msg->dup = true; send__pubrec(mosq, (uint16_t)msg->msg.mid, 0, NULL); break; case mosq_ms_resend_pubrel: case mosq_ms_wait_for_pubcomp: msg->timestamp = now; msg->dup = true; send__pubrel(mosq, (uint16_t)msg->msg.mid, NULL); break; default: break; } } #ifdef WITH_THREADING pthread_mutex_unlock(&mosq->msgs_out.mutex); #endif } void mosquitto_message_retry_set(struct mosquitto *mosq, unsigned int message_retry) { UNUSED(mosq); UNUSED(message_retry); } int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos) { struct mosquitto_message_all *message, *tmp; assert(mosq); pthread_mutex_lock(&mosq->msgs_out.mutex); DL_FOREACH_SAFE(mosq->msgs_out.inflight, message, tmp){ if(message->msg.mid == mid){ if(message->msg.qos != qos){ pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_PROTOCOL; } message->state = state; message->timestamp = mosquitto_time(); pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_SUCCESS; } } pthread_mutex_unlock(&mosq->msgs_out.mutex); return MOSQ_ERR_NOT_FOUND; } int mosquitto_max_inflight_messages_set(struct mosquitto *mosq, unsigned int max_inflight_messages) { return mosquitto_int_option(mosq, MOSQ_OPT_SEND_MAXIMUM, (int)max_inflight_messages); } mosquitto-2.0.18/lib/messages_mosq.h000066400000000000000000000027321450213760600174430ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MESSAGES_MOSQ_H #define MESSAGES_MOSQ_H #include "mosquitto_internal.h" #include "mosquitto.h" void message__cleanup_all(struct mosquitto *mosq); void message__cleanup(struct mosquitto_message_all **message); int message__delete(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, int qos); int message__queue(struct mosquitto *mosq, struct mosquitto_message_all *message, enum mosquitto_msg_direction dir); void message__reconnect_reset(struct mosquitto *mosq, bool update_quota_only); int message__release_to_inflight(struct mosquitto *mosq, enum mosquitto_msg_direction dir); int message__remove(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_direction dir, struct mosquitto_message_all **message, int qos); void message__retry_check(struct mosquitto *mosq); int message__out_update(struct mosquitto *mosq, uint16_t mid, enum mosquitto_msg_state state, int qos); #endif mosquitto-2.0.18/lib/misc_mosq.c000066400000000000000000000130271450213760600165610ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ /* This contains general purpose utility functions that are not specific to * Mosquitto/MQTT features. */ #include "config.h" #include #include #include #include #include #include #include #ifdef WIN32 # include # include # include # include # include # define PATH_MAX MAX_PATH #else # include # include # include # include #endif #include "misc_mosq.h" #include "logging_mosq.h" FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) { #ifdef WIN32 char buf[4096]; int rc; int flags = 0; rc = ExpandEnvironmentStringsA(path, buf, 4096); if(rc == 0 || rc > 4096){ return NULL; }else{ if (restrict_read) { HANDLE hfile; SECURITY_ATTRIBUTES sec; EXPLICIT_ACCESS_A ea; PACL pacl = NULL; char username[UNLEN + 1]; DWORD ulen = UNLEN; SECURITY_DESCRIPTOR sd; DWORD dwCreationDisposition; int fd; FILE *fptr; switch(mode[0]){ case 'a': dwCreationDisposition = OPEN_ALWAYS; flags = _O_APPEND; break; case 'r': dwCreationDisposition = OPEN_EXISTING; flags = _O_RDONLY; break; case 'w': dwCreationDisposition = CREATE_ALWAYS; break; default: return NULL; } GetUserNameA(username, &ulen); if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { return NULL; } BuildExplicitAccessWithNameA(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE); if (SetEntriesInAclA(1, &ea, NULL, &pacl) != ERROR_SUCCESS) { return NULL; } if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { LocalFree(pacl); return NULL; } memset(&sec, 0, sizeof(sec)); sec.nLength = sizeof(SECURITY_ATTRIBUTES); sec.bInheritHandle = FALSE; sec.lpSecurityDescriptor = &sd; hfile = CreateFileA(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, &sec, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); LocalFree(pacl); fd = _open_osfhandle((intptr_t)hfile, flags); if (fd < 0) { return NULL; } fptr = _fdopen(fd, mode); if (!fptr) { _close(fd); return NULL; } if(mode[0] == 'a'){ fseek(fptr, 0, SEEK_END); } return fptr; }else { return fopen(buf, mode); } } #else FILE *fptr; struct stat statbuf; if (restrict_read) { mode_t old_mask; old_mask = umask(0077); fptr = fopen(path, mode); umask(old_mask); }else{ fptr = fopen(path, mode); } if(!fptr) return NULL; if(fstat(fileno(fptr), &statbuf) < 0){ fclose(fptr); return NULL; } if(restrict_read){ if(statbuf.st_mode & S_IRWXO){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_WARNING, #else fprintf(stderr, #endif "Warning: File %s has world readable permissions. Future versions will refuse to load this file.\n" "To fix this, use `chmod 0700 %s`.", path, path); #if 0 return NULL; #endif } if(statbuf.st_uid != getuid()){ char buf[4096]; struct passwd pw, *result; getpwuid_r(getuid(), &pw, buf, sizeof(buf), &result); if(result){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_WARNING, #else fprintf(stderr, #endif "Warning: File %s owner is not %s. Future versions will refuse to load this file." "To fix this, use `chown %s %s`.", path, result->pw_name, result->pw_name, path); } #if 0 // Future version return NULL; #endif } if(statbuf.st_gid != getgid()){ char buf[4096]; struct group grp, *result; getgrgid_r(getgid(), &grp, buf, sizeof(buf), &result); if(result){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_WARNING, #else fprintf(stderr, #endif "Warning: File %s group is not %s. Future versions will refuse to load this file.", path, result->gr_name); } #if 0 // Future version return NULL #endif } } if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path); #endif fclose(fptr); return NULL; } return fptr; #endif } char *misc__trimblanks(char *str) { char *endptr; if(str == NULL) return NULL; while(isspace(str[0])){ str++; } endptr = &str[strlen(str)-1]; while(endptr > str && isspace(endptr[0])){ endptr[0] = '\0'; endptr--; } return str; } char *fgets_extending(char **buf, int *buflen, FILE *stream) { char *rc; char endchar; int offset = 0; char *newbuf; size_t len; if(stream == NULL || buf == NULL || buflen == NULL || *buflen < 1){ return NULL; } do{ rc = fgets(&((*buf)[offset]), (*buflen)-offset, stream); if(feof(stream) || rc == NULL){ return rc; } len = strlen(*buf); if(len == 0){ return rc; } endchar = (*buf)[len-1]; if(endchar == '\n'){ return rc; } /* No EOL char found, so extend buffer */ offset = (*buflen)-1; *buflen += 1000; newbuf = realloc(*buf, (size_t)*buflen); if(!newbuf){ return NULL; } *buf = newbuf; }while(1); } mosquitto-2.0.18/lib/misc_mosq.h000066400000000000000000000015231450213760600165640ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MISC_MOSQ_H #define MISC_MOSQ_H #include #include FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read); char *misc__trimblanks(char *str); char *fgets_extending(char **buf, int *buflen, FILE *stream); #endif mosquitto-2.0.18/lib/mosquitto.c000066400000000000000000000226401450213760600166340ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifndef WIN32 #include #include #endif #if defined(__APPLE__) # include #endif #include "logging_mosq.h" #include "mosquitto.h" #include "mosquitto_internal.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "will_mosq.h" static unsigned int init_refcount = 0; void mosquitto__destroy(struct mosquitto *mosq); int mosquitto_lib_version(int *major, int *minor, int *revision) { if(major) *major = LIBMOSQUITTO_MAJOR; if(minor) *minor = LIBMOSQUITTO_MINOR; if(revision) *revision = LIBMOSQUITTO_REVISION; return LIBMOSQUITTO_VERSION_NUMBER; } int mosquitto_lib_init(void) { int rc; if (init_refcount == 0) { #ifdef WIN32 srand((unsigned int)GetTickCount64()); #elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK) struct timespec tp; #ifdef CLOCK_BOOTTIME clock_gettime(CLOCK_BOOTTIME, &tp); #else clock_gettime(CLOCK_MONOTONIC, &tp); #endif srand((unsigned int)tp.tv_nsec); #elif defined(__APPLE__) uint64_t ticks; ticks = mach_absolute_time(); srand((unsigned int)ticks); #else struct timeval tv; gettimeofday(&tv, NULL); srand(tv.tv_sec*1000 + tv.tv_usec/1000); #endif rc = net__init(); if (rc != MOSQ_ERR_SUCCESS) { return rc; } } init_refcount++; return MOSQ_ERR_SUCCESS; } int mosquitto_lib_cleanup(void) { if (init_refcount == 1) { net__cleanup(); } if (init_refcount > 0) { --init_refcount; } return MOSQ_ERR_SUCCESS; } struct mosquitto *mosquitto_new(const char *id, bool clean_start, void *userdata) { struct mosquitto *mosq = NULL; int rc; if(clean_start == false && id == NULL){ errno = EINVAL; return NULL; } mosq = (struct mosquitto *)mosquitto__calloc(1, sizeof(struct mosquitto)); if(mosq){ mosq->sock = INVALID_SOCKET; #ifdef WITH_THREADING mosq->thread_id = pthread_self(); #endif mosq->sockpairR = INVALID_SOCKET; mosq->sockpairW = INVALID_SOCKET; rc = mosquitto_reinitialise(mosq, id, clean_start, userdata); if(rc){ mosquitto_destroy(mosq); if(rc == MOSQ_ERR_INVAL){ errno = EINVAL; }else if(rc == MOSQ_ERR_NOMEM){ errno = ENOMEM; } return NULL; } }else{ errno = ENOMEM; } return mosq; } int mosquitto_reinitialise(struct mosquitto *mosq, const char *id, bool clean_start, void *userdata) { if(!mosq) return MOSQ_ERR_INVAL; if(clean_start == false && id == NULL){ return MOSQ_ERR_INVAL; } mosquitto__destroy(mosq); memset(mosq, 0, sizeof(struct mosquitto)); if(userdata){ mosq->userdata = userdata; }else{ mosq->userdata = mosq; } mosq->protocol = mosq_p_mqtt311; mosq->sock = INVALID_SOCKET; mosq->sockpairR = INVALID_SOCKET; mosq->sockpairW = INVALID_SOCKET; mosq->keepalive = 60; mosq->clean_start = clean_start; if(id){ if(STREMPTY(id)){ return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(id, (int)strlen(id))){ return MOSQ_ERR_MALFORMED_UTF8; } mosq->id = mosquitto__strdup(id); if(!mosq->id){ return MOSQ_ERR_NOMEM; } } mosq->in_packet.payload = NULL; packet__cleanup(&mosq->in_packet); mosq->out_packet = NULL; mosq->out_packet_count = 0; mosq->current_out_packet = NULL; mosq->last_msg_in = mosquitto_time(); mosq->next_msg_out = mosquitto_time() + mosq->keepalive; mosq->ping_t = 0; mosq->last_mid = 0; mosq->state = mosq_cs_new; mosq->max_qos = 2; mosq->msgs_in.inflight_maximum = 20; mosq->msgs_out.inflight_maximum = 20; mosq->msgs_in.inflight_quota = 20; mosq->msgs_out.inflight_quota = 20; mosq->will = NULL; mosq->on_connect = NULL; mosq->on_publish = NULL; mosq->on_message = NULL; mosq->on_subscribe = NULL; mosq->on_unsubscribe = NULL; mosq->host = NULL; mosq->port = 1883; mosq->in_callback = false; mosq->reconnect_delay = 1; mosq->reconnect_delay_max = 1; mosq->reconnect_exponential_backoff = false; mosq->threaded = mosq_ts_none; #ifdef WITH_TLS mosq->ssl = NULL; mosq->ssl_ctx = NULL; mosq->ssl_ctx_defaults = true; mosq->tls_cert_reqs = SSL_VERIFY_PEER; mosq->tls_insecure = false; mosq->want_write = false; mosq->tls_ocsp_required = false; #endif #ifdef WITH_THREADING pthread_mutex_init(&mosq->callback_mutex, NULL); pthread_mutex_init(&mosq->log_callback_mutex, NULL); pthread_mutex_init(&mosq->state_mutex, NULL); pthread_mutex_init(&mosq->out_packet_mutex, NULL); pthread_mutex_init(&mosq->current_out_packet_mutex, NULL); pthread_mutex_init(&mosq->msgtime_mutex, NULL); pthread_mutex_init(&mosq->msgs_in.mutex, NULL); pthread_mutex_init(&mosq->msgs_out.mutex, NULL); pthread_mutex_init(&mosq->mid_mutex, NULL); mosq->thread_id = pthread_self(); #endif /* This must be after pthread_mutex_init(), otherwise the log mutex may be * used before being initialised. */ if(net__socketpair(&mosq->sockpairR, &mosq->sockpairW)){ log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Unable to open socket pair, outgoing publish commands may be delayed."); } return MOSQ_ERR_SUCCESS; } void mosquitto__destroy(struct mosquitto *mosq) { if(!mosq) return; #ifdef WITH_THREADING # ifdef HAVE_PTHREAD_CANCEL if(mosq->threaded == mosq_ts_self && !pthread_equal(mosq->thread_id, pthread_self())){ pthread_cancel(mosq->thread_id); pthread_join(mosq->thread_id, NULL); mosq->threaded = mosq_ts_none; } # endif if(mosq->id){ /* If mosq->id is not NULL then the client has already been initialised * and so the mutexes need destroying. If mosq->id is NULL, the mutexes * haven't been initialised. */ pthread_mutex_destroy(&mosq->callback_mutex); pthread_mutex_destroy(&mosq->log_callback_mutex); pthread_mutex_destroy(&mosq->state_mutex); pthread_mutex_destroy(&mosq->out_packet_mutex); pthread_mutex_destroy(&mosq->current_out_packet_mutex); pthread_mutex_destroy(&mosq->msgtime_mutex); pthread_mutex_destroy(&mosq->msgs_in.mutex); pthread_mutex_destroy(&mosq->msgs_out.mutex); pthread_mutex_destroy(&mosq->mid_mutex); } #endif if(mosq->sock != INVALID_SOCKET){ net__socket_close(mosq); } message__cleanup_all(mosq); will__clear(mosq); #ifdef WITH_TLS if(mosq->ssl){ SSL_free(mosq->ssl); } if(mosq->ssl_ctx){ SSL_CTX_free(mosq->ssl_ctx); } mosquitto__free(mosq->tls_cafile); mosquitto__free(mosq->tls_capath); mosquitto__free(mosq->tls_certfile); mosquitto__free(mosq->tls_keyfile); if(mosq->tls_pw_callback) mosq->tls_pw_callback = NULL; mosquitto__free(mosq->tls_version); mosquitto__free(mosq->tls_ciphers); mosquitto__free(mosq->tls_psk); mosquitto__free(mosq->tls_psk_identity); mosquitto__free(mosq->tls_alpn); #endif mosquitto__free(mosq->address); mosq->address = NULL; mosquitto__free(mosq->id); mosq->id = NULL; mosquitto__free(mosq->username); mosq->username = NULL; mosquitto__free(mosq->password); mosq->password = NULL; mosquitto__free(mosq->host); mosq->host = NULL; mosquitto__free(mosq->bind_address); mosq->bind_address = NULL; mosquitto_property_free_all(&mosq->connect_properties); packet__cleanup_all_no_locks(mosq); packet__cleanup(&mosq->in_packet); if(mosq->sockpairR != INVALID_SOCKET){ COMPAT_CLOSE(mosq->sockpairR); mosq->sockpairR = INVALID_SOCKET; } if(mosq->sockpairW != INVALID_SOCKET){ COMPAT_CLOSE(mosq->sockpairW); mosq->sockpairW = INVALID_SOCKET; } } void mosquitto_destroy(struct mosquitto *mosq) { if(!mosq) return; mosquitto__destroy(mosq); mosquitto__free(mosq); } int mosquitto_socket(struct mosquitto *mosq) { if(!mosq) return INVALID_SOCKET; return mosq->sock; } bool mosquitto_want_write(struct mosquitto *mosq) { return mosq->out_packet || mosq->current_out_packet || mosq->want_write; } int mosquitto_sub_topic_tokenise(const char *subtopic, char ***topics, int *count) { size_t len; size_t hier_count = 1; size_t start, stop; size_t hier; size_t tlen; size_t i, j; if(!subtopic || !topics || !count) return MOSQ_ERR_INVAL; len = strlen(subtopic); for(i=0; i len-1){ /* Separator at end of line */ }else{ hier_count++; } } } (*topics) = mosquitto__calloc(hier_count, sizeof(char *)); if(!(*topics)) return MOSQ_ERR_NOMEM; start = 0; hier = 0; for(i=0; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. Tatsuzo Osawa - Add epoll. */ #ifndef MOSQUITTO_INTERNAL_H #define MOSQUITTO_INTERNAL_H #include "config.h" #ifdef WIN32 # include #endif #ifdef WITH_TLS # include #else # include #endif #include #if defined(WITH_THREADING) && !defined(WITH_BROKER) # include #else # include #endif #ifdef WITH_SRV # include #endif #ifdef WIN32 # if _MSC_VER < 1600 typedef unsigned char uint8_t; typedef unsigned short uint16_t; typedef unsigned int uint32_t; typedef unsigned long long uint64_t; # else # include # endif #else # include #endif #include "mosquitto.h" #include "time_mosq.h" #ifdef WITH_BROKER # ifdef __linux__ # include # endif # include "uthash.h" struct mosquitto_client_msg; #endif #ifdef WIN32 typedef SOCKET mosq_sock_t; #else typedef int mosq_sock_t; #endif #define SAFE_PRINT(A) (A)?(A):"null" enum mosquitto_msg_direction { mosq_md_in = 0, mosq_md_out = 1 }; enum mosquitto_msg_state { mosq_ms_invalid = 0, mosq_ms_publish_qos0 = 1, mosq_ms_publish_qos1 = 2, mosq_ms_wait_for_puback = 3, mosq_ms_publish_qos2 = 4, mosq_ms_wait_for_pubrec = 5, mosq_ms_resend_pubrel = 6, mosq_ms_wait_for_pubrel = 7, mosq_ms_resend_pubcomp = 8, mosq_ms_wait_for_pubcomp = 9, mosq_ms_send_pubrec = 10, mosq_ms_queued = 11 }; enum mosquitto_client_state { mosq_cs_new = 0, mosq_cs_connected = 1, mosq_cs_disconnecting = 2, mosq_cs_active = 3, mosq_cs_connect_pending = 4, mosq_cs_connect_srv = 5, mosq_cs_disconnect_ws = 6, mosq_cs_disconnected = 7, mosq_cs_socks5_new = 8, mosq_cs_socks5_start = 9, mosq_cs_socks5_request = 10, mosq_cs_socks5_reply = 11, mosq_cs_socks5_auth_ok = 12, mosq_cs_socks5_userpass_reply = 13, mosq_cs_socks5_send_userpass = 14, mosq_cs_expiring = 15, mosq_cs_duplicate = 17, /* client that has been taken over by another with the same id */ mosq_cs_disconnect_with_will = 18, mosq_cs_disused = 19, /* client that has been added to the disused list to be freed */ mosq_cs_authenticating = 20, /* Client has sent CONNECT but is still undergoing extended authentication */ mosq_cs_reauthenticating = 21, /* Client is undergoing reauthentication and shouldn't do anything else until complete */ }; enum mosquitto__protocol { mosq_p_invalid = 0, mosq_p_mqtt31 = 1, mosq_p_mqtt311 = 2, mosq_p_mqtts = 3, mosq_p_mqtt5 = 5, }; enum mosquitto__threaded_state { mosq_ts_none, /* No threads in use */ mosq_ts_self, /* Threads started by libmosquitto */ mosq_ts_external /* Threads started by external code */ }; enum mosquitto__transport { mosq_t_invalid = 0, mosq_t_tcp = 1, mosq_t_ws = 2, mosq_t_sctp = 3 }; struct mosquitto__alias{ char *topic; uint16_t alias; }; struct session_expiry_list { struct mosquitto *context; struct session_expiry_list *prev; struct session_expiry_list *next; }; struct mosquitto__packet{ uint8_t *payload; struct mosquitto__packet *next; uint32_t remaining_mult; uint32_t remaining_length; uint32_t packet_length; uint32_t to_process; uint32_t pos; uint16_t mid; uint8_t command; int8_t remaining_count; }; struct mosquitto_message_all{ struct mosquitto_message_all *next; struct mosquitto_message_all *prev; mosquitto_property *properties; time_t timestamp; enum mosquitto_msg_state state; bool dup; struct mosquitto_message msg; uint32_t expiry_interval; }; #ifdef WITH_TLS enum mosquitto__keyform { mosq_k_pem = 0, mosq_k_engine = 1, }; #endif struct will_delay_list { struct mosquitto *context; struct will_delay_list *prev; struct will_delay_list *next; }; struct mosquitto_msg_data{ #ifdef WITH_BROKER struct mosquitto_client_msg *inflight; struct mosquitto_client_msg *queued; long inflight_bytes; long inflight_bytes12; int inflight_count; int inflight_count12; long queued_bytes; long queued_bytes12; int queued_count; int queued_count12; #else struct mosquitto_message_all *inflight; int queue_len; # ifdef WITH_THREADING pthread_mutex_t mutex; # endif #endif int inflight_quota; uint16_t inflight_maximum; }; struct mosquitto { #if defined(WITH_BROKER) && defined(WITH_EPOLL) /* This *must* be the first element in the struct. */ int ident; #endif mosq_sock_t sock; #ifndef WITH_BROKER mosq_sock_t sockpairR, sockpairW; #endif uint32_t maximum_packet_size; #if defined(__GLIBC__) && defined(WITH_ADNS) struct gaicb *adns; /* For getaddrinfo_a */ #endif enum mosquitto__protocol protocol; char *address; char *id; char *username; char *password; uint16_t keepalive; uint16_t last_mid; enum mosquitto_client_state state; time_t last_msg_in; time_t next_msg_out; time_t ping_t; struct mosquitto__packet in_packet; struct mosquitto__packet *current_out_packet; struct mosquitto__packet *out_packet; struct mosquitto_message_all *will; struct mosquitto__alias *aliases; struct will_delay_list *will_delay_entry; int alias_count; int out_packet_count; uint32_t will_delay_interval; time_t will_delay_time; #ifdef WITH_TLS SSL *ssl; SSL_CTX *ssl_ctx; #ifndef WITH_BROKER SSL_CTX *user_ssl_ctx; #endif char *tls_cafile; char *tls_capath; char *tls_certfile; char *tls_keyfile; int (*tls_pw_callback)(char *buf, int size, int rwflag, void *userdata); char *tls_version; char *tls_ciphers; char *tls_psk; char *tls_psk_identity; char *tls_engine; char *tls_engine_kpass_sha1; char *tls_alpn; int tls_cert_reqs; bool tls_insecure; bool ssl_ctx_defaults; bool tls_ocsp_required; bool tls_use_os_certs; enum mosquitto__keyform tls_keyform; #endif bool want_write; #if defined(WITH_THREADING) && !defined(WITH_BROKER) pthread_mutex_t callback_mutex; pthread_mutex_t log_callback_mutex; pthread_mutex_t msgtime_mutex; pthread_mutex_t out_packet_mutex; pthread_mutex_t current_out_packet_mutex; pthread_mutex_t state_mutex; pthread_mutex_t mid_mutex; pthread_t thread_id; #endif bool clean_start; time_t session_expiry_time; uint32_t session_expiry_interval; #ifdef WITH_BROKER bool in_by_id; bool is_dropping; bool is_bridge; struct mosquitto__bridge *bridge; struct mosquitto_msg_data msgs_in; struct mosquitto_msg_data msgs_out; struct mosquitto__acl_user *acl_list; struct mosquitto__listener *listener; struct mosquitto__packet *out_packet_last; struct mosquitto__client_sub **subs; char *auth_method; int sub_count; # ifndef WITH_EPOLL int pollfd_index; # endif # ifdef WITH_WEBSOCKETS struct lws *wsi; # endif bool ws_want_write; bool assigned_id; #else # ifdef WITH_SOCKS char *socks5_host; uint16_t socks5_port; char *socks5_username; char *socks5_password; # endif void *userdata; bool in_callback; struct mosquitto_msg_data msgs_in; struct mosquitto_msg_data msgs_out; void (*on_connect)(struct mosquitto *, void *userdata, int rc); void (*on_connect_with_flags)(struct mosquitto *, void *userdata, int rc, int flags); void (*on_connect_v5)(struct mosquitto *, void *userdata, int rc, int flags, const mosquitto_property *props); void (*on_disconnect)(struct mosquitto *, void *userdata, int rc); void (*on_disconnect_v5)(struct mosquitto *, void *userdata, int rc, const mosquitto_property *props); void (*on_publish)(struct mosquitto *, void *userdata, int mid); void (*on_publish_v5)(struct mosquitto *, void *userdata, int mid, int reason_code, const mosquitto_property *props); void (*on_message)(struct mosquitto *, void *userdata, const struct mosquitto_message *message); void (*on_message_v5)(struct mosquitto *, void *userdata, const struct mosquitto_message *message, const mosquitto_property *props); void (*on_subscribe)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos); void (*on_subscribe_v5)(struct mosquitto *, void *userdata, int mid, int qos_count, const int *granted_qos, const mosquitto_property *props); void (*on_unsubscribe)(struct mosquitto *, void *userdata, int mid); void (*on_unsubscribe_v5)(struct mosquitto *, void *userdata, int mid, const mosquitto_property *props); void (*on_log)(struct mosquitto *, void *userdata, int level, const char *str); /*void (*on_error)();*/ char *host; uint16_t port; char *bind_address; unsigned int reconnects; unsigned int reconnect_delay; unsigned int reconnect_delay_max; bool reconnect_exponential_backoff; bool request_disconnect; char threaded; struct mosquitto__packet *out_packet_last; mosquitto_property *connect_properties; # ifdef WITH_SRV ares_channel achan; # endif #endif uint8_t max_qos; uint8_t retain_available; bool tcp_nodelay; #ifdef WITH_BROKER UT_hash_handle hh_id; UT_hash_handle hh_sock; struct mosquitto *for_free_next; struct session_expiry_list *expiry_list_item; uint16_t remote_port; #endif uint32_t events; }; #define STREMPTY(str) (str[0] == '\0') void do_client_disconnect(struct mosquitto *mosq, int reason_code, const mosquitto_property *properties); #endif mosquitto-2.0.18/lib/net_mosq.c000066400000000000000000000675401450213760600164250ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #define _GNU_SOURCE #include "config.h" #include #include #include #include #include #ifndef WIN32 #define _GNU_SOURCE #include #include #include #include #else #include #include #endif #ifdef __ANDROID__ #include #include #include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef WITH_UNIX_SOCKETS # include #endif #ifdef __QNX__ #include #endif #ifdef WITH_TLS #include #include #include #include #include #endif #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" # ifdef WITH_WEBSOCKETS # include # endif #else # include "read_handle.h" #endif #include "logging_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "time_mosq.h" #include "util_mosq.h" #ifdef WITH_TLS int tls_ex_index_mosq = -1; UI_METHOD *_ui_method = NULL; static bool is_tls_initialized = false; /* Functions taken from OpenSSL s_server/s_client */ static int ui_open(UI *ui) { return UI_method_get_opener(UI_OpenSSL())(ui); } static int ui_read(UI *ui, UI_STRING *uis) { return UI_method_get_reader(UI_OpenSSL())(ui, uis); } static int ui_write(UI *ui, UI_STRING *uis) { return UI_method_get_writer(UI_OpenSSL())(ui, uis); } static int ui_close(UI *ui) { return UI_method_get_closer(UI_OpenSSL())(ui); } static void setup_ui_method(void) { _ui_method = UI_create_method("OpenSSL application user interface"); UI_method_set_opener(_ui_method, ui_open); UI_method_set_reader(_ui_method, ui_read); UI_method_set_writer(_ui_method, ui_write); UI_method_set_closer(_ui_method, ui_close); } static void cleanup_ui_method(void) { if(_ui_method){ UI_destroy_method(_ui_method); _ui_method = NULL; } } UI_METHOD *net__get_ui_method(void) { return _ui_method; } #endif int net__init(void) { #ifdef WIN32 WSADATA wsaData; if(WSAStartup(MAKEWORD(2,2), &wsaData) != 0){ return MOSQ_ERR_UNKNOWN; } #endif #ifdef WITH_SRV ares_library_init(ARES_LIB_INIT_ALL); #endif return MOSQ_ERR_SUCCESS; } void net__cleanup(void) { #ifdef WITH_TLS # if OPENSSL_VERSION_NUMBER < 0x10100000L CRYPTO_cleanup_all_ex_data(); ERR_free_strings(); ERR_remove_thread_state(NULL); EVP_cleanup(); # if !defined(OPENSSL_NO_ENGINE) ENGINE_cleanup(); # endif is_tls_initialized = false; # endif CONF_modules_unload(1); cleanup_ui_method(); #endif #ifdef WITH_SRV ares_library_cleanup(); #endif #ifdef WIN32 WSACleanup(); #endif } #ifdef WITH_TLS void net__init_tls(void) { if(is_tls_initialized) return; # if OPENSSL_VERSION_NUMBER < 0x10100000L SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); # else OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ | OPENSSL_INIT_ADD_ALL_DIGESTS \ | OPENSSL_INIT_LOAD_CONFIG, NULL); # endif #if !defined(OPENSSL_NO_ENGINE) ENGINE_load_builtin_engines(); #endif setup_ui_method(); if(tls_ex_index_mosq == -1){ tls_ex_index_mosq = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL); } is_tls_initialized = true; } #endif /* Close a socket associated with a context and set it to -1. * Returns 1 on failure (context is NULL) * Returns 0 on success. */ int net__socket_close(struct mosquitto *mosq) { int rc = 0; #ifdef WITH_BROKER struct mosquitto *mosq_found; #endif assert(mosq); #ifdef WITH_TLS #ifdef WITH_WEBSOCKETS if(!mosq->wsi) #endif { if(mosq->ssl){ if(!SSL_in_init(mosq->ssl)){ SSL_shutdown(mosq->ssl); } SSL_free(mosq->ssl); mosq->ssl = NULL; } } #endif #ifdef WITH_WEBSOCKETS if(mosq->wsi) { if(mosq->state != mosq_cs_disconnecting){ mosquitto__set_state(mosq, mosq_cs_disconnect_ws); } lws_callback_on_writable(mosq->wsi); }else #endif { if(mosq->sock != INVALID_SOCKET){ #ifdef WITH_BROKER HASH_FIND(hh_sock, db.contexts_by_sock, &mosq->sock, sizeof(mosq->sock), mosq_found); if(mosq_found){ HASH_DELETE(hh_sock, db.contexts_by_sock, mosq_found); } #endif rc = COMPAT_CLOSE(mosq->sock); mosq->sock = INVALID_SOCKET; } } #ifdef WITH_BROKER if(mosq->listener){ mosq->listener->client_count--; mosq->listener = NULL; } #endif return rc; } #ifdef FINAL_WITH_TLS_PSK static unsigned int psk_client_callback(SSL *ssl, const char *hint, char *identity, unsigned int max_identity_len, unsigned char *psk, unsigned int max_psk_len) { struct mosquitto *mosq; int len; UNUSED(hint); mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq); if(!mosq) return 0; snprintf(identity, max_identity_len, "%s", mosq->tls_psk_identity); len = mosquitto__hex2bin(mosq->tls_psk, psk, (int)max_psk_len); if (len < 0) return 0; return (unsigned int)len; } #endif #if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS) /* Async connect, part 1 (dns lookup) */ int net__try_connect_step1(struct mosquitto *mosq, const char *host) { int s; void *sevp = NULL; struct addrinfo *hints; if(mosq->adns){ gai_cancel(mosq->adns); mosquitto__free((struct addrinfo *)mosq->adns->ar_request); mosquitto__free(mosq->adns); } mosq->adns = mosquitto__calloc(1, sizeof(struct gaicb)); if(!mosq->adns){ return MOSQ_ERR_NOMEM; } hints = mosquitto__calloc(1, sizeof(struct addrinfo)); if(!hints){ mosquitto__free(mosq->adns); mosq->adns = NULL; return MOSQ_ERR_NOMEM; } hints->ai_family = AF_UNSPEC; hints->ai_socktype = SOCK_STREAM; mosq->adns->ar_name = host; mosq->adns->ar_request = hints; s = getaddrinfo_a(GAI_NOWAIT, &mosq->adns, 1, sevp); if(s){ errno = s; if(mosq->adns){ mosquitto__free((struct addrinfo *)mosq->adns->ar_request); mosquitto__free(mosq->adns); mosq->adns = NULL; } return MOSQ_ERR_EAI; } return MOSQ_ERR_SUCCESS; } /* Async connect part 2, the connection. */ int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock) { struct addrinfo *ainfo, *rp; int rc; ainfo = mosq->adns->ar_result; for(rp = ainfo; rp != NULL; rp = rp->ai_next){ *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(*sock == INVALID_SOCKET) continue; if(rp->ai_family == AF_INET){ ((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port); }else if(rp->ai_family == AF_INET6){ ((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port); }else{ COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; continue; } /* Set non-blocking */ if(net__socket_nonblock(sock)){ continue; } rc = connect(*sock, rp->ai_addr, rp->ai_addrlen); #ifdef WIN32 errno = WSAGetLastError(); #endif if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){ if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){ rc = MOSQ_ERR_CONN_PENDING; } /* Set non-blocking */ if(net__socket_nonblock(sock)){ continue; } break; } COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; } freeaddrinfo(mosq->adns->ar_result); mosq->adns->ar_result = NULL; mosquitto__free((struct addrinfo *)mosq->adns->ar_request); mosquitto__free(mosq->adns); mosq->adns = NULL; if(!rp){ return MOSQ_ERR_ERRNO; } return rc; } #endif static int net__try_connect_tcp(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking) { struct addrinfo hints; struct addrinfo *ainfo, *rp; struct addrinfo *ainfo_bind, *rp_bind; int s; int rc = MOSQ_ERR_SUCCESS; ainfo_bind = NULL; *sock = INVALID_SOCKET; memset(&hints, 0, sizeof(struct addrinfo)); hints.ai_family = AF_UNSPEC; hints.ai_socktype = SOCK_STREAM; s = getaddrinfo(host, NULL, &hints, &ainfo); if(s){ errno = s; return MOSQ_ERR_EAI; } if(bind_address){ s = getaddrinfo(bind_address, NULL, &hints, &ainfo_bind); if(s){ freeaddrinfo(ainfo); errno = s; return MOSQ_ERR_EAI; } } for(rp = ainfo; rp != NULL; rp = rp->ai_next){ *sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(*sock == INVALID_SOCKET) continue; if(rp->ai_family == AF_INET){ ((struct sockaddr_in *)rp->ai_addr)->sin_port = htons(port); }else if(rp->ai_family == AF_INET6){ ((struct sockaddr_in6 *)rp->ai_addr)->sin6_port = htons(port); }else{ COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; continue; } if(bind_address){ for(rp_bind = ainfo_bind; rp_bind != NULL; rp_bind = rp_bind->ai_next){ if(bind(*sock, rp_bind->ai_addr, rp_bind->ai_addrlen) == 0){ break; } } if(!rp_bind){ COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; continue; } } if(!blocking){ /* Set non-blocking */ if(net__socket_nonblock(sock)){ continue; } } rc = connect(*sock, rp->ai_addr, rp->ai_addrlen); #ifdef WIN32 errno = WSAGetLastError(); #endif if(rc == 0 || errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK){ if(rc < 0 && (errno == EINPROGRESS || errno == COMPAT_EWOULDBLOCK)){ rc = MOSQ_ERR_CONN_PENDING; } if(blocking){ /* Set non-blocking */ if(net__socket_nonblock(sock)){ continue; } } break; } COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; } freeaddrinfo(ainfo); if(bind_address){ freeaddrinfo(ainfo_bind); } if(!rp){ return MOSQ_ERR_ERRNO; } return rc; } #ifdef WITH_UNIX_SOCKETS static int net__try_connect_unix(const char *host, mosq_sock_t *sock) { struct sockaddr_un addr; int s; int rc; if(host == NULL || strlen(host) == 0 || strlen(host) > sizeof(addr.sun_path)-1){ return MOSQ_ERR_INVAL; } memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, host, sizeof(addr.sun_path)-1); s = socket(AF_UNIX, SOCK_STREAM, 0); if(s < 0){ return MOSQ_ERR_ERRNO; } rc = net__socket_nonblock(&s); if(rc) return rc; rc = connect(s, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); if(rc < 0){ close(s); return MOSQ_ERR_ERRNO; } *sock = s; return 0; } #endif int net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking) { if(port == 0){ #ifdef WITH_UNIX_SOCKETS return net__try_connect_unix(host, sock); #else return MOSQ_ERR_NOT_SUPPORTED; #endif }else{ return net__try_connect_tcp(host, port, sock, bind_address, blocking); } } #ifdef WITH_TLS void net__print_ssl_error(struct mosquitto *mosq) { char ebuf[256]; unsigned long e; int num = 0; e = ERR_get_error(); while(e){ log__printf(mosq, MOSQ_LOG_ERR, "OpenSSL Error[%d]: %s", num, ERR_error_string(e, ebuf)); e = ERR_get_error(); num++; } } int net__socket_connect_tls(struct mosquitto *mosq) { int ret, err; long res; ERR_clear_error(); if (mosq->tls_ocsp_required) { /* Note: OCSP is available in all currently supported OpenSSL versions. */ if ((res=SSL_set_tlsext_status_type(mosq->ssl, TLSEXT_STATUSTYPE_ocsp)) != 1) { log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); return MOSQ_ERR_OCSP; } if ((res=SSL_CTX_set_tlsext_status_cb(mosq->ssl_ctx, mosquitto__verify_ocsp_status_cb)) != 1) { log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); return MOSQ_ERR_OCSP; } if ((res=SSL_CTX_set_tlsext_status_arg(mosq->ssl_ctx, mosq)) != 1) { log__printf(mosq, MOSQ_LOG_ERR, "Could not activate OCSP (error: %ld)", res); return MOSQ_ERR_OCSP; } } SSL_set_connect_state(mosq->ssl); return MOSQ_ERR_SUCCESS; } #endif #ifdef WITH_TLS static int net__tls_load_ca(struct mosquitto *mosq) { int ret; if(mosq->tls_use_os_certs){ SSL_CTX_set_default_verify_paths(mosq->ssl_ctx); } #if OPENSSL_VERSION_NUMBER < 0x30000000L if(mosq->tls_cafile || mosq->tls_capath){ ret = SSL_CTX_load_verify_locations(mosq->ssl_ctx, mosq->tls_cafile, mosq->tls_capath); if(ret == 0){ # ifdef WITH_BROKER if(mosq->tls_cafile && mosq->tls_capath){ log__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); }else if(mosq->tls_cafile){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_cafile \"%s\".", mosq->tls_cafile); }else{ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_capath \"%s\".", mosq->tls_capath); } # else if(mosq->tls_cafile && mosq->tls_capath){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\" and capath \"%s\".", mosq->tls_cafile, mosq->tls_capath); }else if(mosq->tls_cafile){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\".", mosq->tls_cafile); }else{ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath); } # endif return MOSQ_ERR_TLS; } } #else if(mosq->tls_cafile){ ret = SSL_CTX_load_verify_file(mosq->ssl_ctx, mosq->tls_cafile); if(ret == 0){ # ifdef WITH_BROKER log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_cafile \"%s\".", mosq->tls_cafile); # else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check cafile \"%s\".", mosq->tls_cafile); # endif return MOSQ_ERR_TLS; } } if(mosq->tls_capath){ ret = SSL_CTX_load_verify_dir(mosq->ssl_ctx, mosq->tls_capath); if(ret == 0){ # ifdef WITH_BROKER log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check bridge_capath \"%s\".", mosq->tls_capath); # else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath); # endif return MOSQ_ERR_TLS; } } #endif return MOSQ_ERR_SUCCESS; } static int net__init_ssl_ctx(struct mosquitto *mosq) { int ret; ENGINE *engine = NULL; uint8_t tls_alpn_wire[256]; uint8_t tls_alpn_len; #if !defined(OPENSSL_NO_ENGINE) EVP_PKEY *pkey; #endif #ifndef WITH_BROKER if(mosq->user_ssl_ctx){ mosq->ssl_ctx = mosq->user_ssl_ctx; if(!mosq->ssl_ctx_defaults){ return MOSQ_ERR_SUCCESS; }else if(!mosq->tls_cafile && !mosq->tls_capath && !mosq->tls_psk){ log__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."); return MOSQ_ERR_INVAL; } } #endif /* Apply default SSL_CTX settings. This is only used if MOSQ_OPT_SSL_CTX * has not been set, or if both of MOSQ_OPT_SSL_CTX and * MOSQ_OPT_SSL_CTX_WITH_DEFAULTS are set. */ if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk || mosq->tls_use_os_certs){ net__init_tls(); if(!mosq->ssl_ctx){ #if OPENSSL_VERSION_NUMBER < 0x10100000L mosq->ssl_ctx = SSL_CTX_new(SSLv23_client_method()); #else mosq->ssl_ctx = SSL_CTX_new(TLS_client_method()); #endif if(!mosq->ssl_ctx){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to create TLS context."); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } #ifdef SSL_OP_NO_TLSv1_3 if(mosq->tls_psk){ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_TLSv1_3); } #endif if(!mosq->tls_version){ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); #ifdef SSL_OP_NO_TLSv1_3 }else if(!strcmp(mosq->tls_version, "tlsv1.3")){ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); #endif }else if(!strcmp(mosq->tls_version, "tlsv1.2")){ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); }else if(!strcmp(mosq->tls_version, "tlsv1.1")){ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); }else{ log__printf(mosq, MOSQ_LOG_ERR, "Error: Protocol %s not supported.", mosq->tls_version); return MOSQ_ERR_INVAL; } #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* Allow use of DHE ciphers */ SSL_CTX_set_dh_auto(mosq->ssl_ctx, 1); #endif /* Disable compression */ SSL_CTX_set_options(mosq->ssl_ctx, SSL_OP_NO_COMPRESSION); /* Set ALPN */ if(mosq->tls_alpn) { tls_alpn_len = (uint8_t) strnlen(mosq->tls_alpn, 254); tls_alpn_wire[0] = tls_alpn_len; /* first byte is length of string */ memcpy(tls_alpn_wire + 1, mosq->tls_alpn, tls_alpn_len); SSL_CTX_set_alpn_protos(mosq->ssl_ctx, tls_alpn_wire, tls_alpn_len + 1U); } #ifdef SSL_MODE_RELEASE_BUFFERS /* Use even less memory per SSL connection. */ SSL_CTX_set_mode(mosq->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif #if !defined(OPENSSL_NO_ENGINE) if(mosq->tls_engine){ engine = ENGINE_by_id(mosq->tls_engine); if(!engine){ log__printf(mosq, MOSQ_LOG_ERR, "Error loading %s engine\n", mosq->tls_engine); return MOSQ_ERR_TLS; } if(!ENGINE_init(engine)){ log__printf(mosq, MOSQ_LOG_ERR, "Failed engine initialisation\n"); ENGINE_free(engine); return MOSQ_ERR_TLS; } ENGINE_set_default(engine, ENGINE_METHOD_ALL); ENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */ } #endif if(mosq->tls_ciphers){ ret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers); if(ret == 0){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", mosq->tls_ciphers); #if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); #endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_use_os_certs){ ret = net__tls_load_ca(mosq); if(ret != MOSQ_ERR_SUCCESS){ # if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); # endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } if(mosq->tls_cert_reqs == 0){ SSL_CTX_set_verify(mosq->ssl_ctx, SSL_VERIFY_NONE, NULL); }else{ SSL_CTX_set_verify(mosq->ssl_ctx, SSL_VERIFY_PEER, mosquitto__server_certificate_verify); } if(mosq->tls_pw_callback){ SSL_CTX_set_default_passwd_cb(mosq->ssl_ctx, mosq->tls_pw_callback); SSL_CTX_set_default_passwd_cb_userdata(mosq->ssl_ctx, mosq); } if(mosq->tls_certfile){ ret = SSL_CTX_use_certificate_chain_file(mosq->ssl_ctx, mosq->tls_certfile); if(ret != 1){ #ifdef WITH_BROKER log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate, check bridge_certfile \"%s\".", mosq->tls_certfile); #else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile); #endif #if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); #endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } if(mosq->tls_keyfile){ if(mosq->tls_keyform == mosq_k_engine){ #if !defined(OPENSSL_NO_ENGINE) UI_METHOD *ui_method = net__get_ui_method(); if(mosq->tls_engine_kpass_sha1){ if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set engine secret mode sha1"); ENGINE_FINISH(engine); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } if(!ENGINE_ctrl_cmd(engine, ENGINE_PIN, 0, mosq->tls_engine_kpass_sha1, NULL, 0)){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set engine pin"); ENGINE_FINISH(engine); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } ui_method = NULL; } pkey = ENGINE_load_private_key(engine, mosq->tls_keyfile, ui_method, NULL); if(!pkey){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load engine private key file \"%s\".", mosq->tls_keyfile); ENGINE_FINISH(engine); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } if(SSL_CTX_use_PrivateKey(mosq->ssl_ctx, pkey) <= 0){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to use engine private key file \"%s\".", mosq->tls_keyfile); ENGINE_FINISH(engine); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } #endif }else{ ret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM); if(ret != 1){ #ifdef WITH_BROKER log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file, check bridge_keyfile \"%s\".", mosq->tls_keyfile); #else log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile); #endif #if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); #endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } ret = SSL_CTX_check_private_key(mosq->ssl_ctx); if(ret != 1){ log__printf(mosq, MOSQ_LOG_ERR, "Error: Client certificate/key are inconsistent."); #if !defined(OPENSSL_NO_ENGINE) ENGINE_FINISH(engine); #endif net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } } #ifdef FINAL_WITH_TLS_PSK }else if(mosq->tls_psk){ SSL_CTX_set_psk_client_callback(mosq->ssl_ctx, psk_client_callback); if(mosq->tls_ciphers == NULL){ SSL_CTX_set_cipher_list(mosq->ssl_ctx, "PSK"); } #endif } } return MOSQ_ERR_SUCCESS; } #endif int net__socket_connect_step3(struct mosquitto *mosq, const char *host) { #ifdef WITH_TLS BIO *bio; int rc = net__init_ssl_ctx(mosq); if(rc){ net__socket_close(mosq); return rc; } if(mosq->ssl_ctx){ if(mosq->ssl){ SSL_free(mosq->ssl); } mosq->ssl = SSL_new(mosq->ssl_ctx); if(!mosq->ssl){ net__socket_close(mosq); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } SSL_set_ex_data(mosq->ssl, tls_ex_index_mosq, mosq); bio = BIO_new_socket(mosq->sock, BIO_NOCLOSE); if(!bio){ net__socket_close(mosq); net__print_ssl_error(mosq); return MOSQ_ERR_TLS; } SSL_set_bio(mosq->ssl, bio, bio); /* * required for the SNI resolving */ if(SSL_set_tlsext_host_name(mosq->ssl, host) != 1) { net__socket_close(mosq); return MOSQ_ERR_TLS; } if(net__socket_connect_tls(mosq)){ net__socket_close(mosq); return MOSQ_ERR_TLS; } } #else UNUSED(mosq); UNUSED(host); #endif return MOSQ_ERR_SUCCESS; } /* Create a socket and connect it to 'ip' on port 'port'. */ int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking) { int rc, rc2; if(!mosq || !host) return MOSQ_ERR_INVAL; rc = net__try_connect(host, port, &mosq->sock, bind_address, blocking); if(rc > 0) return rc; if(mosq->tcp_nodelay){ int flag = 1; if(setsockopt(mosq->sock, IPPROTO_TCP, TCP_NODELAY, (const void*)&flag, sizeof(int)) != 0){ log__printf(mosq, MOSQ_LOG_WARNING, "Warning: Unable to set TCP_NODELAY."); } } #if defined(WITH_SOCKS) && !defined(WITH_BROKER) if(!mosq->socks5_host) #endif { rc2 = net__socket_connect_step3(mosq, host); if(rc2) return rc2; } return rc; } #ifdef WITH_TLS static int net__handle_ssl(struct mosquitto* mosq, int ret) { int err; err = SSL_get_error(mosq->ssl, ret); if (err == SSL_ERROR_WANT_READ) { ret = -1; errno = EAGAIN; } else if (err == SSL_ERROR_WANT_WRITE) { ret = -1; #ifdef WITH_BROKER mux__add_out(mosq); #else mosq->want_write = true; #endif errno = EAGAIN; } else { net__print_ssl_error(mosq); errno = EPROTO; } ERR_clear_error(); #ifdef WIN32 WSASetLastError(errno); #endif return ret; } #endif ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count) { #ifdef WITH_TLS int ret; #endif assert(mosq); errno = 0; #ifdef WITH_TLS if(mosq->ssl){ ret = SSL_read(mosq->ssl, buf, (int)count); if(ret <= 0){ ret = net__handle_ssl(mosq, ret); } return (ssize_t )ret; }else{ /* Call normal read/recv */ #endif #ifndef WIN32 return read(mosq->sock, buf, count); #else return recv(mosq->sock, buf, count, 0); #endif #ifdef WITH_TLS } #endif } ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count) { #ifdef WITH_TLS int ret; #endif assert(mosq); errno = 0; #ifdef WITH_TLS if(mosq->ssl){ mosq->want_write = false; ret = SSL_write(mosq->ssl, buf, (int)count); if(ret < 0){ ret = net__handle_ssl(mosq, ret); } return (ssize_t )ret; }else{ /* Call normal write/send */ #endif return send(mosq->sock, buf, count, MSG_NOSIGNAL); #ifdef WITH_TLS } #endif } int net__socket_nonblock(mosq_sock_t *sock) { #ifndef WIN32 int opt; /* Set non-blocking */ opt = fcntl(*sock, F_GETFL, 0); if(opt == -1){ COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; return MOSQ_ERR_ERRNO; } if(fcntl(*sock, F_SETFL, opt | O_NONBLOCK) == -1){ /* If either fcntl fails, don't want to allow this client to connect. */ COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; return MOSQ_ERR_ERRNO; } #else unsigned long opt = 1; if(ioctlsocket(*sock, FIONBIO, &opt)){ COMPAT_CLOSE(*sock); *sock = INVALID_SOCKET; return MOSQ_ERR_ERRNO; } #endif return MOSQ_ERR_SUCCESS; } #ifndef WITH_BROKER int net__socketpair(mosq_sock_t *pairR, mosq_sock_t *pairW) { #ifdef WIN32 int family[2] = {AF_INET, AF_INET6}; int i; struct sockaddr_storage ss; struct sockaddr_in *sa = (struct sockaddr_in *)&ss; struct sockaddr_in6 *sa6 = (struct sockaddr_in6 *)&ss; socklen_t ss_len; mosq_sock_t spR, spW; mosq_sock_t listensock; *pairR = INVALID_SOCKET; *pairW = INVALID_SOCKET; for(i=0; i<2; i++){ memset(&ss, 0, sizeof(ss)); if(family[i] == AF_INET){ sa->sin_family = family[i]; sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); sa->sin_port = 0; ss_len = sizeof(struct sockaddr_in); }else if(family[i] == AF_INET6){ sa6->sin6_family = family[i]; sa6->sin6_addr = in6addr_loopback; sa6->sin6_port = 0; ss_len = sizeof(struct sockaddr_in6); }else{ return MOSQ_ERR_INVAL; } listensock = socket(family[i], SOCK_STREAM, IPPROTO_TCP); if(listensock == -1){ continue; } if(bind(listensock, (struct sockaddr *)&ss, ss_len) == -1){ COMPAT_CLOSE(listensock); continue; } if(listen(listensock, 1) == -1){ COMPAT_CLOSE(listensock); continue; } memset(&ss, 0, sizeof(ss)); ss_len = sizeof(ss); if(getsockname(listensock, (struct sockaddr *)&ss, &ss_len) < 0){ COMPAT_CLOSE(listensock); continue; } if(family[i] == AF_INET){ sa->sin_family = family[i]; sa->sin_addr.s_addr = htonl(INADDR_LOOPBACK); ss_len = sizeof(struct sockaddr_in); }else if(family[i] == AF_INET6){ sa6->sin6_family = family[i]; sa6->sin6_addr = in6addr_loopback; ss_len = sizeof(struct sockaddr_in6); } spR = socket(family[i], SOCK_STREAM, IPPROTO_TCP); if(spR == -1){ COMPAT_CLOSE(listensock); continue; } if(net__socket_nonblock(&spR)){ COMPAT_CLOSE(listensock); continue; } if(connect(spR, (struct sockaddr *)&ss, ss_len) < 0){ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){ COMPAT_CLOSE(spR); COMPAT_CLOSE(listensock); continue; } } spW = accept(listensock, NULL, 0); if(spW == -1){ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno != EINPROGRESS && errno != COMPAT_EWOULDBLOCK){ COMPAT_CLOSE(spR); COMPAT_CLOSE(listensock); continue; } } if(net__socket_nonblock(&spW)){ COMPAT_CLOSE(spR); COMPAT_CLOSE(listensock); continue; } COMPAT_CLOSE(listensock); *pairR = spR; *pairW = spW; return MOSQ_ERR_SUCCESS; } return MOSQ_ERR_UNKNOWN; #else int sv[2]; *pairR = INVALID_SOCKET; *pairW = INVALID_SOCKET; if(socketpair(AF_UNIX, SOCK_STREAM, 0, sv) == -1){ return MOSQ_ERR_ERRNO; } if(net__socket_nonblock(&sv[0])){ COMPAT_CLOSE(sv[1]); return MOSQ_ERR_ERRNO; } if(net__socket_nonblock(&sv[1])){ COMPAT_CLOSE(sv[0]); return MOSQ_ERR_ERRNO; } *pairR = sv[0]; *pairW = sv[1]; return MOSQ_ERR_SUCCESS; #endif } #endif #ifndef WITH_BROKER void *mosquitto_ssl_get(struct mosquitto *mosq) { #ifdef WITH_TLS return mosq->ssl; #else UNUSED(mosq); return NULL; #endif } #endif mosquitto-2.0.18/lib/net_mosq.h000066400000000000000000000054621450213760600164250ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef NET_MOSQ_H #define NET_MOSQ_H #ifndef WIN32 # include # include #else # include # ifndef _SSIZE_T_DEFINED typedef SSIZE_T ssize_t; # define _SSIZE_T_DEFINED # endif #endif #include "mosquitto_internal.h" #include "mosquitto.h" #ifdef WIN32 # define COMPAT_CLOSE(a) closesocket(a) # define COMPAT_ECONNRESET WSAECONNRESET # define COMPAT_EINTR WSAEINTR # define COMPAT_EWOULDBLOCK WSAEWOULDBLOCK # ifndef EINPROGRESS # define EINPROGRESS WSAEINPROGRESS # endif #else # define COMPAT_CLOSE(a) close(a) # define COMPAT_ECONNRESET ECONNRESET # define COMPAT_EINTR EINTR # define COMPAT_EWOULDBLOCK EWOULDBLOCK #endif /* For when not using winsock libraries. */ #ifndef INVALID_SOCKET #define INVALID_SOCKET -1 #endif #ifndef MSG_NOSIGNAL # define MSG_NOSIGNAL 0 #endif /* Macros for accessing the MSB and LSB of a uint16_t */ #define MOSQ_MSB(A) (uint8_t)((A & 0xFF00) >> 8) #define MOSQ_LSB(A) (uint8_t)(A & 0x00FF) int net__init(void); void net__cleanup(void); #ifdef WITH_TLS void net__init_tls(void); #endif int net__socket_connect(struct mosquitto *mosq, const char *host, uint16_t port, const char *bind_address, bool blocking); int net__socket_close(struct mosquitto *mosq); int net__try_connect(const char *host, uint16_t port, mosq_sock_t *sock, const char *bind_address, bool blocking); int net__try_connect_step1(struct mosquitto *mosq, const char *host); int net__try_connect_step2(struct mosquitto *mosq, uint16_t port, mosq_sock_t *sock); int net__socket_connect_step3(struct mosquitto *mosq, const char *host); int net__socket_nonblock(mosq_sock_t *sock); int net__socketpair(mosq_sock_t *sp1, mosq_sock_t *sp2); ssize_t net__read(struct mosquitto *mosq, void *buf, size_t count); ssize_t net__write(struct mosquitto *mosq, const void *buf, size_t count); #ifdef WITH_TLS void net__print_ssl_error(struct mosquitto *mosq); int net__socket_apply_tls(struct mosquitto *mosq); int net__socket_connect_tls(struct mosquitto *mosq); int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg); UI_METHOD *net__get_ui_method(void); #define ENGINE_FINISH(e) if(e) ENGINE_finish(e) #define ENGINE_SECRET_MODE "SECRET_MODE" #define ENGINE_SECRET_MODE_SHA 0x1000 #define ENGINE_PIN "PIN" #endif #endif mosquitto-2.0.18/lib/net_mosq_ocsp.c000066400000000000000000000120471450213760600174410ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light Copyright (c) 2017 Bayerische Motoren Werke Aktiengesellschaft (BMW AG), Dr. Lars Voelker All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Dr. Lars Voelker, BMW AG */ /* COPYRIGHT AND PERMISSION NOTICE of curl on which the ocsp code is based: Copyright (c) 1996 - 2016, Daniel Stenberg, , and many contributors, see the THANKS file. All rights reserved. Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of a copyright holder shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization of the copyright holder. */ #include "config.h" #ifdef WITH_TLS #include #include #include #include #include #include #include int mosquitto__verify_ocsp_status_cb(SSL * ssl, void *arg) { struct mosquitto *mosq = (struct mosquitto *)arg; int ocsp_status, result2, i; unsigned char *p; const unsigned char *cp; OCSP_RESPONSE *rsp = NULL; OCSP_BASICRESP *br = NULL; X509_STORE *st = NULL; STACK_OF(X509) *ch = NULL; long len; UNUSED(ssl); len = SSL_get_tlsext_status_ocsp_resp(mosq->ssl, &p); log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL_get_tlsext_status_ocsp_resp returned %ld bytes", len); /* the following functions expect a const pointer */ cp = (const unsigned char *)p; if (!cp || len <= 0) { log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: no response"); goto end; } rsp = d2i_OCSP_RESPONSE(NULL, &cp, len); if (rsp==NULL) { log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response"); goto end; } ocsp_status = OCSP_response_status(rsp); if(ocsp_status != OCSP_RESPONSE_STATUS_SUCCESSFUL) { log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid status: %s (%d)", OCSP_response_status_str(ocsp_status), ocsp_status); goto end; } br = OCSP_response_get1_basic(rsp); if (!br) { log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: invalid response"); goto end; } ch = SSL_get_peer_cert_chain(mosq->ssl); if (sk_X509_num(ch) <= 0) { log__printf(mosq, MOSQ_LOG_ERR, "OCSP: we did not receive certificates of the server (num: %d)", sk_X509_num(ch)); goto end; } st = SSL_CTX_get_cert_store(mosq->ssl_ctx); /* Note: * Other checkers often fix problems in OpenSSL before 1.0.2a (e.g. libcurl). * For all currently supported versions of the OpenSSL project, this is not needed anymore. */ if ((result2=OCSP_basic_verify(br, ch, st, 0)) <= 0) { log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: response verification failed (error: %d)", result2); goto end; } for(i = 0; i < OCSP_resp_count(br); i++) { int cert_status, crl_reason; OCSP_SINGLERESP *single = NULL; ASN1_GENERALIZEDTIME *rev, *thisupd, *nextupd; single = OCSP_resp_get0(br, i); if(!single) continue; cert_status = OCSP_single_get0_status(single, &crl_reason, &rev, &thisupd, &nextupd); log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate status: %s (%d)", OCSP_cert_status_str(cert_status), cert_status); switch(cert_status) { case V_OCSP_CERTSTATUS_GOOD: /* Note: A OCSP stapling result will be accepted up to 5 minutes after it expired! */ if(!OCSP_check_validity(thisupd, nextupd, 300L, -1L)) { log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: OCSP response has expired"); goto end; } break; case V_OCSP_CERTSTATUS_REVOKED: log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation reason: %s (%d)", OCSP_crl_reason_str(crl_reason), crl_reason); goto end; case V_OCSP_CERTSTATUS_UNKNOWN: goto end; default: log__printf(mosq, MOSQ_LOG_DEBUG, "OCSP: SSL certificate revocation status unknown"); goto end; } } if (br!=NULL) OCSP_BASICRESP_free(br); if (rsp!=NULL) OCSP_RESPONSE_free(rsp); return 1; /* OK */ end: if (br!=NULL) OCSP_BASICRESP_free(br); if (rsp!=NULL) OCSP_RESPONSE_free(rsp); return 0; /* Not OK */ } #endif mosquitto-2.0.18/lib/options.c000066400000000000000000000300551450213760600162620ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifndef WIN32 # include #endif #include #ifdef WITH_TLS # ifdef WIN32 # include # endif # include #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "memory_mosq.h" #include "misc_mosq.h" #include "mqtt_protocol.h" #include "util_mosq.h" #include "will_mosq.h" int mosquitto_will_set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain) { return mosquitto_will_set_v5(mosq, topic, payloadlen, payload, qos, retain, NULL); } int mosquitto_will_set_v5(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties) { int rc; if(!mosq) return MOSQ_ERR_INVAL; if(properties){ rc = mosquitto_property_check_all(CMD_WILL, properties); if(rc) return rc; } return will__set(mosq, topic, payloadlen, payload, qos, retain, properties); } int mosquitto_will_clear(struct mosquitto *mosq) { if(!mosq) return MOSQ_ERR_INVAL; return will__clear(mosq); } int mosquitto_username_pw_set(struct mosquitto *mosq, const char *username, const char *password) { size_t slen; if(!mosq) return MOSQ_ERR_INVAL; if(mosq->protocol == mosq_p_mqtt311 || mosq->protocol == mosq_p_mqtt31){ if(password != NULL && username == NULL){ return MOSQ_ERR_INVAL; } } mosquitto__free(mosq->username); mosq->username = NULL; mosquitto__free(mosq->password); mosq->password = NULL; if(username){ slen = strlen(username); if(slen > UINT16_MAX){ return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)slen)){ return MOSQ_ERR_MALFORMED_UTF8; } mosq->username = mosquitto__strdup(username); if(!mosq->username) return MOSQ_ERR_NOMEM; } if(password){ mosq->password = mosquitto__strdup(password); if(!mosq->password){ mosquitto__free(mosq->username); mosq->username = NULL; return MOSQ_ERR_NOMEM; } } return MOSQ_ERR_SUCCESS; } int mosquitto_reconnect_delay_set(struct mosquitto *mosq, unsigned int reconnect_delay, unsigned int reconnect_delay_max, bool reconnect_exponential_backoff) { if(!mosq) return MOSQ_ERR_INVAL; if(reconnect_delay == 0) reconnect_delay = 1; mosq->reconnect_delay = reconnect_delay; mosq->reconnect_delay_max = reconnect_delay_max; mosq->reconnect_exponential_backoff = reconnect_exponential_backoff; return MOSQ_ERR_SUCCESS; } int 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)) { #ifdef WITH_TLS FILE *fptr; if(!mosq || (!cafile && !capath) || (certfile && !keyfile) || (!certfile && keyfile)) return MOSQ_ERR_INVAL; mosquitto__free(mosq->tls_cafile); mosq->tls_cafile = NULL; if(cafile){ fptr = mosquitto__fopen(cafile, "rt", false); if(fptr){ fclose(fptr); }else{ return MOSQ_ERR_INVAL; } mosq->tls_cafile = mosquitto__strdup(cafile); if(!mosq->tls_cafile){ return MOSQ_ERR_NOMEM; } } mosquitto__free(mosq->tls_capath); mosq->tls_capath = NULL; if(capath){ mosq->tls_capath = mosquitto__strdup(capath); if(!mosq->tls_capath){ return MOSQ_ERR_NOMEM; } } mosquitto__free(mosq->tls_certfile); mosq->tls_certfile = NULL; if(certfile){ fptr = mosquitto__fopen(certfile, "rt", false); if(fptr){ fclose(fptr); }else{ mosquitto__free(mosq->tls_cafile); mosq->tls_cafile = NULL; mosquitto__free(mosq->tls_capath); mosq->tls_capath = NULL; return MOSQ_ERR_INVAL; } mosq->tls_certfile = mosquitto__strdup(certfile); if(!mosq->tls_certfile){ return MOSQ_ERR_NOMEM; } } mosquitto__free(mosq->tls_keyfile); mosq->tls_keyfile = NULL; if(keyfile){ if(mosq->tls_keyform == mosq_k_pem){ fptr = mosquitto__fopen(keyfile, "rt", false); if(fptr){ fclose(fptr); }else{ mosquitto__free(mosq->tls_cafile); mosq->tls_cafile = NULL; mosquitto__free(mosq->tls_capath); mosq->tls_capath = NULL; mosquitto__free(mosq->tls_certfile); mosq->tls_certfile = NULL; return MOSQ_ERR_INVAL; } } mosq->tls_keyfile = mosquitto__strdup(keyfile); if(!mosq->tls_keyfile){ return MOSQ_ERR_NOMEM; } } mosq->tls_pw_callback = pw_callback; return MOSQ_ERR_SUCCESS; #else UNUSED(mosq); UNUSED(cafile); UNUSED(capath); UNUSED(certfile); UNUSED(keyfile); UNUSED(pw_callback); return MOSQ_ERR_NOT_SUPPORTED; #endif } int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs, const char *tls_version, const char *ciphers) { #ifdef WITH_TLS if(!mosq) return MOSQ_ERR_INVAL; mosq->tls_cert_reqs = cert_reqs; if(tls_version){ if(!strcasecmp(tls_version, "tlsv1.3") || !strcasecmp(tls_version, "tlsv1.2") || !strcasecmp(tls_version, "tlsv1.1")){ mosquitto__free(mosq->tls_version); mosq->tls_version = mosquitto__strdup(tls_version); if(!mosq->tls_version) return MOSQ_ERR_NOMEM; }else{ return MOSQ_ERR_INVAL; } }else{ mosquitto__free(mosq->tls_version); mosq->tls_version = mosquitto__strdup("tlsv1.2"); if(!mosq->tls_version) return MOSQ_ERR_NOMEM; } if(ciphers){ mosquitto__free(mosq->tls_ciphers); mosq->tls_ciphers = mosquitto__strdup(ciphers); if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM; }else{ mosquitto__free(mosq->tls_ciphers); mosq->tls_ciphers = NULL; } return MOSQ_ERR_SUCCESS; #else UNUSED(mosq); UNUSED(cert_reqs); UNUSED(tls_version); UNUSED(ciphers); return MOSQ_ERR_NOT_SUPPORTED; #endif } int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value) { #ifdef WITH_TLS if(!mosq) return MOSQ_ERR_INVAL; mosq->tls_insecure = value; return MOSQ_ERR_SUCCESS; #else UNUSED(mosq); UNUSED(value); return MOSQ_ERR_NOT_SUPPORTED; #endif } int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, const char *value) { #ifdef WITH_TLS ENGINE *eng; char *str; #endif if(!mosq) return MOSQ_ERR_INVAL; switch(option){ case MOSQ_OPT_TLS_ENGINE: #if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) mosquitto__free(mosq->tls_engine); if(value){ #if OPENSSL_VERSION_NUMBER >= 0x10100000L /* The "Dynamic" OpenSSL engine is not initialized by default but is required by ENGINE_by_id() to find dynamically loadable engines */ OPENSSL_init_crypto(OPENSSL_INIT_ENGINE_DYNAMIC, NULL); #endif eng = ENGINE_by_id(value); if(!eng){ return MOSQ_ERR_INVAL; } ENGINE_free(eng); /* release the structural reference from ENGINE_by_id() */ mosq->tls_engine = mosquitto__strdup(value); if(!mosq->tls_engine){ return MOSQ_ERR_NOMEM; } } return MOSQ_ERR_SUCCESS; #else return MOSQ_ERR_NOT_SUPPORTED; #endif break; case MOSQ_OPT_TLS_KEYFORM: #ifdef WITH_TLS if(!value) return MOSQ_ERR_INVAL; if(!strcasecmp(value, "pem")){ mosq->tls_keyform = mosq_k_pem; }else if (!strcasecmp(value, "engine")){ mosq->tls_keyform = mosq_k_engine; }else{ return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; #else return MOSQ_ERR_NOT_SUPPORTED; #endif break; case MOSQ_OPT_TLS_ENGINE_KPASS_SHA1: #ifdef WITH_TLS if(mosquitto__hex2bin_sha1(value, (unsigned char**)&str) != MOSQ_ERR_SUCCESS){ return MOSQ_ERR_INVAL; } mosq->tls_engine_kpass_sha1 = str; return MOSQ_ERR_SUCCESS; #else return MOSQ_ERR_NOT_SUPPORTED; #endif break; case MOSQ_OPT_TLS_ALPN: #ifdef WITH_TLS mosq->tls_alpn = mosquitto__strdup(value); if(!mosq->tls_alpn){ return MOSQ_ERR_NOMEM; } return MOSQ_ERR_SUCCESS; #else return MOSQ_ERR_NOT_SUPPORTED; #endif break; case MOSQ_OPT_BIND_ADDRESS: mosquitto__free(mosq->bind_address); if(value){ mosq->bind_address = mosquitto__strdup(value); if(mosq->bind_address){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOMEM; } }else{ return MOSQ_ERR_SUCCESS; } default: return MOSQ_ERR_INVAL; } } int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers) { #ifdef FINAL_WITH_TLS_PSK if(!mosq || !psk || !identity) return MOSQ_ERR_INVAL; /* Check for hex only digits */ if(strspn(psk, "0123456789abcdefABCDEF") < strlen(psk)){ return MOSQ_ERR_INVAL; } mosq->tls_psk = mosquitto__strdup(psk); if(!mosq->tls_psk) return MOSQ_ERR_NOMEM; mosq->tls_psk_identity = mosquitto__strdup(identity); if(!mosq->tls_psk_identity){ mosquitto__free(mosq->tls_psk); return MOSQ_ERR_NOMEM; } if(ciphers){ mosq->tls_ciphers = mosquitto__strdup(ciphers); if(!mosq->tls_ciphers) return MOSQ_ERR_NOMEM; }else{ mosq->tls_ciphers = NULL; } return MOSQ_ERR_SUCCESS; #else UNUSED(mosq); UNUSED(psk); UNUSED(identity); UNUSED(ciphers); return MOSQ_ERR_NOT_SUPPORTED; #endif } int mosquitto_opts_set(struct mosquitto *mosq, enum mosq_opt_t option, void *value) { int ival; if(!mosq) return MOSQ_ERR_INVAL; switch(option){ case MOSQ_OPT_PROTOCOL_VERSION: if(value == NULL){ return MOSQ_ERR_INVAL; } ival = *((int *)value); return mosquitto_int_option(mosq, option, ival); case MOSQ_OPT_SSL_CTX: return mosquitto_void_option(mosq, option, value); default: return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t option, int value) { if(!mosq) return MOSQ_ERR_INVAL; switch(option){ case MOSQ_OPT_PROTOCOL_VERSION: if(value == MQTT_PROTOCOL_V31){ mosq->protocol = mosq_p_mqtt31; }else if(value == MQTT_PROTOCOL_V311){ mosq->protocol = mosq_p_mqtt311; }else if(value == MQTT_PROTOCOL_V5){ mosq->protocol = mosq_p_mqtt5; }else{ return MOSQ_ERR_INVAL; } break; case MOSQ_OPT_RECEIVE_MAXIMUM: if(value < 0 || value > UINT16_MAX){ return MOSQ_ERR_INVAL; } if(value == 0){ mosq->msgs_in.inflight_maximum = UINT16_MAX; }else{ mosq->msgs_in.inflight_maximum = (uint16_t)value; } break; case MOSQ_OPT_SEND_MAXIMUM: if(value < 0 || value > UINT16_MAX){ return MOSQ_ERR_INVAL; } if(value == 0){ mosq->msgs_out.inflight_maximum = UINT16_MAX; }else{ mosq->msgs_out.inflight_maximum = (uint16_t)value; } break; case MOSQ_OPT_SSL_CTX_WITH_DEFAULTS: #if defined(WITH_TLS) && OPENSSL_VERSION_NUMBER >= 0x10100000L if(value){ mosq->ssl_ctx_defaults = true; }else{ mosq->ssl_ctx_defaults = false; } break; #else return MOSQ_ERR_NOT_SUPPORTED; #endif case MOSQ_OPT_TLS_USE_OS_CERTS: #ifdef WITH_TLS if(value){ mosq->tls_use_os_certs = true; }else{ mosq->tls_use_os_certs = false; } break; #else return MOSQ_ERR_NOT_SUPPORTED; #endif case MOSQ_OPT_TLS_OCSP_REQUIRED: #ifdef WITH_TLS mosq->tls_ocsp_required = (bool)value; #else return MOSQ_ERR_NOT_SUPPORTED; #endif break; case MOSQ_OPT_TCP_NODELAY: mosq->tcp_nodelay = (bool)value; break; default: return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } int mosquitto_void_option(struct mosquitto *mosq, enum mosq_opt_t option, void *value) { if(!mosq) return MOSQ_ERR_INVAL; switch(option){ case MOSQ_OPT_SSL_CTX: #ifdef WITH_TLS mosq->user_ssl_ctx = (SSL_CTX *)value; if(mosq->user_ssl_ctx){ #if (OPENSSL_VERSION_NUMBER >= 0x10100000L) SSL_CTX_up_ref(mosq->user_ssl_ctx); #else CRYPTO_add(&(mosq->user_ssl_ctx)->references, 1, CRYPTO_LOCK_SSL_CTX); #endif } break; #else return MOSQ_ERR_NOT_SUPPORTED; #endif default: return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } void mosquitto_user_data_set(struct mosquitto *mosq, void *userdata) { if(mosq){ mosq->userdata = userdata; } } void *mosquitto_userdata(struct mosquitto *mosq) { return mosq->userdata; } mosquitto-2.0.18/lib/packet_datatypes.c000066400000000000000000000135221450213760600201140ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" # ifdef WITH_WEBSOCKETS # include # endif #else # include "read_handle.h" #endif #include "memory_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #ifdef WITH_BROKER # include "sys_tree.h" #else # define G_BYTES_RECEIVED_INC(A) # define G_BYTES_SENT_INC(A) # define G_MSGS_SENT_INC(A) # define G_PUB_MSGS_SENT_INC(A) #endif int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte) { assert(packet); if(packet->pos+1 > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; *byte = packet->payload[packet->pos]; packet->pos++; return MOSQ_ERR_SUCCESS; } void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte) { assert(packet); assert(packet->pos+1 <= packet->packet_length); packet->payload[packet->pos] = byte; packet->pos++; } int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count) { assert(packet); if(packet->pos+count > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; memcpy(bytes, &(packet->payload[packet->pos]), count); packet->pos += count; return MOSQ_ERR_SUCCESS; } void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count) { assert(packet); assert(packet->pos+count <= packet->packet_length); memcpy(&(packet->payload[packet->pos]), bytes, count); packet->pos += count; } int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, uint16_t *length) { uint16_t slen; int rc; assert(packet); rc = packet__read_uint16(packet, &slen); if(rc) return rc; if(slen == 0){ *data = NULL; *length = 0; return MOSQ_ERR_SUCCESS; } if(packet->pos+slen > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; *data = mosquitto__malloc(slen+1U); if(*data){ memcpy(*data, &(packet->payload[packet->pos]), slen); ((uint8_t *)(*data))[slen] = '\0'; packet->pos += slen; }else{ return MOSQ_ERR_NOMEM; } *length = slen; return MOSQ_ERR_SUCCESS; } int packet__read_string(struct mosquitto__packet *packet, char **str, uint16_t *length) { int rc; rc = packet__read_binary(packet, (uint8_t **)str, length); if(rc) return rc; if(*length == 0) return MOSQ_ERR_SUCCESS; if(mosquitto_validate_utf8(*str, *length)){ mosquitto__free(*str); *str = NULL; *length = 0; return MOSQ_ERR_MALFORMED_UTF8; } return MOSQ_ERR_SUCCESS; } void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length) { assert(packet); packet__write_uint16(packet, length); packet__write_bytes(packet, str, length); } int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word) { uint8_t msb, lsb; assert(packet); if(packet->pos+2 > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; msb = packet->payload[packet->pos]; packet->pos++; lsb = packet->payload[packet->pos]; packet->pos++; *word = (uint16_t)((msb<<8) + lsb); return MOSQ_ERR_SUCCESS; } void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word) { packet__write_byte(packet, MOSQ_MSB(word)); packet__write_byte(packet, MOSQ_LSB(word)); } int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word) { uint32_t val = 0; int i; assert(packet); if(packet->pos+4 > packet->remaining_length) return MOSQ_ERR_MALFORMED_PACKET; for(i=0; i<4; i++){ val = (val << 8) + packet->payload[packet->pos]; packet->pos++; } *word = val; return MOSQ_ERR_SUCCESS; } void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word) { packet__write_byte(packet, (uint8_t)((word & 0xFF000000) >> 24)); packet__write_byte(packet, (uint8_t)((word & 0x00FF0000) >> 16)); packet__write_byte(packet, (uint8_t)((word & 0x0000FF00) >> 8)); packet__write_byte(packet, (uint8_t)((word & 0x000000FF))); } int packet__read_varint(struct mosquitto__packet *packet, uint32_t *word, uint8_t *bytes) { int i; uint8_t byte; unsigned int remaining_mult = 1; uint32_t lword = 0; uint8_t lbytes = 0; for(i=0; i<4; i++){ if(packet->pos < packet->remaining_length){ lbytes++; byte = packet->payload[packet->pos]; lword += (byte & 127) * remaining_mult; remaining_mult *= 128; packet->pos++; if((byte & 128) == 0){ if(lbytes > 1 && byte == 0){ /* Catch overlong encodings */ return MOSQ_ERR_MALFORMED_PACKET; }else{ *word = lword; if(bytes) (*bytes) = lbytes; return MOSQ_ERR_SUCCESS; } } }else{ return MOSQ_ERR_MALFORMED_PACKET; } } return MOSQ_ERR_MALFORMED_PACKET; } int packet__write_varint(struct mosquitto__packet *packet, uint32_t word) { uint8_t byte; int count = 0; do{ byte = (uint8_t)(word % 128); word = word / 128; /* If there are more digits to encode, set the top bit of this digit */ if(word > 0){ byte = byte | 0x80; } packet__write_byte(packet, byte); count++; }while(word > 0 && count < 5); if(count == 5){ return MOSQ_ERR_MALFORMED_PACKET; } return MOSQ_ERR_SUCCESS; } unsigned int packet__varint_bytes(uint32_t word) { if(word < 128){ return 1; }else if(word < 16384){ return 2; }else if(word < 2097152){ return 3; }else if(word < 268435456){ return 4; }else{ return 5; } } mosquitto-2.0.18/lib/packet_mosq.c000066400000000000000000000367741450213760600171130ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" # ifdef WITH_WEBSOCKETS # include # endif #else # include "read_handle.h" #endif #include "memory_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "util_mosq.h" #ifdef WITH_BROKER # include "sys_tree.h" # include "send_mosq.h" #else # define G_BYTES_RECEIVED_INC(A) # define G_BYTES_SENT_INC(A) # define G_MSGS_SENT_INC(A) # define G_PUB_MSGS_SENT_INC(A) #endif int packet__alloc(struct mosquitto__packet *packet) { uint8_t remaining_bytes[5], byte; uint32_t remaining_length; int i; assert(packet); remaining_length = packet->remaining_length; packet->payload = NULL; packet->remaining_count = 0; do{ byte = remaining_length % 128; remaining_length = remaining_length / 128; /* If there are more digits to encode, set the top bit of this digit */ if(remaining_length > 0){ byte = byte | 0x80; } remaining_bytes[packet->remaining_count] = byte; packet->remaining_count++; }while(remaining_length > 0 && packet->remaining_count < 5); if(packet->remaining_count == 5) return MOSQ_ERR_PAYLOAD_SIZE; packet->packet_length = packet->remaining_length + 1 + (uint8_t)packet->remaining_count; #ifdef WITH_WEBSOCKETS packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length + LWS_PRE); #else packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); #endif if(!packet->payload) return MOSQ_ERR_NOMEM; packet->payload[0] = packet->command; for(i=0; iremaining_count; i++){ packet->payload[i+1] = remaining_bytes[i]; } packet->pos = 1U + (uint8_t)packet->remaining_count; return MOSQ_ERR_SUCCESS; } void packet__cleanup(struct mosquitto__packet *packet) { if(!packet) return; /* Free data and reset values */ packet->command = 0; packet->remaining_count = 0; packet->remaining_mult = 1; packet->remaining_length = 0; mosquitto__free(packet->payload); packet->payload = NULL; packet->to_process = 0; packet->pos = 0; } void packet__cleanup_all_no_locks(struct mosquitto *mosq) { struct mosquitto__packet *packet; /* Out packet cleanup */ if(mosq->out_packet && !mosq->current_out_packet){ mosq->current_out_packet = mosq->out_packet; mosq->out_packet = mosq->out_packet->next; } while(mosq->current_out_packet){ packet = mosq->current_out_packet; /* Free data and reset values */ mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ mosq->out_packet = mosq->out_packet->next; } packet__cleanup(packet); mosquitto__free(packet); } mosq->out_packet_count = 0; packet__cleanup(&mosq->in_packet); } void packet__cleanup_all(struct mosquitto *mosq) { pthread_mutex_lock(&mosq->current_out_packet_mutex); pthread_mutex_lock(&mosq->out_packet_mutex); packet__cleanup_all_no_locks(mosq); pthread_mutex_unlock(&mosq->out_packet_mutex); pthread_mutex_unlock(&mosq->current_out_packet_mutex); } int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet) { #ifndef WITH_BROKER char sockpair_data = 0; #endif assert(mosq); assert(packet); packet->pos = 0; packet->to_process = packet->packet_length; packet->next = NULL; pthread_mutex_lock(&mosq->out_packet_mutex); #ifdef WITH_BROKER if(db.config->max_queued_messages > 0 && mosq->out_packet_count >= db.config->max_queued_messages){ mosquitto__free(packet); if(mosq->is_dropping == false){ mosq->is_dropping = true; log__printf(NULL, MOSQ_LOG_NOTICE, "Outgoing messages are being dropped for client %s.", mosq->id); } G_MSGS_DROPPED_INC(); return MOSQ_ERR_SUCCESS; } #endif if(mosq->out_packet){ mosq->out_packet_last->next = packet; }else{ mosq->out_packet = packet; } mosq->out_packet_last = packet; mosq->out_packet_count++; pthread_mutex_unlock(&mosq->out_packet_mutex); #ifdef WITH_BROKER # ifdef WITH_WEBSOCKETS if(mosq->wsi){ lws_callback_on_writable(mosq->wsi); return MOSQ_ERR_SUCCESS; }else{ return packet__write(mosq); } # else return packet__write(mosq); # endif #else /* Write a single byte to sockpairW (connected to sockpairR) to break out * of select() if in threaded mode. */ if(mosq->sockpairW != INVALID_SOCKET){ #ifndef WIN32 if(write(mosq->sockpairW, &sockpair_data, 1)){ } #else send(mosq->sockpairW, &sockpair_data, 1, 0); #endif } if(mosq->in_callback == false && mosq->threaded == mosq_ts_none){ return packet__write(mosq); }else{ return MOSQ_ERR_SUCCESS; } #endif } int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length) { uint32_t len; if(mosq->maximum_packet_size == 0) return MOSQ_ERR_SUCCESS; len = remaining_length + packet__varint_bytes(remaining_length); if(len > mosq->maximum_packet_size){ return MOSQ_ERR_OVERSIZE_PACKET; }else{ return MOSQ_ERR_SUCCESS; } } int packet__write(struct mosquitto *mosq) { ssize_t write_length; struct mosquitto__packet *packet; enum mosquitto_client_state state; if(!mosq) return MOSQ_ERR_INVAL; if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; pthread_mutex_lock(&mosq->current_out_packet_mutex); pthread_mutex_lock(&mosq->out_packet_mutex); if(mosq->out_packet && !mosq->current_out_packet){ mosq->current_out_packet = mosq->out_packet; mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } mosq->out_packet_count--; } pthread_mutex_unlock(&mosq->out_packet_mutex); #ifdef WITH_BROKER if(mosq->current_out_packet){ mux__add_out(mosq); } #endif state = mosquitto__get_state(mosq); if(state == mosq_cs_connect_pending){ pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; } while(mosq->current_out_packet){ packet = mosq->current_out_packet; while(packet->to_process > 0){ write_length = net__write(mosq, &(packet->payload[packet->pos]), packet->to_process); if(write_length > 0){ G_BYTES_SENT_INC(write_length); packet->to_process -= (uint32_t)write_length; packet->pos += (uint32_t)write_length; }else{ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK #ifdef WIN32 || errno == WSAENOTCONN #endif ){ pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; }else{ pthread_mutex_unlock(&mosq->current_out_packet_mutex); switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; case COMPAT_EINTR: return MOSQ_ERR_SUCCESS; case EPROTO: return MOSQ_ERR_TLS; default: return MOSQ_ERR_ERRNO; } } } } G_MSGS_SENT_INC(1); if(((packet->command)&0xF6) == CMD_PUBLISH){ G_PUB_MSGS_SENT_INC(1); #ifndef WITH_BROKER pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_publish){ /* This is a QoS=0 message */ mosq->in_callback = true; mosq->on_publish(mosq, mosq->userdata, packet->mid); mosq->in_callback = false; } if(mosq->on_publish_v5){ /* This is a QoS=0 message */ mosq->in_callback = true; mosq->on_publish_v5(mosq, mosq->userdata, packet->mid, 0, NULL); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); }else if(((packet->command)&0xF0) == CMD_DISCONNECT){ do_client_disconnect(mosq, MOSQ_ERR_SUCCESS, NULL); packet__cleanup(packet); mosquitto__free(packet); return MOSQ_ERR_SUCCESS; #endif }else if(((packet->command)&0xF0) == CMD_PUBLISH){ G_PUB_MSGS_SENT_INC(1); } /* Free data and reset values */ pthread_mutex_lock(&mosq->out_packet_mutex); mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } mosq->out_packet_count--; } pthread_mutex_unlock(&mosq->out_packet_mutex); packet__cleanup(packet); mosquitto__free(packet); #ifdef WITH_BROKER mosq->next_msg_out = db.now_s + mosq->keepalive; #else pthread_mutex_lock(&mosq->msgtime_mutex); mosq->next_msg_out = mosquitto_time() + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); #endif } #ifdef WITH_BROKER if (mosq->current_out_packet == NULL) { mux__remove_out(mosq); } #endif pthread_mutex_unlock(&mosq->current_out_packet_mutex); return MOSQ_ERR_SUCCESS; } int packet__read(struct mosquitto *mosq) { uint8_t byte; ssize_t read_length; int rc = 0; enum mosquitto_client_state state; if(!mosq){ return MOSQ_ERR_INVAL; } if(mosq->sock == INVALID_SOCKET){ return MOSQ_ERR_NO_CONN; } state = mosquitto__get_state(mosq); if(state == mosq_cs_connect_pending){ return MOSQ_ERR_SUCCESS; } /* This gets called if pselect() indicates that there is network data * available - ie. at least one byte. What we do depends on what data we * already have. * If we've not got a command, attempt to read one and save it. This should * always work because it's only a single byte. * Then try to read the remaining length. This may fail because it is may * be more than one byte - will need to save data pending next read if it * does fail. * Then try to read the remaining payload, where 'payload' here means the * combined variable header and actual payload. This is the most likely to * fail due to longer length, so save current data and current position. * After all data is read, send to mosquitto__handle_packet() to deal with. * Finally, free the memory and reset everything to starting conditions. */ if(!mosq->in_packet.command){ read_length = net__read(mosq, &byte, 1); if(read_length == 1){ mosq->in_packet.command = byte; #ifdef WITH_BROKER G_BYTES_RECEIVED_INC(1); /* Clients must send CONNECT as their first command. */ if(!(mosq->bridge) && state == mosq_cs_new && (byte&0xF0) != CMD_CONNECT){ return MOSQ_ERR_PROTOCOL; } #endif }else{ if(read_length == 0){ return MOSQ_ERR_CONN_LOST; /* EOF */ } #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; case COMPAT_EINTR: return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } } } } /* remaining_count is the number of bytes that the remaining_length * parameter occupied in this incoming packet. We don't use it here as such * (it is used when allocating an outgoing packet), but we must be able to * determine whether all of the remaining_length parameter has been read. * remaining_count has three states here: * 0 means that we haven't read any remaining_length bytes * <0 means we have read some remaining_length bytes but haven't finished * >0 means we have finished reading the remaining_length bytes. */ if(mosq->in_packet.remaining_count <= 0){ do{ read_length = net__read(mosq, &byte, 1); if(read_length == 1){ mosq->in_packet.remaining_count--; /* Max 4 bytes length for remaining length as defined by protocol. * Anything more likely means a broken/malicious client. */ if(mosq->in_packet.remaining_count < -4){ return MOSQ_ERR_MALFORMED_PACKET; } G_BYTES_RECEIVED_INC(1); mosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult; mosq->in_packet.remaining_mult *= 128; }else{ if(read_length == 0){ return MOSQ_ERR_CONN_LOST; /* EOF */ } #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; case COMPAT_EINTR: return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } } } }while((byte & 128) != 0); /* We have finished reading remaining_length, so make remaining_count * positive. */ mosq->in_packet.remaining_count = (int8_t)(mosq->in_packet.remaining_count * -1); #ifdef WITH_BROKER switch(mosq->in_packet.command & 0xF0){ case CMD_CONNECT: if(mosq->in_packet.remaining_length > 100000){ /* Arbitrary limit, make configurable */ return MOSQ_ERR_MALFORMED_PACKET; } break; case CMD_PUBACK: case CMD_PUBREC: case CMD_PUBREL: case CMD_PUBCOMP: case CMD_UNSUBACK: if(mosq->protocol != mosq_p_mqtt5 && mosq->in_packet.remaining_length != 2){ return MOSQ_ERR_MALFORMED_PACKET; } break; case CMD_PINGREQ: case CMD_PINGRESP: if(mosq->in_packet.remaining_length != 0){ return MOSQ_ERR_MALFORMED_PACKET; } break; case CMD_DISCONNECT: if(mosq->protocol != mosq_p_mqtt5 && mosq->in_packet.remaining_length != 0){ return MOSQ_ERR_MALFORMED_PACKET; } break; } if(db.config->max_packet_size > 0 && mosq->in_packet.remaining_length+1 > db.config->max_packet_size){ if(mosq->protocol == mosq_p_mqtt5){ send__disconnect(mosq, MQTT_RC_PACKET_TOO_LARGE, NULL); } return MOSQ_ERR_OVERSIZE_PACKET; } #else /* FIXME - client case for incoming message received from broker too large */ #endif if(mosq->in_packet.remaining_length > 0){ mosq->in_packet.payload = mosquitto__malloc(mosq->in_packet.remaining_length*sizeof(uint8_t)); if(!mosq->in_packet.payload){ return MOSQ_ERR_NOMEM; } mosq->in_packet.to_process = mosq->in_packet.remaining_length; } } while(mosq->in_packet.to_process>0){ read_length = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(read_length > 0){ G_BYTES_RECEIVED_INC(read_length); mosq->in_packet.to_process -= (uint32_t)read_length; mosq->in_packet.pos += (uint32_t)read_length; }else{ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ if(mosq->in_packet.to_process > 1000){ /* Update last_msg_in time if more than 1000 bytes left to * receive. Helps when receiving large messages. * This is an arbitrary limit, but with some consideration. * If a client can't send 1000 bytes in a second it * probably shouldn't be using a 1 second keep alive. */ #ifdef WITH_BROKER keepalive__update(mosq); #else pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = mosquitto_time(); pthread_mutex_unlock(&mosq->msgtime_mutex); #endif } return MOSQ_ERR_SUCCESS; }else{ switch(errno){ case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; case COMPAT_EINTR: return MOSQ_ERR_SUCCESS; default: return MOSQ_ERR_ERRNO; } } } } /* All data for this packet is read. */ mosq->in_packet.pos = 0; #ifdef WITH_BROKER G_MSGS_RECEIVED_INC(1); if(((mosq->in_packet.command)&0xF0) == CMD_PUBLISH){ G_PUB_MSGS_RECEIVED_INC(1); } #endif rc = handle__packet(mosq); /* Free data and reset values */ packet__cleanup(&mosq->in_packet); #ifdef WITH_BROKER keepalive__update(mosq); #else pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = mosquitto_time(); pthread_mutex_unlock(&mosq->msgtime_mutex); #endif return rc; } mosquitto-2.0.18/lib/packet_mosq.h000066400000000000000000000043471450213760600171070ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef PACKET_MOSQ_H #define PACKET_MOSQ_H #include "mosquitto_internal.h" #include "mosquitto.h" int packet__alloc(struct mosquitto__packet *packet); void packet__cleanup(struct mosquitto__packet *packet); void packet__cleanup_all(struct mosquitto *mosq); void packet__cleanup_all_no_locks(struct mosquitto *mosq); int packet__queue(struct mosquitto *mosq, struct mosquitto__packet *packet); int packet__check_oversize(struct mosquitto *mosq, uint32_t remaining_length); int packet__read_byte(struct mosquitto__packet *packet, uint8_t *byte); int packet__read_bytes(struct mosquitto__packet *packet, void *bytes, uint32_t count); int packet__read_binary(struct mosquitto__packet *packet, uint8_t **data, uint16_t *length); int packet__read_string(struct mosquitto__packet *packet, char **str, uint16_t *length); int packet__read_uint16(struct mosquitto__packet *packet, uint16_t *word); int packet__read_uint32(struct mosquitto__packet *packet, uint32_t *word); int packet__read_varint(struct mosquitto__packet *packet, uint32_t *word, uint8_t *bytes); void packet__write_byte(struct mosquitto__packet *packet, uint8_t byte); void packet__write_bytes(struct mosquitto__packet *packet, const void *bytes, uint32_t count); void packet__write_string(struct mosquitto__packet *packet, const char *str, uint16_t length); void packet__write_uint16(struct mosquitto__packet *packet, uint16_t word); void packet__write_uint32(struct mosquitto__packet *packet, uint32_t word); int packet__write_varint(struct mosquitto__packet *packet, uint32_t word); unsigned int packet__varint_bytes(uint32_t word); int packet__write(struct mosquitto *mosq); int packet__read(struct mosquitto *mosq); #endif mosquitto-2.0.18/lib/property_mosq.c000066400000000000000000001050451450213760600175140ustar00rootroot00000000000000/* Copyright (c) 2018-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifndef WIN32 # include #endif #include "logging_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" static int property__read(struct mosquitto__packet *packet, uint32_t *len, mosquitto_property *property) { int rc; uint32_t property_identifier; uint8_t byte; uint8_t byte_count; uint16_t uint16; uint32_t uint32; uint32_t varint; char *str1, *str2; uint16_t slen1, slen2; if(!property) return MOSQ_ERR_INVAL; rc = packet__read_varint(packet, &property_identifier, NULL); if(rc){ return rc; } *len -= 1; memset(property, 0, sizeof(mosquitto_property)); property->identifier = (int32_t)property_identifier; switch(property_identifier){ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: case MQTT_PROP_MAXIMUM_QOS: case MQTT_PROP_RETAIN_AVAILABLE: case MQTT_PROP_WILDCARD_SUB_AVAILABLE: case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: case MQTT_PROP_SHARED_SUB_AVAILABLE: rc = packet__read_byte(packet, &byte); if(rc) return rc; *len -= 1; /* byte */ property->value.i8 = byte; break; case MQTT_PROP_SERVER_KEEP_ALIVE: case MQTT_PROP_RECEIVE_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS: rc = packet__read_uint16(packet, &uint16); if(rc) return rc; *len -= 2; /* uint16 */ property->value.i16 = uint16; break; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: case MQTT_PROP_SESSION_EXPIRY_INTERVAL: case MQTT_PROP_WILL_DELAY_INTERVAL: case MQTT_PROP_MAXIMUM_PACKET_SIZE: rc = packet__read_uint32(packet, &uint32); if(rc) return rc; *len -= 4; /* uint32 */ property->value.i32 = uint32; break; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: rc = packet__read_varint(packet, &varint, &byte_count); if(rc) return rc; *len -= byte_count; property->value.varint = varint; break; case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: case MQTT_PROP_AUTHENTICATION_METHOD: case MQTT_PROP_RESPONSE_INFORMATION: case MQTT_PROP_SERVER_REFERENCE: case MQTT_PROP_REASON_STRING: rc = packet__read_string(packet, &str1, &slen1); if(rc) return rc; *len = (*len) - 2 - slen1; /* uint16, string len */ property->value.s.v = str1; property->value.s.len = slen1; break; case MQTT_PROP_AUTHENTICATION_DATA: case MQTT_PROP_CORRELATION_DATA: rc = packet__read_binary(packet, (uint8_t **)&str1, &slen1); if(rc) return rc; *len = (*len) - 2 - slen1; /* uint16, binary len */ property->value.bin.v = str1; property->value.bin.len = slen1; break; case MQTT_PROP_USER_PROPERTY: rc = packet__read_string(packet, &str1, &slen1); if(rc) return rc; *len = (*len) - 2 - slen1; /* uint16, string len */ rc = packet__read_string(packet, &str2, &slen2); if(rc){ mosquitto__free(str1); return rc; } *len = (*len) - 2 - slen2; /* uint16, string len */ property->name.v = str1; property->name.len = slen1; property->value.s.v = str2; property->value.s.len = slen2; break; default: #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", property_identifier); #endif return MOSQ_ERR_MALFORMED_PACKET; } return MOSQ_ERR_SUCCESS; } int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **properties) { int rc; uint32_t proplen; mosquitto_property *p, *tail = NULL; rc = packet__read_varint(packet, &proplen, NULL); if(rc) return rc; *properties = NULL; /* The order of properties must be preserved for some types, so keep the * same order for all */ while(proplen > 0){ p = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!p){ mosquitto_property_free_all(properties); return MOSQ_ERR_NOMEM; } rc = property__read(packet, &proplen, p); if(rc){ mosquitto__free(p); mosquitto_property_free_all(properties); return rc; } if(!(*properties)){ *properties = p; }else{ tail->next = p; } tail = p; } rc = mosquitto_property_check_all(command, *properties); if(rc){ mosquitto_property_free_all(properties); return rc; } return MOSQ_ERR_SUCCESS; } void property__free(mosquitto_property **property) { if(!property || !(*property)) return; switch((*property)->identifier){ case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: case MQTT_PROP_AUTHENTICATION_METHOD: case MQTT_PROP_RESPONSE_INFORMATION: case MQTT_PROP_SERVER_REFERENCE: case MQTT_PROP_REASON_STRING: mosquitto__free((*property)->value.s.v); break; case MQTT_PROP_AUTHENTICATION_DATA: case MQTT_PROP_CORRELATION_DATA: mosquitto__free((*property)->value.bin.v); break; case MQTT_PROP_USER_PROPERTY: mosquitto__free((*property)->name.v); mosquitto__free((*property)->value.s.v); break; case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: case MQTT_PROP_SESSION_EXPIRY_INTERVAL: case MQTT_PROP_SERVER_KEEP_ALIVE: case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: case MQTT_PROP_WILL_DELAY_INTERVAL: case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: case MQTT_PROP_RECEIVE_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS: case MQTT_PROP_MAXIMUM_QOS: case MQTT_PROP_RETAIN_AVAILABLE: case MQTT_PROP_MAXIMUM_PACKET_SIZE: case MQTT_PROP_WILDCARD_SUB_AVAILABLE: case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: case MQTT_PROP_SHARED_SUB_AVAILABLE: /* Nothing to free */ break; } free(*property); *property = NULL; } void mosquitto_property_free_all(mosquitto_property **property) { mosquitto_property *p, *next; if(!property) return; p = *property; while(p){ next = p->next; property__free(&p); p = next; } *property = NULL; } unsigned int property__get_length(const mosquitto_property *property) { if(!property) return 0; switch(property->identifier){ /* Byte */ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: case MQTT_PROP_MAXIMUM_QOS: case MQTT_PROP_RETAIN_AVAILABLE: case MQTT_PROP_WILDCARD_SUB_AVAILABLE: case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: case MQTT_PROP_SHARED_SUB_AVAILABLE: return 2; /* 1 (identifier) + 1 byte */ /* uint16 */ case MQTT_PROP_SERVER_KEEP_ALIVE: case MQTT_PROP_RECEIVE_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS: return 3; /* 1 (identifier) + 2 bytes */ /* uint32 */ case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: case MQTT_PROP_WILL_DELAY_INTERVAL: case MQTT_PROP_MAXIMUM_PACKET_SIZE: case MQTT_PROP_SESSION_EXPIRY_INTERVAL: return 5; /* 1 (identifier) + 4 bytes */ /* varint */ case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: if(property->value.varint < 128){ return 2; }else if(property->value.varint < 16384){ return 3; }else if(property->value.varint < 2097152){ return 4; }else if(property->value.varint < 268435456){ return 5; }else{ return 0; } /* binary */ case MQTT_PROP_CORRELATION_DATA: case MQTT_PROP_AUTHENTICATION_DATA: return 3U + property->value.bin.len; /* 1 + 2 bytes (len) + X bytes (payload) */ /* string */ case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: case MQTT_PROP_AUTHENTICATION_METHOD: case MQTT_PROP_RESPONSE_INFORMATION: case MQTT_PROP_SERVER_REFERENCE: case MQTT_PROP_REASON_STRING: return 3U + property->value.s.len; /* 1 + 2 bytes (len) + X bytes (string) */ /* string pair */ case MQTT_PROP_USER_PROPERTY: return 5U + property->value.s.len + property->name.len; /* 1 + 2*(2 bytes (len) + X bytes (string))*/ default: return 0; } return 0; } unsigned int property__get_length_all(const mosquitto_property *property) { const mosquitto_property *p; unsigned int len = 0; p = property; while(p){ len += property__get_length(p); p = p->next; } return len; } /* Return the number of bytes we need to add on to the remaining length when * encoding these properties. */ unsigned int property__get_remaining_length(const mosquitto_property *props) { unsigned int proplen, varbytes; proplen = property__get_length_all(props); varbytes = packet__varint_bytes(proplen); return proplen + varbytes; } static int property__write(struct mosquitto__packet *packet, const mosquitto_property *property) { int rc; rc = packet__write_varint(packet, (uint32_t)property->identifier); if(rc) return rc; switch(property->identifier){ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: case MQTT_PROP_MAXIMUM_QOS: case MQTT_PROP_RETAIN_AVAILABLE: case MQTT_PROP_WILDCARD_SUB_AVAILABLE: case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: case MQTT_PROP_SHARED_SUB_AVAILABLE: packet__write_byte(packet, property->value.i8); break; case MQTT_PROP_SERVER_KEEP_ALIVE: case MQTT_PROP_RECEIVE_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS: packet__write_uint16(packet, property->value.i16); break; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: case MQTT_PROP_SESSION_EXPIRY_INTERVAL: case MQTT_PROP_WILL_DELAY_INTERVAL: case MQTT_PROP_MAXIMUM_PACKET_SIZE: packet__write_uint32(packet, property->value.i32); break; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: return packet__write_varint(packet, property->value.varint); case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: case MQTT_PROP_AUTHENTICATION_METHOD: case MQTT_PROP_RESPONSE_INFORMATION: case MQTT_PROP_SERVER_REFERENCE: case MQTT_PROP_REASON_STRING: packet__write_string(packet, property->value.s.v, property->value.s.len); break; case MQTT_PROP_AUTHENTICATION_DATA: case MQTT_PROP_CORRELATION_DATA: packet__write_uint16(packet, property->value.bin.len); packet__write_bytes(packet, property->value.bin.v, property->value.bin.len); break; case MQTT_PROP_USER_PROPERTY: packet__write_string(packet, property->name.v, property->name.len); packet__write_string(packet, property->value.s.v, property->value.s.len); break; default: #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Unsupported property type: %d", property->identifier); #endif return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *properties, bool write_len) { int rc; const mosquitto_property *p; if(write_len){ rc = packet__write_varint(packet, property__get_length_all(properties)); if(rc) return rc; } p = properties; while(p){ rc = property__write(packet, p); if(rc) return rc; p = p->next; } return MOSQ_ERR_SUCCESS; } int mosquitto_property_check_command(int command, int identifier) { switch(identifier){ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_CORRELATION_DATA: if(command != CMD_PUBLISH && command != CMD_WILL){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: if(command != CMD_PUBLISH && command != CMD_SUBSCRIBE){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_SESSION_EXPIRY_INTERVAL: if(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_DISCONNECT){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_AUTHENTICATION_METHOD: case MQTT_PROP_AUTHENTICATION_DATA: if(command != CMD_CONNECT && command != CMD_CONNACK && command != CMD_AUTH){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: case MQTT_PROP_SERVER_KEEP_ALIVE: case MQTT_PROP_RESPONSE_INFORMATION: case MQTT_PROP_MAXIMUM_QOS: case MQTT_PROP_RETAIN_AVAILABLE: case MQTT_PROP_WILDCARD_SUB_AVAILABLE: case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: case MQTT_PROP_SHARED_SUB_AVAILABLE: if(command != CMD_CONNACK){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_WILL_DELAY_INTERVAL: if(command != CMD_WILL){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: if(command != CMD_CONNECT){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_SERVER_REFERENCE: if(command != CMD_CONNACK && command != CMD_DISCONNECT){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_REASON_STRING: if(command == CMD_CONNECT || command == CMD_PUBLISH || command == CMD_SUBSCRIBE || command == CMD_UNSUBSCRIBE){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_RECEIVE_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: case MQTT_PROP_MAXIMUM_PACKET_SIZE: if(command != CMD_CONNECT && command != CMD_CONNACK){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_TOPIC_ALIAS: if(command != CMD_PUBLISH){ return MOSQ_ERR_PROTOCOL; } break; case MQTT_PROP_USER_PROPERTY: break; default: return MOSQ_ERR_PROTOCOL; } return MOSQ_ERR_SUCCESS; } const char *mosquitto_property_identifier_to_string(int identifier) { switch(identifier){ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: return "payload-format-indicator"; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: return "message-expiry-interval"; case MQTT_PROP_CONTENT_TYPE: return "content-type"; case MQTT_PROP_RESPONSE_TOPIC: return "response-topic"; case MQTT_PROP_CORRELATION_DATA: return "correlation-data"; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: return "subscription-identifier"; case MQTT_PROP_SESSION_EXPIRY_INTERVAL: return "session-expiry-interval"; case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: return "assigned-client-identifier"; case MQTT_PROP_SERVER_KEEP_ALIVE: return "server-keep-alive"; case MQTT_PROP_AUTHENTICATION_METHOD: return "authentication-method"; case MQTT_PROP_AUTHENTICATION_DATA: return "authentication-data"; case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: return "request-problem-information"; case MQTT_PROP_WILL_DELAY_INTERVAL: return "will-delay-interval"; case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: return "request-response-information"; case MQTT_PROP_RESPONSE_INFORMATION: return "response-information"; case MQTT_PROP_SERVER_REFERENCE: return "server-reference"; case MQTT_PROP_REASON_STRING: return "reason-string"; case MQTT_PROP_RECEIVE_MAXIMUM: return "receive-maximum"; case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: return "topic-alias-maximum"; case MQTT_PROP_TOPIC_ALIAS: return "topic-alias"; case MQTT_PROP_MAXIMUM_QOS: return "maximum-qos"; case MQTT_PROP_RETAIN_AVAILABLE: return "retain-available"; case MQTT_PROP_USER_PROPERTY: return "user-property"; case MQTT_PROP_MAXIMUM_PACKET_SIZE: return "maximum-packet-size"; case MQTT_PROP_WILDCARD_SUB_AVAILABLE: return "wildcard-subscription-available"; case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: return "subscription-identifier-available"; case MQTT_PROP_SHARED_SUB_AVAILABLE: return "shared-subscription-available"; default: return NULL; } } int mosquitto_string_to_property_info(const char *propname, int *identifier, int *type) { if(!propname) return MOSQ_ERR_INVAL; if(!strcasecmp(propname, "payload-format-indicator")){ *identifier = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR; *type = MQTT_PROP_TYPE_BYTE; }else if(!strcasecmp(propname, "message-expiry-interval")){ *identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; *type = MQTT_PROP_TYPE_INT32; }else if(!strcasecmp(propname, "content-type")){ *identifier = MQTT_PROP_CONTENT_TYPE; *type = MQTT_PROP_TYPE_STRING; }else if(!strcasecmp(propname, "response-topic")){ *identifier = MQTT_PROP_RESPONSE_TOPIC; *type = MQTT_PROP_TYPE_STRING; }else if(!strcasecmp(propname, "correlation-data")){ *identifier = MQTT_PROP_CORRELATION_DATA; *type = MQTT_PROP_TYPE_BINARY; }else if(!strcasecmp(propname, "subscription-identifier")){ *identifier = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; *type = MQTT_PROP_TYPE_VARINT; }else if(!strcasecmp(propname, "session-expiry-interval")){ *identifier = MQTT_PROP_SESSION_EXPIRY_INTERVAL; *type = MQTT_PROP_TYPE_INT32; }else if(!strcasecmp(propname, "assigned-client-identifier")){ *identifier = MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER; *type = MQTT_PROP_TYPE_STRING; }else if(!strcasecmp(propname, "server-keep-alive")){ *identifier = MQTT_PROP_SERVER_KEEP_ALIVE; *type = MQTT_PROP_TYPE_INT16; }else if(!strcasecmp(propname, "authentication-method")){ *identifier = MQTT_PROP_AUTHENTICATION_METHOD; *type = MQTT_PROP_TYPE_STRING; }else if(!strcasecmp(propname, "authentication-data")){ *identifier = MQTT_PROP_AUTHENTICATION_DATA; *type = MQTT_PROP_TYPE_BINARY; }else if(!strcasecmp(propname, "request-problem-information")){ *identifier = MQTT_PROP_REQUEST_PROBLEM_INFORMATION; *type = MQTT_PROP_TYPE_BYTE; }else if(!strcasecmp(propname, "will-delay-interval")){ *identifier = MQTT_PROP_WILL_DELAY_INTERVAL; *type = MQTT_PROP_TYPE_INT32; }else if(!strcasecmp(propname, "request-response-information")){ *identifier = MQTT_PROP_REQUEST_RESPONSE_INFORMATION; *type = MQTT_PROP_TYPE_BYTE; }else if(!strcasecmp(propname, "response-information")){ *identifier = MQTT_PROP_RESPONSE_INFORMATION; *type = MQTT_PROP_TYPE_STRING; }else if(!strcasecmp(propname, "server-reference")){ *identifier = MQTT_PROP_SERVER_REFERENCE; *type = MQTT_PROP_TYPE_STRING; }else if(!strcasecmp(propname, "reason-string")){ *identifier = MQTT_PROP_REASON_STRING; *type = MQTT_PROP_TYPE_STRING; }else if(!strcasecmp(propname, "receive-maximum")){ *identifier = MQTT_PROP_RECEIVE_MAXIMUM; *type = MQTT_PROP_TYPE_INT16; }else if(!strcasecmp(propname, "topic-alias-maximum")){ *identifier = MQTT_PROP_TOPIC_ALIAS_MAXIMUM; *type = MQTT_PROP_TYPE_INT16; }else if(!strcasecmp(propname, "topic-alias")){ *identifier = MQTT_PROP_TOPIC_ALIAS; *type = MQTT_PROP_TYPE_INT16; }else if(!strcasecmp(propname, "maximum-qos")){ *identifier = MQTT_PROP_MAXIMUM_QOS; *type = MQTT_PROP_TYPE_BYTE; }else if(!strcasecmp(propname, "retain-available")){ *identifier = MQTT_PROP_RETAIN_AVAILABLE; *type = MQTT_PROP_TYPE_BYTE; }else if(!strcasecmp(propname, "user-property")){ *identifier = MQTT_PROP_USER_PROPERTY; *type = MQTT_PROP_TYPE_STRING_PAIR; }else if(!strcasecmp(propname, "maximum-packet-size")){ *identifier = MQTT_PROP_MAXIMUM_PACKET_SIZE; *type = MQTT_PROP_TYPE_INT32; }else if(!strcasecmp(propname, "wildcard-subscription-available")){ *identifier = MQTT_PROP_WILDCARD_SUB_AVAILABLE; *type = MQTT_PROP_TYPE_BYTE; }else if(!strcasecmp(propname, "subscription-identifier-available")){ *identifier = MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE; *type = MQTT_PROP_TYPE_BYTE; }else if(!strcasecmp(propname, "shared-subscription-available")){ *identifier = MQTT_PROP_SHARED_SUB_AVAILABLE; *type = MQTT_PROP_TYPE_BYTE; }else{ return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } static void property__add(mosquitto_property **proplist, struct mqtt5__property *prop) { mosquitto_property *p; if(!(*proplist)){ *proplist = prop; } p = *proplist; while(p->next){ p = p->next; } p->next = prop; prop->next = NULL; } int mosquitto_property_add_byte(mosquitto_property **proplist, int identifier, uint8_t value) { mosquitto_property *prop; if(!proplist) return MOSQ_ERR_INVAL; if(identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR && identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION && identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION && identifier != MQTT_PROP_MAXIMUM_QOS && identifier != MQTT_PROP_RETAIN_AVAILABLE && identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE && identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE && identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){ return MOSQ_ERR_INVAL; } prop = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!prop) return MOSQ_ERR_NOMEM; prop->client_generated = true; prop->identifier = identifier; prop->value.i8 = value; property__add(proplist, prop); return MOSQ_ERR_SUCCESS; } int mosquitto_property_add_int16(mosquitto_property **proplist, int identifier, uint16_t value) { mosquitto_property *prop; if(!proplist) return MOSQ_ERR_INVAL; if(identifier != MQTT_PROP_SERVER_KEEP_ALIVE && identifier != MQTT_PROP_RECEIVE_MAXIMUM && identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM && identifier != MQTT_PROP_TOPIC_ALIAS){ return MOSQ_ERR_INVAL; } prop = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!prop) return MOSQ_ERR_NOMEM; prop->client_generated = true; prop->identifier = identifier; prop->value.i16 = value; property__add(proplist, prop); return MOSQ_ERR_SUCCESS; } int mosquitto_property_add_int32(mosquitto_property **proplist, int identifier, uint32_t value) { mosquitto_property *prop; if(!proplist) return MOSQ_ERR_INVAL; if(identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL && identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL && identifier != MQTT_PROP_WILL_DELAY_INTERVAL && identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){ return MOSQ_ERR_INVAL; } prop = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!prop) return MOSQ_ERR_NOMEM; prop->client_generated = true; prop->identifier = identifier; prop->value.i32 = value; property__add(proplist, prop); return MOSQ_ERR_SUCCESS; } int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value) { mosquitto_property *prop; if(!proplist || value > 268435455) return MOSQ_ERR_INVAL; if(identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER) return MOSQ_ERR_INVAL; prop = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!prop) return MOSQ_ERR_NOMEM; prop->client_generated = true; prop->identifier = identifier; prop->value.varint = value; property__add(proplist, prop); return MOSQ_ERR_SUCCESS; } int mosquitto_property_add_binary(mosquitto_property **proplist, int identifier, const void *value, uint16_t len) { mosquitto_property *prop; if(!proplist) return MOSQ_ERR_INVAL; if(identifier != MQTT_PROP_CORRELATION_DATA && identifier != MQTT_PROP_AUTHENTICATION_DATA){ return MOSQ_ERR_INVAL; } prop = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!prop) return MOSQ_ERR_NOMEM; prop->client_generated = true; prop->identifier = identifier; if(len){ prop->value.bin.v = mosquitto__malloc(len); if(!prop->value.bin.v){ mosquitto__free(prop); return MOSQ_ERR_NOMEM; } memcpy(prop->value.bin.v, value, len); prop->value.bin.len = len; } property__add(proplist, prop); return MOSQ_ERR_SUCCESS; } int mosquitto_property_add_string(mosquitto_property **proplist, int identifier, const char *value) { mosquitto_property *prop; size_t slen = 0; if(!proplist) return MOSQ_ERR_INVAL; if(value){ slen = strlen(value); if(mosquitto_validate_utf8(value, (int)slen)) return MOSQ_ERR_MALFORMED_UTF8; } if(identifier != MQTT_PROP_CONTENT_TYPE && identifier != MQTT_PROP_RESPONSE_TOPIC && identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER && identifier != MQTT_PROP_AUTHENTICATION_METHOD && identifier != MQTT_PROP_RESPONSE_INFORMATION && identifier != MQTT_PROP_SERVER_REFERENCE && identifier != MQTT_PROP_REASON_STRING){ return MOSQ_ERR_INVAL; } prop = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!prop) return MOSQ_ERR_NOMEM; prop->client_generated = true; prop->identifier = identifier; if(value && slen > 0){ prop->value.s.v = mosquitto__strdup(value); if(!prop->value.s.v){ mosquitto__free(prop); return MOSQ_ERR_NOMEM; } prop->value.s.len = (uint16_t)slen; } property__add(proplist, prop); return MOSQ_ERR_SUCCESS; } int mosquitto_property_add_string_pair(mosquitto_property **proplist, int identifier, const char *name, const char *value) { mosquitto_property *prop; size_t slen_name = 0, slen_value = 0; if(!proplist) return MOSQ_ERR_INVAL; if(identifier != MQTT_PROP_USER_PROPERTY) return MOSQ_ERR_INVAL; if(name){ slen_name = strlen(name); if(mosquitto_validate_utf8(name, (int)slen_name)) return MOSQ_ERR_MALFORMED_UTF8; } if(value){ if(mosquitto_validate_utf8(value, (int)slen_value)) return MOSQ_ERR_MALFORMED_UTF8; } prop = mosquitto__calloc(1, sizeof(mosquitto_property)); if(!prop) return MOSQ_ERR_NOMEM; prop->client_generated = true; prop->identifier = identifier; if(name){ prop->name.v = mosquitto__strdup(name); if(!prop->name.v){ mosquitto__free(prop); return MOSQ_ERR_NOMEM; } prop->name.len = (uint16_t)strlen(name); } if(value){ prop->value.s.v = mosquitto__strdup(value); if(!prop->value.s.v){ mosquitto__free(prop->name.v); mosquitto__free(prop); return MOSQ_ERR_NOMEM; } prop->value.s.len = (uint16_t)strlen(value); } property__add(proplist, prop); return MOSQ_ERR_SUCCESS; } int mosquitto_property_check_all(int command, const mosquitto_property *properties) { const mosquitto_property *p, *tail; int rc; p = properties; while(p){ /* Validity checks */ if(p->identifier == MQTT_PROP_REQUEST_PROBLEM_INFORMATION || p->identifier == MQTT_PROP_PAYLOAD_FORMAT_INDICATOR || p->identifier == MQTT_PROP_REQUEST_RESPONSE_INFORMATION || p->identifier == MQTT_PROP_MAXIMUM_QOS || p->identifier == MQTT_PROP_RETAIN_AVAILABLE || p->identifier == MQTT_PROP_WILDCARD_SUB_AVAILABLE || p->identifier == MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE || p->identifier == MQTT_PROP_SHARED_SUB_AVAILABLE){ if(p->value.i8 > 1){ return MOSQ_ERR_PROTOCOL; } }else if(p->identifier == MQTT_PROP_MAXIMUM_PACKET_SIZE){ if( p->value.i32 == 0){ return MOSQ_ERR_PROTOCOL; } }else if(p->identifier == MQTT_PROP_RECEIVE_MAXIMUM || p->identifier == MQTT_PROP_TOPIC_ALIAS){ if(p->value.i16 == 0){ return MOSQ_ERR_PROTOCOL; } } /* Check for properties on incorrect commands */ rc = mosquitto_property_check_command(command, p->identifier); if(rc) return rc; /* Check for duplicates */ if(p->identifier != MQTT_PROP_USER_PROPERTY){ tail = p->next; while(tail){ if(p->identifier == tail->identifier){ return MOSQ_ERR_DUPLICATE_PROPERTY; } tail = tail->next; } } p = p->next; } return MOSQ_ERR_SUCCESS; } static const mosquitto_property *property__get_property(const mosquitto_property *proplist, int identifier, bool skip_first) { const mosquitto_property *p; bool is_first = true; p = proplist; while(p){ if(p->identifier == identifier){ if(!is_first || !skip_first){ return p; } is_first = false; } p = p->next; } return NULL; } int mosquitto_property_identifier(const mosquitto_property *property) { if(property == NULL) return 0; return property->identifier; } const mosquitto_property *mosquitto_property_next(const mosquitto_property *proplist) { if(proplist == NULL) return NULL; return proplist->next; } const mosquitto_property *mosquitto_property_read_byte(const mosquitto_property *proplist, int identifier, uint8_t *value, bool skip_first) { const mosquitto_property *p; if(!proplist) return NULL; p = property__get_property(proplist, identifier, skip_first); if(!p) return NULL; if(p->identifier != MQTT_PROP_PAYLOAD_FORMAT_INDICATOR && p->identifier != MQTT_PROP_REQUEST_PROBLEM_INFORMATION && p->identifier != MQTT_PROP_REQUEST_RESPONSE_INFORMATION && p->identifier != MQTT_PROP_MAXIMUM_QOS && p->identifier != MQTT_PROP_RETAIN_AVAILABLE && p->identifier != MQTT_PROP_WILDCARD_SUB_AVAILABLE && p->identifier != MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE && p->identifier != MQTT_PROP_SHARED_SUB_AVAILABLE){ return NULL; } if(value) *value = p->value.i8; return p; } const mosquitto_property *mosquitto_property_read_int16(const mosquitto_property *proplist, int identifier, uint16_t *value, bool skip_first) { const mosquitto_property *p; if(!proplist) return NULL; p = property__get_property(proplist, identifier, skip_first); if(!p) return NULL; if(p->identifier != MQTT_PROP_SERVER_KEEP_ALIVE && p->identifier != MQTT_PROP_RECEIVE_MAXIMUM && p->identifier != MQTT_PROP_TOPIC_ALIAS_MAXIMUM && p->identifier != MQTT_PROP_TOPIC_ALIAS){ return NULL; } if(value) *value = p->value.i16; return p; } const mosquitto_property *mosquitto_property_read_int32(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first) { const mosquitto_property *p; if(!proplist) return NULL; p = property__get_property(proplist, identifier, skip_first); if(!p) return NULL; if(p->identifier != MQTT_PROP_MESSAGE_EXPIRY_INTERVAL && p->identifier != MQTT_PROP_SESSION_EXPIRY_INTERVAL && p->identifier != MQTT_PROP_WILL_DELAY_INTERVAL && p->identifier != MQTT_PROP_MAXIMUM_PACKET_SIZE){ return NULL; } if(value) *value = p->value.i32; return p; } const mosquitto_property *mosquitto_property_read_varint(const mosquitto_property *proplist, int identifier, uint32_t *value, bool skip_first) { const mosquitto_property *p; if(!proplist) return NULL; p = property__get_property(proplist, identifier, skip_first); if(!p) return NULL; if(p->identifier != MQTT_PROP_SUBSCRIPTION_IDENTIFIER){ return NULL; } if(value) *value = p->value.varint; return p; } const mosquitto_property *mosquitto_property_read_binary(const mosquitto_property *proplist, int identifier, void **value, uint16_t *len, bool skip_first) { const mosquitto_property *p; if(!proplist || (value && !len) || (!value && len)) return NULL; if(value) *value = NULL; p = property__get_property(proplist, identifier, skip_first); if(!p) return NULL; if(p->identifier != MQTT_PROP_CORRELATION_DATA && p->identifier != MQTT_PROP_AUTHENTICATION_DATA){ return NULL; } if(value){ *len = p->value.bin.len; *value = calloc(1, *len + 1U); if(!(*value)) return NULL; memcpy(*value, p->value.bin.v, *len); } return p; } const mosquitto_property *mosquitto_property_read_string(const mosquitto_property *proplist, int identifier, char **value, bool skip_first) { const mosquitto_property *p; if(!proplist) return NULL; p = property__get_property(proplist, identifier, skip_first); if(!p) return NULL; if(p->identifier != MQTT_PROP_CONTENT_TYPE && p->identifier != MQTT_PROP_RESPONSE_TOPIC && p->identifier != MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER && p->identifier != MQTT_PROP_AUTHENTICATION_METHOD && p->identifier != MQTT_PROP_RESPONSE_INFORMATION && p->identifier != MQTT_PROP_SERVER_REFERENCE && p->identifier != MQTT_PROP_REASON_STRING){ return NULL; } if(value){ *value = calloc(1, (size_t)p->value.s.len+1); if(!(*value)) return NULL; memcpy(*value, p->value.s.v, p->value.s.len); } return p; } const mosquitto_property *mosquitto_property_read_string_pair(const mosquitto_property *proplist, int identifier, char **name, char **value, bool skip_first) { const mosquitto_property *p; if(!proplist) return NULL; if(name) *name = NULL; if(value) *value = NULL; p = property__get_property(proplist, identifier, skip_first); if(!p) return NULL; if(p->identifier != MQTT_PROP_USER_PROPERTY) return NULL; if(name){ *name = calloc(1, (size_t)p->name.len+1); if(!(*name)) return NULL; memcpy(*name, p->name.v, p->name.len); } if(value){ *value = calloc(1, (size_t)p->value.s.len+1); if(!(*value)){ if(name){ free(*name); *name = NULL; } return NULL; } memcpy(*value, p->value.s.v, p->value.s.len); } return p; } int mosquitto_property_copy_all(mosquitto_property **dest, const mosquitto_property *src) { mosquitto_property *pnew, *plast = NULL; if(!src) return MOSQ_ERR_SUCCESS; if(!dest) return MOSQ_ERR_INVAL; *dest = NULL; while(src){ pnew = calloc(1, sizeof(mosquitto_property)); if(!pnew){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; } if(plast){ plast->next = pnew; }else{ *dest = pnew; } plast = pnew; pnew->client_generated = src->client_generated; pnew->identifier = src->identifier; switch(pnew->identifier){ case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_REQUEST_PROBLEM_INFORMATION: case MQTT_PROP_REQUEST_RESPONSE_INFORMATION: case MQTT_PROP_MAXIMUM_QOS: case MQTT_PROP_RETAIN_AVAILABLE: case MQTT_PROP_WILDCARD_SUB_AVAILABLE: case MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE: case MQTT_PROP_SHARED_SUB_AVAILABLE: pnew->value.i8 = src->value.i8; break; case MQTT_PROP_SERVER_KEEP_ALIVE: case MQTT_PROP_RECEIVE_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS_MAXIMUM: case MQTT_PROP_TOPIC_ALIAS: pnew->value.i16 = src->value.i16; break; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: case MQTT_PROP_SESSION_EXPIRY_INTERVAL: case MQTT_PROP_WILL_DELAY_INTERVAL: case MQTT_PROP_MAXIMUM_PACKET_SIZE: pnew->value.i32 = src->value.i32; break; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: pnew->value.varint = src->value.varint; break; case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER: case MQTT_PROP_AUTHENTICATION_METHOD: case MQTT_PROP_RESPONSE_INFORMATION: case MQTT_PROP_SERVER_REFERENCE: case MQTT_PROP_REASON_STRING: pnew->value.s.len = src->value.s.len; pnew->value.s.v = src->value.s.v ? strdup(src->value.s.v) : (char*)calloc(1,1); if(!pnew->value.s.v){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; } break; case MQTT_PROP_AUTHENTICATION_DATA: case MQTT_PROP_CORRELATION_DATA: pnew->value.bin.len = src->value.bin.len; pnew->value.bin.v = malloc(pnew->value.bin.len); if(!pnew->value.bin.v){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; } memcpy(pnew->value.bin.v, src->value.bin.v, pnew->value.bin.len); break; case MQTT_PROP_USER_PROPERTY: pnew->value.s.len = src->value.s.len; pnew->value.s.v = src->value.s.v ? strdup(src->value.s.v) : (char*)calloc(1,1); if(!pnew->value.s.v){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; } pnew->name.len = src->name.len; pnew->name.v = src->name.v ? strdup(src->name.v) : (char*)calloc(1,1); if(!pnew->name.v){ mosquitto_property_free_all(dest); return MOSQ_ERR_NOMEM; } break; default: mosquitto_property_free_all(dest); return MOSQ_ERR_INVAL; } src = src->next; } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/property_mosq.h000066400000000000000000000027441450213760600175230ustar00rootroot00000000000000/* Copyright (c) 2018-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef PROPERTY_MOSQ_H #define PROPERTY_MOSQ_H #include "mosquitto_internal.h" #include "mosquitto.h" struct mqtt__string { char *v; uint16_t len; }; struct mqtt5__property { struct mqtt5__property *next; union { uint8_t i8; uint16_t i16; uint32_t i32; uint32_t varint; struct mqtt__string bin; struct mqtt__string s; } value; struct mqtt__string name; int32_t identifier; bool client_generated; }; int property__read_all(int command, struct mosquitto__packet *packet, mosquitto_property **property); int property__write_all(struct mosquitto__packet *packet, const mosquitto_property *property, bool write_len); void property__free(mosquitto_property **property); unsigned int property__get_length(const mosquitto_property *property); unsigned int property__get_length_all(const mosquitto_property *property); unsigned int property__get_remaining_length(const mosquitto_property *props); #endif mosquitto-2.0.18/lib/read_handle.c000066400000000000000000000036031450213760600170140ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "messages_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "time_mosq.h" #include "util_mosq.h" int handle__packet(struct mosquitto *mosq) { assert(mosq); switch((mosq->in_packet.command)&0xF0){ case CMD_PINGREQ: return handle__pingreq(mosq); case CMD_PINGRESP: return handle__pingresp(mosq); case CMD_PUBACK: return handle__pubackcomp(mosq, "PUBACK"); case CMD_PUBCOMP: return handle__pubackcomp(mosq, "PUBCOMP"); case CMD_PUBLISH: return handle__publish(mosq); case CMD_PUBREC: return handle__pubrec(mosq); case CMD_PUBREL: return handle__pubrel(mosq); case CMD_CONNACK: return handle__connack(mosq); case CMD_SUBACK: return handle__suback(mosq); case CMD_UNSUBACK: return handle__unsuback(mosq); case CMD_DISCONNECT: return handle__disconnect(mosq); case CMD_AUTH: return handle__auth(mosq); default: /* If we don't recognise the command, return an error straight away. */ log__printf(mosq, MOSQ_LOG_ERR, "Error: Unrecognised command %d\n", (mosq->in_packet.command)&0xF0); return MOSQ_ERR_PROTOCOL; } } mosquitto-2.0.18/lib/read_handle.h000066400000000000000000000024741450213760600170260ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef READ_HANDLE_H #define READ_HANDLE_H #include "mosquitto.h" struct mosquitto_db; int handle__pingreq(struct mosquitto *mosq); int handle__pingresp(struct mosquitto *mosq); #ifdef WITH_BROKER int handle__pubackcomp(struct mosquitto *mosq, const char *type); #else int handle__packet(struct mosquitto *mosq); int handle__connack(struct mosquitto *mosq); int handle__disconnect(struct mosquitto *mosq); int handle__pubackcomp(struct mosquitto *mosq, const char *type); int handle__publish(struct mosquitto *mosq); int handle__auth(struct mosquitto *mosq); #endif int handle__pubrec(struct mosquitto *mosq); int handle__pubrel(struct mosquitto *mosq); int handle__suback(struct mosquitto *mosq); int handle__unsuback(struct mosquitto *mosq); #endif mosquitto-2.0.18/lib/send_connect.c000066400000000000000000000137261450213760600172370ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "logging_mosq.h" #include "memory_mosq.h" #include "mosquitto.h" #include "mosquitto_internal.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; uint32_t payloadlen; uint8_t will = 0; uint8_t byte; int rc; uint8_t version; char *clientid, *username, *password; uint32_t headerlen; uint32_t proplen = 0, varbytes; mosquitto_property *local_props = NULL; uint16_t receive_maximum; assert(mosq); if(mosq->protocol == mosq_p_mqtt31 && !mosq->id) return MOSQ_ERR_PROTOCOL; #if defined(WITH_BROKER) && defined(WITH_BRIDGE) if(mosq->bridge){ clientid = mosq->bridge->remote_clientid; username = mosq->bridge->remote_username; password = mosq->bridge->remote_password; }else{ clientid = mosq->id; username = mosq->username; password = mosq->password; } #else clientid = mosq->id; username = mosq->username; password = mosq->password; #endif if(mosq->protocol == mosq_p_mqtt5){ /* Generate properties from options */ if(!mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &receive_maximum, false)){ rc = mosquitto_property_add_int16(&local_props, MQTT_PROP_RECEIVE_MAXIMUM, mosq->msgs_in.inflight_maximum); if(rc) return rc; }else{ mosq->msgs_in.inflight_maximum = receive_maximum; mosq->msgs_in.inflight_quota = receive_maximum; } version = MQTT_PROTOCOL_V5; headerlen = 10; proplen = 0; proplen += property__get_length_all(properties); proplen += property__get_length_all(local_props); varbytes = packet__varint_bytes(proplen); headerlen += proplen + varbytes; }else if(mosq->protocol == mosq_p_mqtt311){ version = MQTT_PROTOCOL_V311; headerlen = 10; }else if(mosq->protocol == mosq_p_mqtt31){ version = MQTT_PROTOCOL_V31; headerlen = 12; }else{ return MOSQ_ERR_INVAL; } packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; if(clientid){ payloadlen = (uint32_t)(2U+strlen(clientid)); }else{ payloadlen = 2U; } #ifdef WITH_BROKER if(mosq->will && (mosq->bridge == NULL || mosq->bridge->notifications_local_only == false)){ #else if(mosq->will){ #endif will = 1; assert(mosq->will->msg.topic); payloadlen += (uint32_t)(2+strlen(mosq->will->msg.topic) + 2+(uint32_t)mosq->will->msg.payloadlen); if(mosq->protocol == mosq_p_mqtt5){ payloadlen += property__get_remaining_length(mosq->will->properties); } } /* After this check we can be sure that the username and password are * always valid for the current protocol, so there is no need to check * username before checking password. */ if(mosq->protocol == mosq_p_mqtt31 || mosq->protocol == mosq_p_mqtt311){ if(password != NULL && username == NULL){ mosquitto__free(packet); return MOSQ_ERR_INVAL; } } if(username){ payloadlen += (uint32_t)(2+strlen(username)); } if(password){ payloadlen += (uint32_t)(2+strlen(password)); } packet->command = CMD_CONNECT; packet->remaining_length = headerlen + payloadlen; rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } /* Variable header */ if(version == MQTT_PROTOCOL_V31){ packet__write_string(packet, PROTOCOL_NAME_v31, (uint16_t)strlen(PROTOCOL_NAME_v31)); }else{ packet__write_string(packet, PROTOCOL_NAME, (uint16_t)strlen(PROTOCOL_NAME)); } #if defined(WITH_BROKER) && defined(WITH_BRIDGE) if(mosq->bridge && mosq->bridge->protocol_version != mosq_p_mqtt5 && mosq->bridge->try_private && mosq->bridge->try_private_accepted){ version |= 0x80; }else{ } #endif packet__write_byte(packet, version); byte = (uint8_t)((clean_session&0x1)<<1); if(will){ byte = byte | (uint8_t)(((mosq->will->msg.qos&0x3)<<3) | ((will&0x1)<<2)); if(mosq->retain_available){ byte |= (uint8_t)((mosq->will->msg.retain&0x1)<<5); } } if(username){ byte = byte | 0x1<<7; } if(mosq->password){ byte = byte | 0x1<<6; } packet__write_byte(packet, byte); packet__write_uint16(packet, keepalive); if(mosq->protocol == mosq_p_mqtt5){ /* Write properties */ packet__write_varint(packet, proplen); property__write_all(packet, properties, false); property__write_all(packet, local_props, false); } mosquitto_property_free_all(&local_props); /* Payload */ if(clientid){ packet__write_string(packet, clientid, (uint16_t)strlen(clientid)); }else{ packet__write_uint16(packet, 0); } if(will){ if(mosq->protocol == mosq_p_mqtt5){ /* Write will properties */ property__write_all(packet, mosq->will->properties, true); } packet__write_string(packet, mosq->will->msg.topic, (uint16_t)strlen(mosq->will->msg.topic)); packet__write_string(packet, (const char *)mosq->will->msg.payload, (uint16_t)mosq->will->msg.payloadlen); } if(username){ packet__write_string(packet, username, (uint16_t)strlen(username)); } if(password){ packet__write_string(packet, password, (uint16_t)strlen(password)); } mosq->keepalive = keepalive; #ifdef WITH_BROKER # ifdef WITH_BRIDGE log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending CONNECT", SAFE_PRINT(clientid)); # endif #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending CONNECT", SAFE_PRINT(clientid)); #endif return packet__queue(mosq, packet); } mosquitto-2.0.18/lib/send_disconnect.c000066400000000000000000000042041450213760600177260ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int rc; assert(mosq); #ifdef WITH_BROKER # ifdef WITH_BRIDGE if(mosq->bridge){ log__printf(mosq, MOSQ_LOG_DEBUG, "Bridge %s sending DISCONNECT", SAFE_PRINT(mosq->id)); }else # else { log__printf(mosq, MOSQ_LOG_DEBUG, "Sending DISCONNECT to %s (rc%d)", SAFE_PRINT(mosq->id), reason_code); } # endif #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending DISCONNECT", SAFE_PRINT(mosq->id)); #endif assert(mosq); packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->command = CMD_DISCONNECT; if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){ packet->remaining_length = 1; if(properties){ packet->remaining_length += property__get_remaining_length(properties); } }else{ packet->remaining_length = 0; } rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } if(mosq->protocol == mosq_p_mqtt5 && (reason_code != 0 || properties)){ packet__write_byte(packet, reason_code); if(properties){ property__write_all(packet, properties, true); } } return packet__queue(mosq, packet); } mosquitto-2.0.18/lib/send_mosq.c000066400000000000000000000124451450213760600165620ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" # include "sys_tree.h" #else # define G_PUB_BYTES_SENT_INC(A) #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" #include "time_mosq.h" #include "util_mosq.h" int send__pingreq(struct mosquitto *mosq) { int rc; assert(mosq); #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGREQ to %s", SAFE_PRINT(mosq->id)); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGREQ", SAFE_PRINT(mosq->id)); #endif rc = send__simple_command(mosq, CMD_PINGREQ); if(rc == MOSQ_ERR_SUCCESS){ mosq->ping_t = mosquitto_time(); } return rc; } int send__pingresp(struct mosquitto *mosq) { #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PINGRESP to %s", SAFE_PRINT(mosq->id)); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PINGRESP", SAFE_PRINT(mosq->id)); #endif return send__simple_command(mosq, CMD_PINGRESP); } int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties) { #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBACK to %s (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBACK (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #endif util__increment_receive_quota(mosq); /* We don't use Reason String or User Property yet. */ return send__command_with_mid(mosq, CMD_PUBACK, mid, false, reason_code, properties); } int send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBCOMP to %s (m%d)", SAFE_PRINT(mosq->id), mid); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBCOMP (m%d)", SAFE_PRINT(mosq->id), mid); #endif util__increment_receive_quota(mosq); /* We don't use Reason String or User Property yet. */ return send__command_with_mid(mosq, CMD_PUBCOMP, mid, false, 0, properties); } int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties) { #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREC to %s (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREC (m%d, rc%d)", SAFE_PRINT(mosq->id), mid, reason_code); #endif if(reason_code >= 0x80 && mosq->protocol == mosq_p_mqtt5){ util__increment_receive_quota(mosq); } /* We don't use Reason String or User Property yet. */ return send__command_with_mid(mosq, CMD_PUBREC, mid, false, reason_code, properties); } int send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_DEBUG, "Sending PUBREL to %s (m%d)", SAFE_PRINT(mosq->id), mid); #else log__printf(mosq, MOSQ_LOG_DEBUG, "Client %s sending PUBREL (m%d)", SAFE_PRINT(mosq->id), mid); #endif /* We don't use Reason String or User Property yet. */ return send__command_with_mid(mosq, CMD_PUBREL|2, mid, false, 0, properties); } /* For PUBACK, PUBCOMP, PUBREC, and PUBREL */ int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int rc; assert(mosq); packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->command = command; if(dup){ packet->command |= 8; } packet->remaining_length = 2; if(mosq->protocol == mosq_p_mqtt5){ if(reason_code != 0 || properties){ packet->remaining_length += 1; } if(properties){ packet->remaining_length += property__get_remaining_length(properties); } } rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } packet__write_uint16(packet, mid); if(mosq->protocol == mosq_p_mqtt5){ if(reason_code != 0 || properties){ packet__write_byte(packet, reason_code); } if(properties){ property__write_all(packet, properties, true); } } return packet__queue(mosq, packet); } /* For DISCONNECT, PINGREQ and PINGRESP */ int send__simple_command(struct mosquitto *mosq, uint8_t command) { struct mosquitto__packet *packet = NULL; int rc; assert(mosq); packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->command = command; packet->remaining_length = 0; rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } return packet__queue(mosq, packet); } mosquitto-2.0.18/lib/send_mosq.h000066400000000000000000000046141450213760600165660ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef SEND_MOSQ_H #define SEND_MOSQ_H #include "mosquitto.h" #include "property_mosq.h" int send__simple_command(struct mosquitto *mosq, uint8_t command); int send__command_with_mid(struct mosquitto *mosq, uint8_t command, uint16_t mid, bool dup, uint8_t reason_code, const mosquitto_property *properties); int 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, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval); int send__connect(struct mosquitto *mosq, uint16_t keepalive, bool clean_session, const mosquitto_property *properties); int send__disconnect(struct mosquitto *mosq, uint8_t reason_code, const mosquitto_property *properties); int send__pingreq(struct mosquitto *mosq); int send__pingresp(struct mosquitto *mosq); int send__puback(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties); int send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties); int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval); int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties); int send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties); int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties); int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties); #endif mosquitto-2.0.18/lib/send_publish.c000066400000000000000000000156241450213760600172530ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" # include "sys_tree.h" #else # define G_PUB_BYTES_SENT_INC(A) #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { #ifdef WITH_BROKER size_t len; #ifdef WITH_BRIDGE int i; struct mosquitto__bridge_topic *cur_topic; bool match; int rc; char *mapped_topic = NULL; char *topic_temp = NULL; #endif #endif assert(mosq); #if defined(WITH_BROKER) && defined(WITH_WEBSOCKETS) if(mosq->sock == INVALID_SOCKET && !mosq->wsi) return MOSQ_ERR_NO_CONN; #else if(mosq->sock == INVALID_SOCKET) return MOSQ_ERR_NO_CONN; #endif if(!mosq->retain_available){ retain = false; } #ifdef WITH_BROKER if(mosq->listener && mosq->listener->mount_point){ len = strlen(mosq->listener->mount_point); if(len < strlen(topic)){ topic += len; }else{ /* Invalid topic string. Should never happen, but silently swallow the message anyway. */ return MOSQ_ERR_SUCCESS; } } #ifdef WITH_BRIDGE if(mosq->bridge && mosq->bridge->topics && mosq->bridge->topic_remapping){ for(i=0; ibridge->topic_count; i++){ cur_topic = &mosq->bridge->topics[i]; if((cur_topic->direction == bd_both || cur_topic->direction == bd_out) && (cur_topic->remote_prefix || cur_topic->local_prefix)){ /* Topic mapping required on this topic if the message matches */ rc = mosquitto_topic_matches_sub(cur_topic->local_topic, topic, &match); if(rc){ return rc; } if(match){ mapped_topic = mosquitto__strdup(topic); if(!mapped_topic) return MOSQ_ERR_NOMEM; if(cur_topic->local_prefix){ /* This prefix needs removing. */ if(!strncmp(cur_topic->local_prefix, mapped_topic, strlen(cur_topic->local_prefix))){ topic_temp = mosquitto__strdup(mapped_topic+strlen(cur_topic->local_prefix)); mosquitto__free(mapped_topic); if(!topic_temp){ return MOSQ_ERR_NOMEM; } mapped_topic = topic_temp; } } if(cur_topic->remote_prefix){ /* This prefix needs adding. */ len = strlen(mapped_topic) + strlen(cur_topic->remote_prefix)+1; topic_temp = mosquitto__malloc(len+1); if(!topic_temp){ mosquitto__free(mapped_topic); return MOSQ_ERR_NOMEM; } snprintf(topic_temp, len, "%s%s", cur_topic->remote_prefix, mapped_topic); topic_temp[len] = '\0'; mosquitto__free(mapped_topic); mapped_topic = topic_temp; } log__printf(NULL, 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); G_PUB_BYTES_SENT_INC(payloadlen); rc = send__real_publish(mosq, mid, mapped_topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval); mosquitto__free(mapped_topic); return rc; } } } } #endif log__printf(NULL, 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); G_PUB_BYTES_SENT_INC(payloadlen); #else log__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); #endif return send__real_publish(mosq, mid, topic, payloadlen, payload, qos, retain, dup, cmsg_props, store_props, expiry_interval); } int 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, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { struct mosquitto__packet *packet = NULL; unsigned int packetlen; unsigned int proplen = 0, varbytes; int rc; mosquitto_property expiry_prop; assert(mosq); if(topic){ packetlen = 2+(unsigned int)strlen(topic) + payloadlen; }else{ packetlen = 2 + payloadlen; } if(qos > 0) packetlen += 2; /* For message id */ if(mosq->protocol == mosq_p_mqtt5){ proplen = 0; proplen += property__get_length_all(cmsg_props); proplen += property__get_length_all(store_props); if(expiry_interval > 0){ expiry_prop.next = NULL; expiry_prop.value.i32 = expiry_interval; expiry_prop.identifier = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; expiry_prop.client_generated = false; proplen += property__get_length_all(&expiry_prop); } varbytes = packet__varint_bytes(proplen); if(varbytes > 4){ /* FIXME - Properties too big, don't publish any - should remove some first really */ cmsg_props = NULL; store_props = NULL; expiry_interval = 0; }else{ packetlen += proplen + varbytes; } } if(packet__check_oversize(mosq, packetlen)){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH for %s (%d bytes)", SAFE_PRINT(mosq->id), packetlen); #else log__printf(mosq, MOSQ_LOG_NOTICE, "Dropping too large outgoing PUBLISH (%d bytes)", packetlen); #endif return MOSQ_ERR_OVERSIZE_PACKET; } packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->mid = mid; packet->command = (uint8_t)(CMD_PUBLISH | (uint8_t)((dup&0x1)<<3) | (uint8_t)(qos<<1) | retain); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } /* Variable header (topic string) */ if(topic){ packet__write_string(packet, topic, (uint16_t)strlen(topic)); }else{ packet__write_uint16(packet, 0); } if(qos > 0){ packet__write_uint16(packet, mid); } if(mosq->protocol == mosq_p_mqtt5){ packet__write_varint(packet, proplen); property__write_all(packet, cmsg_props, false); property__write_all(packet, store_props, false); if(expiry_interval > 0){ property__write_all(packet, &expiry_prop, false); } } /* Payload */ if(payloadlen){ packet__write_bytes(packet, payload, payloadlen); } return packet__queue(mosq, packet); } mosquitto-2.0.18/lib/send_subscribe.c000066400000000000000000000052171450213760600175630ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" #include "util_mosq.h" int send__subscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, int topic_qos, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; uint32_t packetlen; uint16_t local_mid; int rc; int i; size_t tlen; assert(mosq); assert(topic); packetlen = 2; if(mosq->protocol == mosq_p_mqtt5){ packetlen += property__get_remaining_length(properties); } for(i=0; i UINT16_MAX){ return MOSQ_ERR_INVAL; } packetlen += 2U+(uint16_t)tlen + 1U; } packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->command = CMD_SUBSCRIBE | (1<<1); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } /* Variable header */ local_mid = mosquitto__mid_generate(mosq); if(mid) *mid = (int)local_mid; packet__write_uint16(packet, local_mid); if(mosq->protocol == mosq_p_mqtt5){ property__write_all(packet, properties, true); } /* Payload */ for(i=0; iid), local_mid, topic[0], topic_qos&0x03, topic_qos&0xFC); # endif #else for(i=0; iid), local_mid, topic[i], topic_qos&0x03, topic_qos&0xFC); } #endif return packet__queue(mosq, packet); } mosquitto-2.0.18/lib/send_unsubscribe.c000066400000000000000000000050071450213760600201230ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "logging_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" #include "util_mosq.h" int send__unsubscribe(struct mosquitto *mosq, int *mid, int topic_count, char *const *const topic, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; uint32_t packetlen; uint16_t local_mid; int rc; int i; size_t tlen; assert(mosq); assert(topic); packetlen = 2; for(i=0; i UINT16_MAX){ return MOSQ_ERR_INVAL; } packetlen += 2U+(uint16_t)tlen; } packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; if(mosq->protocol == mosq_p_mqtt5){ packetlen += property__get_remaining_length(properties); } packet->command = CMD_UNSUBSCRIBE | (1<<1); packet->remaining_length = packetlen; rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } /* Variable header */ local_mid = mosquitto__mid_generate(mosq); if(mid) *mid = (int)local_mid; packet__write_uint16(packet, local_mid); if(mosq->protocol == mosq_p_mqtt5){ /* We don't use User Property yet. */ property__write_all(packet, properties, true); } /* Payload */ for(i=0; iid), local_mid, topic[i]); } # endif #else for(i=0; iid), local_mid, topic[i]); } #endif return packet__queue(mosq, packet); } mosquitto-2.0.18/lib/socks_mosq.c000066400000000000000000000323471450213760600167560ustar00rootroot00000000000000/* Copyright (c) 2014-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WIN32 # include #elif defined(__QNX__) # include # include # include #else # include #endif #if defined(__FreeBSD__) || defined(__OpenBSD__) # include # include #endif #include "mosquitto_internal.h" #include "memory_mosq.h" #include "net_mosq.h" #include "packet_mosq.h" #include "send_mosq.h" #include "socks_mosq.h" #include "util_mosq.h" #define SOCKS_AUTH_NONE 0x00U #define SOCKS_AUTH_GSS 0x01U #define SOCKS_AUTH_USERPASS 0x02U #define SOCKS_AUTH_NO_ACCEPTABLE 0xFFU #define SOCKS_ATYPE_IP_V4 1U /* four bytes */ #define SOCKS_ATYPE_DOMAINNAME 3U /* one byte length, followed by fqdn no null, 256 max chars */ #define SOCKS_ATYPE_IP_V6 4U /* 16 bytes */ #define SOCKS_REPLY_SUCCEEDED 0x00U #define SOCKS_REPLY_GENERAL_FAILURE 0x01U #define SOCKS_REPLY_CONNECTION_NOT_ALLOWED 0x02U #define SOCKS_REPLY_NETWORK_UNREACHABLE 0x03U #define SOCKS_REPLY_HOST_UNREACHABLE 0x04U #define SOCKS_REPLY_CONNECTION_REFUSED 0x05U #define SOCKS_REPLY_TTL_EXPIRED 0x06U #define SOCKS_REPLY_COMMAND_NOT_SUPPORTED 0x07U #define SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED 0x08U int mosquitto_socks5_set(struct mosquitto *mosq, const char *host, int port, const char *username, const char *password) { #ifdef WITH_SOCKS if(!mosq) return MOSQ_ERR_INVAL; if(!host || strlen(host) > 256) return MOSQ_ERR_INVAL; if(port < 1 || port > UINT16_MAX) return MOSQ_ERR_INVAL; mosquitto__free(mosq->socks5_host); mosq->socks5_host = NULL; mosq->socks5_host = mosquitto__strdup(host); if(!mosq->socks5_host){ return MOSQ_ERR_NOMEM; } mosq->socks5_port = (uint16_t)port; mosquitto__free(mosq->socks5_username); mosq->socks5_username = NULL; mosquitto__free(mosq->socks5_password); mosq->socks5_password = NULL; if(username){ if(strlen(username) > UINT8_MAX){ return MOSQ_ERR_INVAL; } mosq->socks5_username = mosquitto__strdup(username); if(!mosq->socks5_username){ return MOSQ_ERR_NOMEM; } if(password){ if(strlen(password) > UINT8_MAX){ return MOSQ_ERR_INVAL; } mosq->socks5_password = mosquitto__strdup(password); if(!mosq->socks5_password){ mosquitto__free(mosq->socks5_username); return MOSQ_ERR_NOMEM; } } } return MOSQ_ERR_SUCCESS; #else UNUSED(mosq); UNUSED(host); UNUSED(port); UNUSED(username); UNUSED(password); return MOSQ_ERR_NOT_SUPPORTED; #endif } #ifdef WITH_SOCKS int socks5__send(struct mosquitto *mosq) { struct mosquitto__packet *packet; size_t slen; uint8_t ulen, plen; struct in_addr addr_ipv4; struct in6_addr addr_ipv6; int ipv4_pton_result; int ipv6_pton_result; enum mosquitto_client_state state; state = mosquitto__get_state(mosq); if(state == mosq_cs_socks5_new){ packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; if(mosq->socks5_username){ packet->packet_length = 4; }else{ packet->packet_length = 3; } packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); packet->payload[0] = 0x05; if(mosq->socks5_username){ packet->payload[1] = 2; packet->payload[2] = SOCKS_AUTH_NONE; packet->payload[3] = SOCKS_AUTH_USERPASS; }else{ packet->payload[1] = 1; packet->payload[2] = SOCKS_AUTH_NONE; } mosquitto__set_state(mosq, mosq_cs_socks5_start); mosq->in_packet.pos = 0; mosq->in_packet.packet_length = 2; mosq->in_packet.to_process = 2; mosq->in_packet.payload = mosquitto__malloc(sizeof(uint8_t)*2); if(!mosq->in_packet.payload){ mosquitto__free(packet->payload); mosquitto__free(packet); return MOSQ_ERR_NOMEM; } return packet__queue(mosq, packet); }else if(state == mosq_cs_socks5_auth_ok){ packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; ipv4_pton_result = inet_pton(AF_INET, mosq->host, &addr_ipv4); ipv6_pton_result = inet_pton(AF_INET6, mosq->host, &addr_ipv6); if(ipv4_pton_result == 1){ packet->packet_length = 10; packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); if(!packet->payload){ mosquitto__free(packet); return MOSQ_ERR_NOMEM; } packet->payload[3] = SOCKS_ATYPE_IP_V4; memcpy(&(packet->payload[4]), (const void*)&addr_ipv4, 4); packet->payload[4+4] = MOSQ_MSB(mosq->port); packet->payload[4+4+1] = MOSQ_LSB(mosq->port); }else if(ipv6_pton_result == 1){ packet->packet_length = 22; packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); if(!packet->payload){ mosquitto__free(packet); return MOSQ_ERR_NOMEM; } packet->payload[3] = SOCKS_ATYPE_IP_V6; memcpy(&(packet->payload[4]), (const void*)&addr_ipv6, 16); packet->payload[4+16] = MOSQ_MSB(mosq->port); packet->payload[4+16+1] = MOSQ_LSB(mosq->port); }else{ slen = strlen(mosq->host); if(slen > UCHAR_MAX){ mosquitto__free(packet); return MOSQ_ERR_NOMEM; } packet->packet_length = 7U + (uint32_t)slen; packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); if(!packet->payload){ mosquitto__free(packet); return MOSQ_ERR_NOMEM; } packet->payload[3] = SOCKS_ATYPE_DOMAINNAME; packet->payload[4] = (uint8_t)slen; memcpy(&(packet->payload[5]), mosq->host, slen); packet->payload[5+slen] = MOSQ_MSB(mosq->port); packet->payload[6+slen] = MOSQ_LSB(mosq->port); } packet->payload[0] = 0x05; packet->payload[1] = 0x01; packet->payload[2] = 0x00; mosquitto__set_state(mosq, mosq_cs_socks5_request); mosq->in_packet.pos = 0; mosq->in_packet.packet_length = 5; mosq->in_packet.to_process = 5; mosq->in_packet.payload = mosquitto__malloc(sizeof(uint8_t)*5); if(!mosq->in_packet.payload){ mosquitto__free(packet->payload); mosquitto__free(packet); return MOSQ_ERR_NOMEM; } return packet__queue(mosq, packet); }else if(state == mosq_cs_socks5_send_userpass){ packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; ulen = (uint8_t)strlen(mosq->socks5_username); plen = (uint8_t)strlen(mosq->socks5_password); packet->packet_length = 3U + ulen + plen; packet->payload = mosquitto__malloc(sizeof(uint8_t)*packet->packet_length); packet->payload[0] = 0x01; packet->payload[1] = ulen; memcpy(&(packet->payload[2]), mosq->socks5_username, ulen); packet->payload[2+ulen] = plen; memcpy(&(packet->payload[3+ulen]), mosq->socks5_password, plen); mosquitto__set_state(mosq, mosq_cs_socks5_userpass_reply); mosq->in_packet.pos = 0; mosq->in_packet.packet_length = 2; mosq->in_packet.to_process = 2; mosq->in_packet.payload = mosquitto__malloc(sizeof(uint8_t)*2); if(!mosq->in_packet.payload){ mosquitto__free(packet->payload); mosquitto__free(packet); return MOSQ_ERR_NOMEM; } return packet__queue(mosq, packet); } return MOSQ_ERR_SUCCESS; } int socks5__read(struct mosquitto *mosq) { ssize_t len; uint8_t *payload; uint8_t i; enum mosquitto_client_state state; state = mosquitto__get_state(mosq); if(state == mosq_cs_socks5_start){ while(mosq->in_packet.to_process > 0){ len = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(len > 0){ mosq->in_packet.pos += (uint32_t)len; mosq->in_packet.to_process -= (uint32_t)len; }else{ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ packet__cleanup(&mosq->in_packet); switch(errno){ case 0: return MOSQ_ERR_PROXY; case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; default: return MOSQ_ERR_ERRNO; } } } } if(mosq->in_packet.payload[0] != 5){ packet__cleanup(&mosq->in_packet); return MOSQ_ERR_PROXY; } switch(mosq->in_packet.payload[1]){ case SOCKS_AUTH_NONE: packet__cleanup(&mosq->in_packet); mosquitto__set_state(mosq, mosq_cs_socks5_auth_ok); return socks5__send(mosq); case SOCKS_AUTH_USERPASS: packet__cleanup(&mosq->in_packet); mosquitto__set_state(mosq, mosq_cs_socks5_send_userpass); return socks5__send(mosq); default: packet__cleanup(&mosq->in_packet); return MOSQ_ERR_AUTH; } }else if(state == mosq_cs_socks5_userpass_reply){ while(mosq->in_packet.to_process > 0){ len = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(len > 0){ mosq->in_packet.pos += (uint32_t)len; mosq->in_packet.to_process -= (uint32_t)len; }else{ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ packet__cleanup(&mosq->in_packet); switch(errno){ case 0: return MOSQ_ERR_PROXY; case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; default: return MOSQ_ERR_ERRNO; } } } } if(mosq->in_packet.payload[0] != 1){ packet__cleanup(&mosq->in_packet); return MOSQ_ERR_PROXY; } if(mosq->in_packet.payload[1] == 0){ packet__cleanup(&mosq->in_packet); mosquitto__set_state(mosq, mosq_cs_socks5_auth_ok); return socks5__send(mosq); }else{ i = mosq->in_packet.payload[1]; packet__cleanup(&mosq->in_packet); switch(i){ case SOCKS_REPLY_CONNECTION_NOT_ALLOWED: return MOSQ_ERR_AUTH; case SOCKS_REPLY_NETWORK_UNREACHABLE: case SOCKS_REPLY_HOST_UNREACHABLE: case SOCKS_REPLY_CONNECTION_REFUSED: return MOSQ_ERR_NO_CONN; case SOCKS_REPLY_GENERAL_FAILURE: case SOCKS_REPLY_TTL_EXPIRED: case SOCKS_REPLY_COMMAND_NOT_SUPPORTED: case SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: return MOSQ_ERR_PROXY; default: return MOSQ_ERR_INVAL; } return MOSQ_ERR_PROXY; } }else if(state == mosq_cs_socks5_request){ while(mosq->in_packet.to_process > 0){ len = net__read(mosq, &(mosq->in_packet.payload[mosq->in_packet.pos]), mosq->in_packet.to_process); if(len > 0){ mosq->in_packet.pos += (uint32_t)len; mosq->in_packet.to_process -= (uint32_t)len; }else{ #ifdef WIN32 errno = WSAGetLastError(); #endif if(errno == EAGAIN || errno == COMPAT_EWOULDBLOCK){ return MOSQ_ERR_SUCCESS; }else{ packet__cleanup(&mosq->in_packet); switch(errno){ case 0: return MOSQ_ERR_PROXY; case COMPAT_ECONNRESET: return MOSQ_ERR_CONN_LOST; default: return MOSQ_ERR_ERRNO; } } } } if(mosq->in_packet.packet_length == 5){ /* First part of the packet has been received, we now know what else to expect. */ if(mosq->in_packet.payload[3] == SOCKS_ATYPE_IP_V4){ mosq->in_packet.to_process += 4+2-1; /* 4 bytes IPv4, 2 bytes port, -1 byte because we've already read the first byte */ mosq->in_packet.packet_length += 4+2-1; }else if(mosq->in_packet.payload[3] == SOCKS_ATYPE_IP_V6){ mosq->in_packet.to_process += 16+2-1; /* 16 bytes IPv6, 2 bytes port, -1 byte because we've already read the first byte */ mosq->in_packet.packet_length += 16+2-1; }else if(mosq->in_packet.payload[3] == SOCKS_ATYPE_DOMAINNAME){ if(mosq->in_packet.payload[4] > 0){ mosq->in_packet.to_process += mosq->in_packet.payload[4]; mosq->in_packet.packet_length += mosq->in_packet.payload[4]; } }else{ packet__cleanup(&mosq->in_packet); return MOSQ_ERR_PROTOCOL; } payload = mosquitto__realloc(mosq->in_packet.payload, mosq->in_packet.packet_length); if(payload){ mosq->in_packet.payload = payload; }else{ packet__cleanup(&mosq->in_packet); return MOSQ_ERR_NOMEM; } return MOSQ_ERR_SUCCESS; } /* Entire packet is now read. */ if(mosq->in_packet.payload[0] != 5){ packet__cleanup(&mosq->in_packet); return MOSQ_ERR_PROXY; } if(mosq->in_packet.payload[1] == 0){ /* Auth passed */ packet__cleanup(&mosq->in_packet); mosquitto__set_state(mosq, mosq_cs_new); if(mosq->socks5_host){ int rc = net__socket_connect_step3(mosq, mosq->host); if(rc) return rc; } return send__connect(mosq, mosq->keepalive, mosq->clean_start, NULL); }else{ i = mosq->in_packet.payload[1]; packet__cleanup(&mosq->in_packet); mosquitto__set_state(mosq, mosq_cs_socks5_new); switch(i){ case SOCKS_REPLY_CONNECTION_NOT_ALLOWED: return MOSQ_ERR_AUTH; case SOCKS_REPLY_NETWORK_UNREACHABLE: case SOCKS_REPLY_HOST_UNREACHABLE: case SOCKS_REPLY_CONNECTION_REFUSED: return MOSQ_ERR_NO_CONN; case SOCKS_REPLY_GENERAL_FAILURE: case SOCKS_REPLY_TTL_EXPIRED: case SOCKS_REPLY_COMMAND_NOT_SUPPORTED: case SOCKS_REPLY_ADDRESS_TYPE_NOT_SUPPORTED: return MOSQ_ERR_PROXY; default: return MOSQ_ERR_INVAL; } } }else{ return packet__read(mosq); } return MOSQ_ERR_SUCCESS; } #endif mosquitto-2.0.18/lib/socks_mosq.h000066400000000000000000000013201450213760600167460ustar00rootroot00000000000000/* Copyright (c) 2014-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef SOCKS_MOSQ_H #define SOCKS_MOSQ_H int socks5__send(struct mosquitto *mosq); int socks5__read(struct mosquitto *mosq); #endif mosquitto-2.0.18/lib/srv_mosq.c000066400000000000000000000057321450213760600164440ustar00rootroot00000000000000/* Copyright (c) 2013-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WITH_SRV # include # include # include # include #endif #include "logging_mosq.h" #include "memory_mosq.h" #include "mosquitto_internal.h" #include "mosquitto.h" #include "util_mosq.h" #ifdef WITH_SRV static void srv_callback(void *arg, int status, int timeouts, unsigned char *abuf, int alen) { struct mosquitto *mosq = arg; struct ares_srv_reply *reply = NULL; UNUSED(timeouts); if(status == ARES_SUCCESS){ status = ares_parse_srv_reply(abuf, alen, &reply); if(status == ARES_SUCCESS){ // FIXME - choose which answer to use based on rfc2782 page 3. */ mosquitto_connect(mosq, reply->host, reply->port, mosq->keepalive); } }else{ log__printf(mosq, MOSQ_LOG_ERR, "Error: SRV lookup failed (%d).", status); /* FIXME - calling on_disconnect here isn't correct. */ pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, MOSQ_ERR_LOOKUP); mosq->in_callback = false; } if(mosq->on_disconnect_v5){ mosq->in_callback = true; mosq->on_disconnect_v5(mosq, mosq->userdata, MOSQ_ERR_LOOKUP, NULL); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); } } #endif int mosquitto_connect_srv(struct mosquitto *mosq, const char *host, int keepalive, const char *bind_address) { #ifdef WITH_SRV char *h; int rc; if(!mosq) return MOSQ_ERR_INVAL; UNUSED(bind_address); if(keepalive < 0 || keepalive > UINT16_MAX){ return MOSQ_ERR_INVAL; } rc = ares_init(&mosq->achan); if(rc != ARES_SUCCESS){ return MOSQ_ERR_UNKNOWN; } if(!host){ // get local domain }else{ #ifdef WITH_TLS if(mosq->tls_cafile || mosq->tls_capath || mosq->tls_psk){ h = mosquitto__malloc(strlen(host) + strlen("_secure-mqtt._tcp.") + 1); if(!h) return MOSQ_ERR_NOMEM; sprintf(h, "_secure-mqtt._tcp.%s", host); }else{ #endif h = mosquitto__malloc(strlen(host) + strlen("_mqtt._tcp.") + 1); if(!h) return MOSQ_ERR_NOMEM; sprintf(h, "_mqtt._tcp.%s", host); #ifdef WITH_TLS } #endif ares_search(mosq->achan, h, ns_c_in, ns_t_srv, srv_callback, mosq); mosquitto__free(h); } mosquitto__set_state(mosq, mosq_cs_connect_srv); mosq->keepalive = (uint16_t)keepalive; return MOSQ_ERR_SUCCESS; #else UNUSED(mosq); UNUSED(host); UNUSED(keepalive); UNUSED(bind_address); return MOSQ_ERR_NOT_SUPPORTED; #endif } mosquitto-2.0.18/lib/strings_mosq.c000066400000000000000000000161461450213760600173240ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #ifndef WIN32 # include #endif #include "mosquitto.h" #include "mqtt_protocol.h" const char *mosquitto_strerror(int mosq_errno) { switch(mosq_errno){ case MOSQ_ERR_AUTH_CONTINUE: return "Continue with authentication."; case MOSQ_ERR_NO_SUBSCRIBERS: return "No subscribers."; case MOSQ_ERR_SUB_EXISTS: return "Subscription already exists."; case MOSQ_ERR_CONN_PENDING: return "Connection pending."; case MOSQ_ERR_SUCCESS: return "No error."; case MOSQ_ERR_NOMEM: return "Out of memory."; case MOSQ_ERR_PROTOCOL: return "A network protocol error occurred when communicating with the broker."; case MOSQ_ERR_INVAL: return "Invalid arguments provided."; case MOSQ_ERR_NO_CONN: return "The client is not currently connected."; case MOSQ_ERR_CONN_REFUSED: return "The connection was refused."; case MOSQ_ERR_NOT_FOUND: return "Message not found (internal error)."; case MOSQ_ERR_CONN_LOST: return "The connection was lost."; case MOSQ_ERR_TLS: return "A TLS error occurred."; case MOSQ_ERR_PAYLOAD_SIZE: return "Payload too large."; case MOSQ_ERR_NOT_SUPPORTED: return "This feature is not supported."; case MOSQ_ERR_AUTH: return "Authorisation failed."; case MOSQ_ERR_ACL_DENIED: return "Access denied by ACL."; case MOSQ_ERR_UNKNOWN: return "Unknown error."; case MOSQ_ERR_ERRNO: return strerror(errno); case MOSQ_ERR_EAI: return "Lookup error."; case MOSQ_ERR_PROXY: return "Proxy error."; case MOSQ_ERR_MALFORMED_UTF8: return "Malformed UTF-8"; case MOSQ_ERR_KEEPALIVE: return "Keepalive exceeded"; case MOSQ_ERR_LOOKUP: return "DNS Lookup failed"; case MOSQ_ERR_DUPLICATE_PROPERTY: return "Duplicate property in property list"; case MOSQ_ERR_TLS_HANDSHAKE: return "TLS handshake failed."; case MOSQ_ERR_QOS_NOT_SUPPORTED: return "Requested QoS not supported on server."; case MOSQ_ERR_OVERSIZE_PACKET: return "Packet larger than supported by the server."; case MOSQ_ERR_OCSP: return "OCSP error."; default: return "Unknown error."; } } const char *mosquitto_connack_string(int connack_code) { switch(connack_code){ case 0: return "Connection Accepted."; case 1: return "Connection Refused: unacceptable protocol version."; case 2: return "Connection Refused: identifier rejected."; case 3: return "Connection Refused: broker unavailable."; case 4: return "Connection Refused: bad user name or password."; case 5: return "Connection Refused: not authorised."; default: return "Connection Refused: unknown reason."; } } const char *mosquitto_reason_string(int reason_code) { switch(reason_code){ case MQTT_RC_SUCCESS: return "Success"; case MQTT_RC_GRANTED_QOS1: return "Granted QoS 1"; case MQTT_RC_GRANTED_QOS2: return "Granted QoS 2"; case MQTT_RC_DISCONNECT_WITH_WILL_MSG: return "Disconnect with Will Message"; case MQTT_RC_NO_MATCHING_SUBSCRIBERS: return "No matching subscribers"; case MQTT_RC_NO_SUBSCRIPTION_EXISTED: return "No subscription existed"; case MQTT_RC_CONTINUE_AUTHENTICATION: return "Continue authentication"; case MQTT_RC_REAUTHENTICATE: return "Re-authenticate"; case MQTT_RC_UNSPECIFIED: return "Unspecified error"; case MQTT_RC_MALFORMED_PACKET: return "Malformed Packet"; case MQTT_RC_PROTOCOL_ERROR: return "Protocol Error"; case MQTT_RC_IMPLEMENTATION_SPECIFIC: return "Implementation specific error"; case MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION: return "Unsupported Protocol Version"; case MQTT_RC_CLIENTID_NOT_VALID: return "Client Identifier not valid"; case MQTT_RC_BAD_USERNAME_OR_PASSWORD: return "Bad User Name or Password"; case MQTT_RC_NOT_AUTHORIZED: return "Not authorized"; case MQTT_RC_SERVER_UNAVAILABLE: return "Server unavailable"; case MQTT_RC_SERVER_BUSY: return "Server busy"; case MQTT_RC_BANNED: return "Banned"; case MQTT_RC_SERVER_SHUTTING_DOWN: return "Server shutting down"; case MQTT_RC_BAD_AUTHENTICATION_METHOD: return "Bad authentication method"; case MQTT_RC_KEEP_ALIVE_TIMEOUT: return "Keep Alive timeout"; case MQTT_RC_SESSION_TAKEN_OVER: return "Session taken over"; case MQTT_RC_TOPIC_FILTER_INVALID: return "Topic Filter invalid"; case MQTT_RC_TOPIC_NAME_INVALID: return "Topic Name invalid"; case MQTT_RC_PACKET_ID_IN_USE: return "Packet Identifier in use"; case MQTT_RC_PACKET_ID_NOT_FOUND: return "Packet Identifier not found"; case MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED: return "Receive Maximum exceeded"; case MQTT_RC_TOPIC_ALIAS_INVALID: return "Topic Alias invalid"; case MQTT_RC_PACKET_TOO_LARGE: return "Packet too large"; case MQTT_RC_MESSAGE_RATE_TOO_HIGH: return "Message rate too high"; case MQTT_RC_QUOTA_EXCEEDED: return "Quota exceeded"; case MQTT_RC_ADMINISTRATIVE_ACTION: return "Administrative action"; case MQTT_RC_PAYLOAD_FORMAT_INVALID: return "Payload format invalid"; case MQTT_RC_RETAIN_NOT_SUPPORTED: return "Retain not supported"; case MQTT_RC_QOS_NOT_SUPPORTED: return "QoS not supported"; case MQTT_RC_USE_ANOTHER_SERVER: return "Use another server"; case MQTT_RC_SERVER_MOVED: return "Server moved"; case MQTT_RC_SHARED_SUBS_NOT_SUPPORTED: return "Shared Subscriptions not supported"; case MQTT_RC_CONNECTION_RATE_EXCEEDED: return "Connection rate exceeded"; case MQTT_RC_MAXIMUM_CONNECT_TIME: return "Maximum connect time"; case MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED: return "Subscription identifiers not supported"; case MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED: return "Wildcard Subscriptions not supported"; default: return "Unknown reason"; } } int mosquitto_string_to_command(const char *str, int *cmd) { if(!strcasecmp(str, "connect")){ *cmd = CMD_CONNECT; }else if(!strcasecmp(str, "connack")){ *cmd = CMD_CONNACK; }else if(!strcasecmp(str, "publish")){ *cmd = CMD_PUBLISH; }else if(!strcasecmp(str, "puback")){ *cmd = CMD_PUBACK; }else if(!strcasecmp(str, "pubrec")){ *cmd = CMD_PUBREC; }else if(!strcasecmp(str, "pubrel")){ *cmd = CMD_PUBREL; }else if(!strcasecmp(str, "pubcomp")){ *cmd = CMD_PUBCOMP; }else if(!strcasecmp(str, "subscribe")){ *cmd = CMD_SUBSCRIBE; }else if(!strcasecmp(str, "unsubscribe")){ *cmd = CMD_UNSUBSCRIBE; }else if(!strcasecmp(str, "disconnect")){ *cmd = CMD_DISCONNECT; }else if(!strcasecmp(str, "auth")){ *cmd = CMD_AUTH; }else if(!strcasecmp(str, "will")){ *cmd = CMD_WILL; }else{ return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/thread_mosq.c000066400000000000000000000064371450213760600171040ustar00rootroot00000000000000/* Copyright (c) 2011-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifndef WIN32 #include #endif #if defined(WITH_THREADING) #if defined(__linux__) || defined(__NetBSD__) # include #elif defined(__FreeBSD__) || defined(__OpenBSD__) # include #endif #endif #include "mosquitto_internal.h" #include "net_mosq.h" #include "util_mosq.h" void *mosquitto__thread_main(void *obj); int mosquitto_loop_start(struct mosquitto *mosq) { #if defined(WITH_THREADING) if(!mosq || mosq->threaded != mosq_ts_none) return MOSQ_ERR_INVAL; mosq->threaded = mosq_ts_self; if(!pthread_create(&mosq->thread_id, NULL, mosquitto__thread_main, mosq)){ #if defined(__linux__) pthread_setname_np(mosq->thread_id, "mosquitto loop"); #elif defined(__NetBSD__) pthread_setname_np(mosq->thread_id, "%s", "mosquitto loop"); #elif defined(__FreeBSD__) || defined(__OpenBSD__) pthread_set_name_np(mosq->thread_id, "mosquitto loop"); #endif return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ERRNO; } #else UNUSED(mosq); return MOSQ_ERR_NOT_SUPPORTED; #endif } int mosquitto_loop_stop(struct mosquitto *mosq, bool force) { #if defined(WITH_THREADING) # ifndef WITH_BROKER char sockpair_data = 0; # endif if(!mosq || mosq->threaded != mosq_ts_self) return MOSQ_ERR_INVAL; /* Write a single byte to sockpairW (connected to sockpairR) to break out * of select() if in threaded mode. */ if(mosq->sockpairW != INVALID_SOCKET){ #ifndef WIN32 if(write(mosq->sockpairW, &sockpair_data, 1)){ } #else send(mosq->sockpairW, &sockpair_data, 1, 0); #endif } #ifdef HAVE_PTHREAD_CANCEL if(force){ pthread_cancel(mosq->thread_id); } #endif pthread_join(mosq->thread_id, NULL); mosq->thread_id = pthread_self(); mosq->threaded = mosq_ts_none; return MOSQ_ERR_SUCCESS; #else UNUSED(mosq); UNUSED(force); return MOSQ_ERR_NOT_SUPPORTED; #endif } #ifdef WITH_THREADING void *mosquitto__thread_main(void *obj) { struct mosquitto *mosq = obj; #ifndef WIN32 struct timespec ts; ts.tv_sec = 0; ts.tv_nsec = 10000000; #endif if(!mosq) return NULL; do{ if(mosquitto__get_state(mosq) == mosq_cs_new){ #ifdef WIN32 Sleep(10); #else nanosleep(&ts, NULL); #endif }else{ break; } }while(1); if(!mosq->keepalive){ /* Sleep for a day if keepalive disabled. */ mosquitto_loop_forever(mosq, 1000*86400, 1); }else{ /* Sleep for our keepalive value. publish() etc. will wake us up. */ mosquitto_loop_forever(mosq, mosq->keepalive*1000, 1); } if(mosq->threaded == mosq_ts_self){ mosq->threaded = mosq_ts_none; } return obj; } #endif int mosquitto_threaded_set(struct mosquitto *mosq, bool threaded) { if(!mosq) return MOSQ_ERR_INVAL; if(threaded){ mosq->threaded = mosq_ts_external; }else{ mosq->threaded = mosq_ts_none; } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/time_mosq.c000066400000000000000000000027051450213760600165650ustar00rootroot00000000000000/* Copyright (c) 2013-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef __APPLE__ #include #include #endif #ifdef WIN32 #if !(defined(_MSC_VER) && _MSC_VER <= 1500) # define _WIN32_WINNT _WIN32_WINNT_VISTA #endif # include #else # include #endif #include #include "mosquitto.h" #include "time_mosq.h" time_t mosquitto_time(void) { #ifdef WIN32 return GetTickCount64()/1000; #elif _POSIX_TIMERS>0 && defined(_POSIX_MONOTONIC_CLOCK) struct timespec tp; #ifdef CLOCK_BOOTTIME clock_gettime(CLOCK_BOOTTIME, &tp); #else clock_gettime(CLOCK_MONOTONIC, &tp); #endif return tp.tv_sec; #elif defined(__APPLE__) static mach_timebase_info_data_t tb; uint64_t ticks; uint64_t sec; ticks = mach_absolute_time(); if(tb.denom == 0){ mach_timebase_info(&tb); } sec = ticks*tb.numer/tb.denom/1000000000; return (time_t)sec; #else return time(NULL); #endif } mosquitto-2.0.18/lib/time_mosq.h000066400000000000000000000012271450213760600165700ustar00rootroot00000000000000/* Copyright (c) 2013-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef TIME_MOSQ_H #define TIME_MOSQ_H time_t mosquitto_time(void); #endif mosquitto-2.0.18/lib/tls_mosq.c000066400000000000000000000120441450213760600164260ustar00rootroot00000000000000/* Copyright (c) 2013-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WITH_TLS #ifdef WIN32 # include # include #else # include # include # include #endif #include #include #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto_internal.h" #include "logging_mosq.h" #include "tls_mosq.h" extern int tls_ex_index_mosq; int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx) { /* Preverify should have already checked expiry, revocation. * We need to verify the hostname. */ struct mosquitto *mosq; SSL *ssl; X509 *cert; /* Always reject if preverify_ok has failed. */ if(!preverify_ok) return 0; ssl = X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx()); mosq = SSL_get_ex_data(ssl, tls_ex_index_mosq); if(!mosq) return 0; if(mosq->tls_insecure == false #ifndef WITH_BROKER && mosq->port != 0 /* no hostname checking for unix sockets */ #endif ){ if(X509_STORE_CTX_get_error_depth(ctx) == 0){ /* FIXME - use X509_check_host() etc. for sufficiently new openssl (>=1.1.x) */ cert = X509_STORE_CTX_get_current_cert(ctx); /* This is the peer certificate, all others are upwards in the chain. */ #if defined(WITH_BROKER) preverify_ok = mosquitto__verify_certificate_hostname(cert, mosq->bridge->addresses[mosq->bridge->cur_address].address); #else preverify_ok = mosquitto__verify_certificate_hostname(cert, mosq->host); #endif if (preverify_ok != 1) { log__printf(mosq, MOSQ_LOG_ERR, "Error: host name verification failed."); } return preverify_ok; }else{ return preverify_ok; } }else{ return preverify_ok; } } static int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname) { size_t i; size_t len; if(!certname || !hostname){ return 1; } if(certname[0] == '*'){ if(certname[1] != '.'){ return 1; } certname += 2; len = strlen(hostname); for(i=0; itype == GEN_DNS){ #if OPENSSL_VERSION_NUMBER < 0x10100000L data = ASN1_STRING_data(nval->d.dNSName); #else data = ASN1_STRING_get0_data(nval->d.dNSName); #endif if(data && !mosquitto__cmp_hostname_wildcard((char *)data, hostname)){ sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); return 1; } have_san_dns = true; }else if(nval->type == GEN_IPADD){ #if OPENSSL_VERSION_NUMBER < 0x10100000L data = ASN1_STRING_data(nval->d.iPAddress); #else data = ASN1_STRING_get0_data(nval->d.iPAddress); #endif if(nval->d.iPAddress->length == 4 && ipv4_ok){ if(!memcmp(ipv4_addr, data, 4)){ sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); return 1; } }else if(nval->d.iPAddress->length == 16 && ipv6_ok){ if(!memcmp(ipv6_addr, data, 16)){ sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); return 1; } } } } sk_GENERAL_NAME_pop_free(san, GENERAL_NAME_free); if(have_san_dns){ /* Only check CN if subjectAltName DNS entry does not exist. */ return 0; } } subj = X509_get_subject_name(cert); if(X509_NAME_get_text_by_NID(subj, NID_commonName, name, sizeof(name)) > 0){ name[sizeof(name) - 1] = '\0'; if (!mosquitto__cmp_hostname_wildcard(name, hostname)) return 1; } return 0; } #endif mosquitto-2.0.18/lib/tls_mosq.h000066400000000000000000000017651450213760600164430ustar00rootroot00000000000000/* Copyright (c) 2013-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef TLS_MOSQ_H #define TLS_MOSQ_H #ifdef WITH_TLS # define SSL_DATA_PENDING(A) ((A)->ssl && SSL_pending((A)->ssl)) #else # define SSL_DATA_PENDING(A) 0 #endif #ifdef WITH_TLS #include #include int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx); int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname); #endif /* WITH_TLS */ #endif mosquitto-2.0.18/lib/utf8_mosq.c000066400000000000000000000057401450213760600165170ustar00rootroot00000000000000/* Copyright (c) 2016-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation. */ #include "config.h" #include #include "mosquitto.h" int mosquitto_validate_utf8(const char *str, int len) { int i; int j; int codelen; int codepoint; const unsigned char *ustr = (const unsigned char *)str; if(!str) return MOSQ_ERR_INVAL; if(len < 0 || len > 65536) return MOSQ_ERR_INVAL; for(i=0; i 0xF4){ /* Invalid, this would produce values > 0x10FFFF. */ return MOSQ_ERR_MALFORMED_UTF8; } codelen = 4; codepoint = (ustr[i] & 0x07); }else{ /* Unexpected continuation byte. */ return MOSQ_ERR_MALFORMED_UTF8; } /* Reconstruct full code point */ if(i == len-codelen+1){ /* Not enough data */ return MOSQ_ERR_MALFORMED_UTF8; } for(j=0; j= 0xD800 && codepoint <= 0xDFFF){ return MOSQ_ERR_MALFORMED_UTF8; } /* Check for overlong or out of range encodings */ /* Checking codelen == 2 isn't necessary here, because it is already * covered above in the C0 and C1 checks. * if(codelen == 2 && codepoint < 0x0080){ * return MOSQ_ERR_MALFORMED_UTF8; * }else */ if(codelen == 3 && codepoint < 0x0800){ return MOSQ_ERR_MALFORMED_UTF8; }else if(codelen == 4 && (codepoint < 0x10000 || codepoint > 0x10FFFF)){ return MOSQ_ERR_MALFORMED_UTF8; } /* Check for non-characters */ if(codepoint >= 0xFDD0 && codepoint <= 0xFDEF){ return MOSQ_ERR_MALFORMED_UTF8; } if((codepoint & 0xFFFF) == 0xFFFE || (codepoint & 0xFFFF) == 0xFFFF){ return MOSQ_ERR_MALFORMED_UTF8; } /* Check for control characters */ if(codepoint <= 0x001F || (codepoint >= 0x007F && codepoint <= 0x009F)){ return MOSQ_ERR_MALFORMED_UTF8; } } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/util_mosq.c000066400000000000000000000163601450213760600166060ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #ifdef WIN32 # include # include # include # include #else # include #endif #if !defined(WITH_TLS) && defined(__linux__) && defined(__GLIBC__) # if __GLIBC_PREREQ(2, 25) # include # define HAVE_GETRANDOM 1 # endif #endif #ifdef WITH_TLS # include # include #endif #ifdef WITH_BROKER #include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "memory_mosq.h" #include "net_mosq.h" #include "send_mosq.h" #include "time_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" #ifdef WITH_WEBSOCKETS #include #endif int mosquitto__check_keepalive(struct mosquitto *mosq) { time_t next_msg_out; time_t last_msg_in; time_t now; #ifndef WITH_BROKER int rc; #endif enum mosquitto_client_state state; assert(mosq); #ifdef WITH_BROKER now = db.now_s; #else now = mosquitto_time(); #endif #if defined(WITH_BROKER) && defined(WITH_BRIDGE) /* Check if a lazy bridge should be timed out due to idle. */ if(mosq->bridge && mosq->bridge->start_type == bst_lazy && mosq->sock != INVALID_SOCKET && now - mosq->next_msg_out - mosq->keepalive >= mosq->bridge->idle_timeout){ log__printf(NULL, MOSQ_LOG_NOTICE, "Bridge connection %s has exceeded idle timeout, disconnecting.", mosq->id); net__socket_close(mosq); return MOSQ_ERR_SUCCESS; } #endif pthread_mutex_lock(&mosq->msgtime_mutex); next_msg_out = mosq->next_msg_out; last_msg_in = mosq->last_msg_in; pthread_mutex_unlock(&mosq->msgtime_mutex); if(mosq->keepalive && mosq->sock != INVALID_SOCKET && (now >= next_msg_out || now - last_msg_in >= mosq->keepalive)){ state = mosquitto__get_state(mosq); if(state == mosq_cs_active && mosq->ping_t == 0){ send__pingreq(mosq); /* Reset last msg times to give the server time to send a pingresp */ pthread_mutex_lock(&mosq->msgtime_mutex); mosq->last_msg_in = now; mosq->next_msg_out = now + mosq->keepalive; pthread_mutex_unlock(&mosq->msgtime_mutex); }else{ #ifdef WITH_BROKER # ifdef WITH_BRIDGE if(mosq->bridge){ context__send_will(mosq); } # endif net__socket_close(mosq); #else net__socket_close(mosq); state = mosquitto__get_state(mosq); if(state == mosq_cs_disconnecting){ rc = MOSQ_ERR_SUCCESS; }else{ rc = MOSQ_ERR_KEEPALIVE; } pthread_mutex_lock(&mosq->callback_mutex); if(mosq->on_disconnect){ mosq->in_callback = true; mosq->on_disconnect(mosq, mosq->userdata, rc); mosq->in_callback = false; } if(mosq->on_disconnect_v5){ mosq->in_callback = true; mosq->on_disconnect_v5(mosq, mosq->userdata, rc, NULL); mosq->in_callback = false; } pthread_mutex_unlock(&mosq->callback_mutex); return rc; #endif } } return MOSQ_ERR_SUCCESS; } uint16_t mosquitto__mid_generate(struct mosquitto *mosq) { /* FIXME - this would be better with atomic increment, but this is safer * for now for a bug fix release. * * If this is changed to use atomic increment, callers of this function * will have to be aware that they may receive a 0 result, which may not be * used as a mid. */ uint16_t mid; assert(mosq); pthread_mutex_lock(&mosq->mid_mutex); mosq->last_mid++; if(mosq->last_mid == 0) mosq->last_mid++; mid = mosq->last_mid; pthread_mutex_unlock(&mosq->mid_mutex); return mid; } #ifdef WITH_TLS int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin) { unsigned char *sha, tmp[SHA_DIGEST_LENGTH]; if(mosquitto__hex2bin(hex, tmp, SHA_DIGEST_LENGTH) != SHA_DIGEST_LENGTH){ return MOSQ_ERR_INVAL; } sha = mosquitto__malloc(SHA_DIGEST_LENGTH); if(!sha){ return MOSQ_ERR_NOMEM; } memcpy(sha, tmp, SHA_DIGEST_LENGTH); *bin = sha; return MOSQ_ERR_SUCCESS; } int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len) { BIGNUM *bn = NULL; int len; int leading_zero = 0; int start = 0; size_t i = 0; /* Count the number of leading zero */ for(i=0; i bin_max_len){ BN_free(bn); return 0; } len = BN_bn2bin(bn, bin + leading_zero); BN_free(bn); return len + leading_zero; } #endif void util__increment_receive_quota(struct mosquitto *mosq) { if(mosq->msgs_in.inflight_quota < mosq->msgs_in.inflight_maximum){ mosq->msgs_in.inflight_quota++; } } void util__increment_send_quota(struct mosquitto *mosq) { if(mosq->msgs_out.inflight_quota < mosq->msgs_out.inflight_maximum){ mosq->msgs_out.inflight_quota++; } } void util__decrement_receive_quota(struct mosquitto *mosq) { if(mosq->msgs_in.inflight_quota > 0){ mosq->msgs_in.inflight_quota--; } } void util__decrement_send_quota(struct mosquitto *mosq) { if(mosq->msgs_out.inflight_quota > 0){ mosq->msgs_out.inflight_quota--; } } int util__random_bytes(void *bytes, int count) { int rc = MOSQ_ERR_UNKNOWN; #ifdef WITH_TLS if(RAND_bytes(bytes, count) == 1){ rc = MOSQ_ERR_SUCCESS; } #elif defined(HAVE_GETRANDOM) if(getrandom(bytes, (size_t)count, 0) == count){ rc = MOSQ_ERR_SUCCESS; } #elif defined(WIN32) HCRYPTPROV provider; if(!CryptAcquireContext(&provider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)){ return MOSQ_ERR_UNKNOWN; } if(CryptGenRandom(provider, count, bytes)){ rc = MOSQ_ERR_SUCCESS; } CryptReleaseContext(provider, 0); #else int i; for(i=0; istate_mutex); #ifdef WITH_BROKER if(mosq->state != mosq_cs_disused) #endif { mosq->state = state; } pthread_mutex_unlock(&mosq->state_mutex); return MOSQ_ERR_SUCCESS; } enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq) { enum mosquitto_client_state state; pthread_mutex_lock(&mosq->state_mutex); state = mosq->state; pthread_mutex_unlock(&mosq->state_mutex); return state; } #ifndef WITH_BROKER void mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect) { pthread_mutex_lock(&mosq->state_mutex); mosq->request_disconnect = request_disconnect; pthread_mutex_unlock(&mosq->state_mutex); } bool mosquitto__get_request_disconnect(struct mosquitto *mosq) { bool request_disconnect; pthread_mutex_lock(&mosq->state_mutex); request_disconnect = mosq->request_disconnect; pthread_mutex_unlock(&mosq->state_mutex); return request_disconnect; } #endif mosquitto-2.0.18/lib/util_mosq.h000066400000000000000000000032521450213760600166070ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef UTIL_MOSQ_H #define UTIL_MOSQ_H #include #include "tls_mosq.h" #include "mosquitto.h" #include "mosquitto_internal.h" #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif int mosquitto__check_keepalive(struct mosquitto *mosq); uint16_t mosquitto__mid_generate(struct mosquitto *mosq); int mosquitto__set_state(struct mosquitto *mosq, enum mosquitto_client_state state); enum mosquitto_client_state mosquitto__get_state(struct mosquitto *mosq); #ifndef WITH_BROKER void mosquitto__set_request_disconnect(struct mosquitto *mosq, bool request_disconnect); bool mosquitto__get_request_disconnect(struct mosquitto *mosq); #endif #ifdef WITH_TLS int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin); int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len); #endif int util__random_bytes(void *bytes, int count); void util__increment_receive_quota(struct mosquitto *mosq); void util__increment_send_quota(struct mosquitto *mosq); void util__decrement_receive_quota(struct mosquitto *mosq); void util__decrement_send_quota(struct mosquitto *mosq); #endif mosquitto-2.0.18/lib/util_topic.c000066400000000000000000000230461450213760600167440ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #ifdef WIN32 # include # include # include # include #else # include #endif #ifdef WITH_BROKER #include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "memory_mosq.h" #include "net_mosq.h" #include "send_mosq.h" #include "time_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" /* Check that a topic used for publishing is valid. * Search for + or # in a topic. Return MOSQ_ERR_INVAL if found. * Also returns MOSQ_ERR_INVAL if the topic string is too long. * Returns MOSQ_ERR_SUCCESS if everything is fine. */ int mosquitto_pub_topic_check(const char *str) { int len = 0; #ifdef WITH_BROKER int hier_count = 0; #endif if(str == NULL){ return MOSQ_ERR_INVAL; } while(str && str[0]){ if(str[0] == '+' || str[0] == '#'){ return MOSQ_ERR_INVAL; } #ifdef WITH_BROKER else if(str[0] == '/'){ hier_count++; } #endif len++; str = &str[1]; } if(len > 65535) return MOSQ_ERR_INVAL; #ifdef WITH_BROKER if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; #endif return MOSQ_ERR_SUCCESS; } int mosquitto_pub_topic_check2(const char *str, size_t len) { size_t i; #ifdef WITH_BROKER int hier_count = 0; #endif if(str == NULL || len > 65535){ return MOSQ_ERR_INVAL; } for(i=0; i TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; #endif return MOSQ_ERR_SUCCESS; } /* Check that a topic used for subscriptions is valid. * Search for + or # in a topic, check they aren't in invalid positions such as * foo/#/bar, foo/+bar or foo/bar#. * Return MOSQ_ERR_INVAL if invalid position found. * Also returns MOSQ_ERR_INVAL if the topic string is too long. * Returns MOSQ_ERR_SUCCESS if everything is fine. */ int mosquitto_sub_topic_check(const char *str) { char c = '\0'; int len = 0; #ifdef WITH_BROKER int hier_count = 0; #endif if(str == NULL){ return MOSQ_ERR_INVAL; } while(str[0]){ if(str[0] == '+'){ if((c != '\0' && c != '/') || (str[1] != '\0' && str[1] != '/')){ return MOSQ_ERR_INVAL; } }else if(str[0] == '#'){ if((c != '\0' && c != '/') || str[1] != '\0'){ return MOSQ_ERR_INVAL; } } #ifdef WITH_BROKER else if(str[0] == '/'){ hier_count++; } #endif len++; c = str[0]; str = &str[1]; } if(len > 65535) return MOSQ_ERR_INVAL; #ifdef WITH_BROKER if(hier_count > TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; #endif return MOSQ_ERR_SUCCESS; } int mosquitto_sub_topic_check2(const char *str, size_t len) { char c = '\0'; size_t i; #ifdef WITH_BROKER int hier_count = 0; #endif if(str == NULL || len > 65535){ return MOSQ_ERR_INVAL; } for(i=0; i TOPIC_HIERARCHY_LIMIT) return MOSQ_ERR_INVAL; #endif return MOSQ_ERR_SUCCESS; } /* Does a topic match a subscription? */ int mosquitto_topic_matches_sub(const char *sub, const char *topic, bool *result) { size_t spos; if(!result) return MOSQ_ERR_INVAL; *result = false; if(!sub || !topic || sub[0] == 0 || topic[0] == 0){ return MOSQ_ERR_INVAL; } if((sub[0] == '$' && topic[0] != '$') || (topic[0] == '$' && sub[0] != '$')){ return MOSQ_ERR_SUCCESS; } spos = 0; while(sub[0] != 0){ if(topic[0] == '+' || topic[0] == '#'){ return MOSQ_ERR_INVAL; } if(sub[0] != topic[0] || topic[0] == 0){ /* Check for wildcard matches */ if(sub[0] == '+'){ /* Check for bad "+foo" or "a/+foo" subscription */ if(spos > 0 && sub[-1] != '/'){ return MOSQ_ERR_INVAL; } /* Check for bad "foo+" or "foo+/a" subscription */ if(sub[1] != 0 && sub[1] != '/'){ return MOSQ_ERR_INVAL; } spos++; sub++; while(topic[0] != 0 && topic[0] != '/'){ if(topic[0] == '+' || topic[0] == '#'){ return MOSQ_ERR_INVAL; } topic++; } if(topic[0] == 0 && sub[0] == 0){ *result = true; return MOSQ_ERR_SUCCESS; } }else if(sub[0] == '#'){ /* Check for bad "foo#" subscription */ if(spos > 0 && sub[-1] != '/'){ return MOSQ_ERR_INVAL; } /* Check for # not the final character of the sub, e.g. "#foo" */ if(sub[1] != 0){ return MOSQ_ERR_INVAL; }else{ while(topic[0] != 0){ if(topic[0] == '+' || topic[0] == '#'){ return MOSQ_ERR_INVAL; } topic++; } *result = true; return MOSQ_ERR_SUCCESS; } }else{ /* Check for e.g. foo/bar matching foo/+/# */ if(topic[0] == 0 && spos > 0 && sub[-1] == '+' && sub[0] == '/' && sub[1] == '#') { *result = true; return MOSQ_ERR_SUCCESS; } /* There is no match at this point, but is the sub invalid? */ while(sub[0] != 0){ if(sub[0] == '#' && sub[1] != 0){ return MOSQ_ERR_INVAL; } spos++; sub++; } /* Valid input, but no match */ return MOSQ_ERR_SUCCESS; } }else{ /* sub[spos] == topic[tpos] */ if(topic[1] == 0){ /* Check for e.g. foo matching foo/# */ if(sub[1] == '/' && sub[2] == '#' && sub[3] == 0){ *result = true; return MOSQ_ERR_SUCCESS; } } spos++; sub++; topic++; if(sub[0] == 0 && topic[0] == 0){ *result = true; return MOSQ_ERR_SUCCESS; }else if(topic[0] == 0 && sub[0] == '+' && sub[1] == 0){ if(spos > 0 && sub[-1] != '/'){ return MOSQ_ERR_INVAL; } spos++; sub++; *result = true; return MOSQ_ERR_SUCCESS; } } } if((topic[0] != 0 || sub[0] != 0)){ *result = false; } while(topic[0] != 0){ if(topic[0] == '+' || topic[0] == '#'){ return MOSQ_ERR_INVAL; } topic++; } return MOSQ_ERR_SUCCESS; } /* Does a topic match a subscription? */ int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *topic, size_t topiclen, bool *result) { size_t spos, tpos; if(!result) return MOSQ_ERR_INVAL; *result = false; if(!sub || !topic || !sublen || !topiclen){ return MOSQ_ERR_INVAL; } if((sub[0] == '$' && topic[0] != '$') || (topic[0] == '$' && sub[0] != '$')){ return MOSQ_ERR_SUCCESS; } spos = 0; tpos = 0; while(spos < sublen){ if(tpos < topiclen && (topic[tpos] == '+' || topic[tpos] == '#')){ return MOSQ_ERR_INVAL; } if(tpos == topiclen || sub[spos] != topic[tpos]){ if(sub[spos] == '+'){ /* Check for bad "+foo" or "a/+foo" subscription */ if(spos > 0 && sub[spos-1] != '/'){ return MOSQ_ERR_INVAL; } /* Check for bad "foo+" or "foo+/a" subscription */ if(spos+1 < sublen && sub[spos+1] != '/'){ return MOSQ_ERR_INVAL; } spos++; while(tpos < topiclen && topic[tpos] != '/'){ if(topic[tpos] == '+' || topic[tpos] == '#'){ return MOSQ_ERR_INVAL; } tpos++; } if(tpos == topiclen && spos == sublen){ *result = true; return MOSQ_ERR_SUCCESS; } }else if(sub[spos] == '#'){ /* Check for bad "foo#" subscription */ if(spos > 0 && sub[spos-1] != '/'){ return MOSQ_ERR_INVAL; } /* Check for # not the final character of the sub, e.g. "#foo" */ if(spos+1 < sublen){ return MOSQ_ERR_INVAL; }else{ while(tpos < topiclen){ if(topic[tpos] == '+' || topic[tpos] == '#'){ return MOSQ_ERR_INVAL; } tpos++; } *result = true; return MOSQ_ERR_SUCCESS; } }else{ /* Check for e.g. foo/bar matching foo/+/# */ if(tpos == topiclen && spos > 0 && sub[spos-1] == '+' && sub[spos] == '/' && spos+1 < sublen && sub[spos+1] == '#') { *result = true; return MOSQ_ERR_SUCCESS; } /* There is no match at this point, but is the sub invalid? */ while(spos < sublen){ if(sub[spos] == '#' && spos+1 < sublen){ return MOSQ_ERR_INVAL; } spos++; } /* Valid input, but no match */ return MOSQ_ERR_SUCCESS; } }else{ /* sub[spos] == topic[tpos] */ if(tpos+1 == topiclen){ /* Check for e.g. foo matching foo/# */ if(spos+3 == sublen && sub[spos+1] == '/' && sub[spos+2] == '#'){ *result = true; return MOSQ_ERR_SUCCESS; } } spos++; tpos++; if(spos == sublen && tpos == topiclen){ *result = true; return MOSQ_ERR_SUCCESS; }else if(tpos == topiclen && sub[spos] == '+' && spos+1 == sublen){ if(spos > 0 && sub[spos-1] != '/'){ return MOSQ_ERR_INVAL; } spos++; *result = true; return MOSQ_ERR_SUCCESS; } } } if(tpos < topiclen || spos < sublen){ *result = false; } while(tpos < topiclen){ if(topic[tpos] == '+' || topic[tpos] == '#'){ return MOSQ_ERR_INVAL; } tpos++; } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/will_mosq.c000066400000000000000000000063571450213760600166050ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #ifdef WITH_BROKER # include "mosquitto_broker_internal.h" #endif #include "mosquitto.h" #include "mosquitto_internal.h" #include "logging_mosq.h" #include "messages_mosq.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "net_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "util_mosq.h" #include "will_mosq.h" int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties) { int rc = MOSQ_ERR_SUCCESS; mosquitto_property *p; if(!mosq || !topic) return MOSQ_ERR_INVAL; if(payloadlen < 0 || payloadlen > (int)MQTT_MAX_PAYLOAD) return MOSQ_ERR_PAYLOAD_SIZE; if(payloadlen > 0 && !payload) return MOSQ_ERR_INVAL; if(mosquitto_pub_topic_check(topic)) return MOSQ_ERR_INVAL; if(mosquitto_validate_utf8(topic, (uint16_t)strlen(topic))) return MOSQ_ERR_MALFORMED_UTF8; if(properties){ if(mosq->protocol != mosq_p_mqtt5){ return MOSQ_ERR_NOT_SUPPORTED; } p = properties; while(p){ rc = mosquitto_property_check_command(CMD_WILL, p->identifier); if(rc) return rc; p = p->next; } } if(mosq->will){ mosquitto__free(mosq->will->msg.topic); mosquitto__free(mosq->will->msg.payload); mosquitto_property_free_all(&mosq->will->properties); mosquitto__free(mosq->will); } mosq->will = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!mosq->will) return MOSQ_ERR_NOMEM; mosq->will->msg.topic = mosquitto__strdup(topic); if(!mosq->will->msg.topic){ rc = MOSQ_ERR_NOMEM; goto cleanup; } mosq->will->msg.payloadlen = payloadlen; if(mosq->will->msg.payloadlen > 0){ if(!payload){ rc = MOSQ_ERR_INVAL; goto cleanup; } mosq->will->msg.payload = mosquitto__malloc(sizeof(char)*(unsigned int)mosq->will->msg.payloadlen); if(!mosq->will->msg.payload){ rc = MOSQ_ERR_NOMEM; goto cleanup; } memcpy(mosq->will->msg.payload, payload, (unsigned int)payloadlen); } mosq->will->msg.qos = qos; mosq->will->msg.retain = retain; mosq->will->properties = properties; return MOSQ_ERR_SUCCESS; cleanup: if(mosq->will){ mosquitto__free(mosq->will->msg.topic); mosquitto__free(mosq->will->msg.payload); mosquitto__free(mosq->will); mosq->will = NULL; } return rc; } int will__clear(struct mosquitto *mosq) { if(!mosq->will) return MOSQ_ERR_SUCCESS; mosquitto__free(mosq->will->msg.topic); mosq->will->msg.topic = NULL; mosquitto__free(mosq->will->msg.payload); mosq->will->msg.payload = NULL; mosquitto_property_free_all(&mosq->will->properties); mosquitto__free(mosq->will); mosq->will = NULL; mosq->will_delay_interval = 0; return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/lib/will_mosq.h000066400000000000000000000015601450213760600166010ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef WILL_MOSQ_H #define WILL_MOSQ_H #include "mosquitto.h" #include "mosquitto_internal.h" int will__set(struct mosquitto *mosq, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties); int will__clear(struct mosquitto *mosq); #endif mosquitto-2.0.18/libmosquitto.pc.in000066400000000000000000000003661450213760600173430ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} includedir=${prefix}/include libdir=${exec_prefix}/lib Name: mosquitto Description: mosquitto MQTT library (C bindings) Version: @VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lmosquitto mosquitto-2.0.18/libmosquittopp.pc.in000066400000000000000000000003741450213760600177020ustar00rootroot00000000000000prefix=@CMAKE_INSTALL_PREFIX@ exec_prefix=${prefix} includedir=${prefix}/include libdir=${exec_prefix}/lib Name: mosquittopp Description: mosquitto MQTT library (C++ bindings) Version: @VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lmosquittopp mosquitto-2.0.18/logo/000077500000000000000000000000001450213760600146125ustar00rootroot00000000000000mosquitto-2.0.18/logo/legacy/000077500000000000000000000000001450213760600160565ustar00rootroot00000000000000mosquitto-2.0.18/logo/legacy/mosquitto-14x14.png000066400000000000000000000010011450213760600213770ustar00rootroot00000000000000PNG  IHDR*sRGBbKGD pHYs0 tIME ~5IDAT(ϵP3&Yq Te` .ڴhY˅Ap )\6|۸{t8|p0I@cx<өmۂ xt7jYrD"t:P("TU?vBl6[B1) 7` X,h4H$dY$i0$Inw]7Ns Zipn1Mzhx9?X,nE4 j(0RTTmq4,J"I>PJzn6ۍRl ˡh:z hZ,c!p>)d]W'/ pEIENDB`mosquitto-2.0.18/logo/legacy/mosquitto-16x16.png000066400000000000000000000010661450213760600214160ustar00rootroot00000000000000PNG  IHDRh6sRGBbKGDPPPƇ pHYs tIME :4IDAT(ϵ?P}$J h!XJ,,l? XWYYhaao/` DD%hPT޻Dz,X:3)f J)7LUh0 !r 7ZVTt:e2vK)EwHRH$r<~n81>~*n{β,x^<,d2zXg1!Xy>vFNS>7M3je#$Ir(fl?>??UU\.$qgFZ%0 s8t]NSEAV+v8 jD"1LfۍFBn_MS0  uCQeB޲,,{4E)tz8l6PJ5M4 {IKcBIENDB`mosquitto-2.0.18/logo/legacy/mosquitto.svg000066400000000000000000000211271450213760600206460ustar00rootroot00000000000000 image/svg+xml mosquitto-2.0.18/logo/mosquitto-logo-min.svg000066400000000000000000000071231450213760600211210ustar00rootroot00000000000000 image/svg+xmlmosquitto-2.0.18/logo/mosquitto-logo-only.svg000066400000000000000000000102731450213760600213170ustar00rootroot00000000000000 image/svg+xmlmosquitto-2.0.18/logo/mosquitto-text-below.svg000066400000000000000000000234401450213760600214720ustar00rootroot00000000000000 image/svg+xmlmosquitto-2.0.18/logo/mosquitto-text-side.svg000066400000000000000000000233011450213760600213020ustar00rootroot00000000000000 image/svg+xmlmosquitto-2.0.18/logo/mosquitto.ico000066400000000000000000001034141450213760600173550ustar00rootroot00000000000000 hV   F00 % vBD(  $$wwwwR<R<R<R<ww w wR<R<R<R<R<R<]RR<RR|@@@(0` $$$wwwwwwwwww%w%wR<R<R<R<ww9w9wR<R<R<R<R<R<RR<R<-RR~>9y̳x<x<x<x<x<x<x<x<x<x<x<x<x<x<x<xߏDsVX}8[`"v(EJk0?_ @ i"zCpx홏!O lI`8ަȧ_ص|ǚ_`0Kfi/<Húl ,zL_\&feGDfg2GfڝRGF徺@ To/­x]+YH]_Eqb󁻁|3cXS.6[30uZR@3\E"ڳdKρqE8.>fIo EE(c*9w;p-ۓ̲o7 0GXdzFz8ʜoZS|IYPaǴ1b?Qa @TSb=لv*%qf@E<CUc0?1x8=;Ho@ pLɉm FkU$;#LL.1S0PF%ؖaiB'UF:NPD\7$+ ڰKbM<;b`Y`񶳱>68YNiFDU-s*?.Kbq=w p>0,y,JKz U1AiNRc瀔mSjFl Kt_)y [wP(ۼ+YXOu$&Z1:Mo\^{rjn)Ş҃E10C14gXHOFz ֫Zj'~4lU^Q*V6n شPLt?/7 +7-H$Ĥ+}#,m'&^`s&~xkYi^q1*&71+^IU9! o2 lzl'4v_&&or3Hp6%=lSyv8H{c\`&;t'vn:lj7!pMSŹaQs`;"$$jnJ[9Hbga?LqrNv2UV[ȿ)+#]J[q\!׾ XRڐoHOlrC!pRXuZ; (e6ޥz[Gpec}F|Iˀ+c! 0N6֙'L6=Mp{EU͔mhbo%4X/Hy([FzתHH{.O飯HNI]^c" c(+yiz 18>ս~/6FJ^nkQ3`S:\7|OR0'F?{ JHg 0jݖ=Q >G`?0߭6 [sEʑF [>Y.X፪l&܂etv̆o חlR^Q K]~kfc#n_[}olXmWƘ]Is3AJ5\JR%goƞXC=4?JH^2 (s|+sϫ+&? ;vEU Ns}eW}Q|y}P7#GzlgݣuRCU.y 섑%Jipe*;$ivՑFk(p4,O-x_x*YPF%] 9p_\Մؐj1d摮^HOb=/M0`@:gcu1K]KQˀG~ aȻ <(<lw s-9:XF:ʀRHŽunҴS7FTraamy%:4 (`ж]]l\coɁ9J:dO!ݾ}CN!]VHC |#p#*y;=9_9*9ae۽ѮJu@>@zc_@BYQ^l8֗p#i1~6W`"_#%,Us}?(@zsq6ظ\wDUv=#t0z p@cOv̳p %tKɝ ؽf+& ;5Ie_Fvd[L@TIb]U8Ё^&FSF&ʧd Izۭټ@:՞f&uR]6@CJ.96@̣dU_J";T6ge,:$֏S"`꿢 ϒ6\\xG&y[Pl~İVXqXM>Q4a%(3 7~d'd`وR숼wgHҋ<|[4*!u)IدwGV 4UIH#_Ǝ Ulg||=6OªJY%R4$Ovt1kf'/ahu{z"pu8M@)vL8dǠ49a.z>fd n\1p~RP"\WY;(4%|8B5=͓fAl7 q>R4p`(o8򦢻WFSJ XX '-HņȷJBGnxw%n_ް&Su*n0'I_>'{] \YqZ%%uvlHP:U"D;WH>s^@ףPvCyO%v<\RjglơtqpNO Gk{+U`P}ă_V*[-'і,ǎKzdKfg Z7ci]R+$Z _ly{ S1cMq)ŖKw~I6j4IZ=xϏuҸZ';qzlRD&anX/Kb>¼322aۑol`cR\C*Lkъd2.@],#Ii#Z':iwa*cy; }q2-jg (`w@f1r-sp$i?"T:@n;f(5{,/6J;.h4`줿k\~z_auҸCZ'K!W 8 8|Bk˸#ƽ9i=:<"6yd]d#McL׶vL6/y 侎cc`W8Z75:+N7řj V:i'`sl4N(SuS\ &Z4J*,d*PS\CdC:eB%r#M2O7W;J=+yo2ਘc95$< <,8nk ƚ29Y"Ol2!I EXkA;-%L*k`J\W9 eN7n(p66ݸ/ fz8gpo9wÜy$Ӷ7LH[6ѭ@ d@Yq'^F4AGL[$1tsq ;<#/ 5mc3)Egl8{ wxY>S܏4 0vZ$IP[\ll\(~'O £XC{U2"z4s[s3ܝWFw9+|{0u"I=w_-~1X)n ˑg 4MVq_5y{8YW:sJ\-ָr8ů[ݲ gIAqN$l/VR+@1#7`*X/ O3"5JC#w{.rMvwc]PXmY  O6 y|c{X'| 007ȕm y}ɒlȝTZq6G0AZ.fc&6W/7If@͢ƹUzw(+z-(j!R0 WkA.6"@>jb"Y 0F^%c&:)!xPA2A@6^STm;Jc *POYeXHs1$싕zf#811pH#@2}ƍq/2J|u W=.| )AKqK39Kńz3gy.QAV>WjG!ˑ県@f#7@4UD%8!kټrZYkK$Y>Pc^pdLc$ >Tf9r#*"2V RNY}3jpIlϛkș͵Rrdًo'u %5B>[fyy!5Hbd &8;XMWU{1oc:9`rix 6jgָk3,1}xSeq/U5Y<7c(m^x&@1^ sv%NƍR@- KYg+m*ap׃XlɰC~qL)rj[1ʙ2䫎8S3Q (u\ml`KqA`ƸۿxLhw:z;yHy a:+kCpߗcYŒAnC vlJu')YDgz/HC$`#eܗ;Muf/.o>F"QS4.|dFOjPlNjkde3: I vMGQm>'dk*o"[&iL]}]%p7a@# T,]}=(SC}M1"7N&W`&;LɅGqK(9IȗX#KLqܟq977͍.ovR疏}?Y#+]+;6G^( LƑȭ{rҴ2oFؽ*ʸW1Yg[֛:͠~|yx 3z;XPdٟԖ2E䓏oB_FU$ `t/qRHt]d]qFYѻ]oι ±3zG],E6f/f lcQ sPJ}sR 7"3K; $(վX}Xu/2ؽ..<}v[{9~c3S<kt))yNzwsx蝿z.ȻO@>B}Z >|>C (5~%_;l %fviEW9QMH^N$WJiWN=39KGO p;GOߖfPik !/c1nє QVaJyn0>_bNx?)hiW,FVEv-!7vU*pK`I3K;m. #] Ϳ _ (;͕ۖMvzwy똞 1Rlfs9gC;l1Cw@"S8g!=~6ȝTcz y{S O9qeӴw#'R0?.<'G-R(d91)t. ;((Q#zn<, r5X٤'d ZU[I Yԣ 8̱UQ`j]/C"N1Zpwq9X;-)6"MAIOYq%՝j-3rACY'YYn)j!+'cF 1 ~t0M)׃*#= m$ KA9}{#.T8;1Y;u1 g1Ґ?z݆K{ LunY \|sFe]bt Vs^i_ë2u&R;$[0쵥aǧOyDogg`ܪ>氤]i@>'5YwcT R0#`p+Z@/$a7k=UAs.'"+kx T ۂMqFJGJul8<p ˁ'ё|kՌ,(,ޑQ%tf(m0'^!4u @X #}bYN"}[q8,ŵ g!nlˡ`^yH_,# ˙`v a#Jr`=PO i20:/B Kx\FU&N@BeF@ -Γ@6QiS-ǓU)!h2`;RW8 Ic_s~rhBTEZOM#/90ȝNF: &U)6;h #=6tur4XD!ڪZ= F MMc{ w.v ? \Us>Ia/aW؁,u~Reח^Rp>rAC~Ť9kN=75cݙ Wamv}qځHD*/ hԦȫN$5yVTYg j6mXMG g0_s}QF L&]nw_dex9~؁.՟V"뱺,s< Zx k؜9夛*I(Ty'aU ę6,']y56řXya;}}ɛh_n l@DƯ>uVϯF`L] S {džN9!Gh{WE+QFNށ Fcӎ%?`FɕAXލZۊnU魍e?uG<,c;$X;g;y3e$q6@+V@%GŞO04sIb0 FXe-s@@;p$Ϩ$X(%6PZ0 Ht=.KסtVQ[wf47{pBNQ{b<𿅱FrM-^Ȋ*ke!)5+Zdb v"JÁm$[3/4.UAQSTEGZO-EFqq"$X߀;k$0jz_8ե7 M)pd-E1Ͻ 0ҵFruSrkL^$  x,Kz^*\l+9S{ˀ _eTI;غuiȲ' aTpMS IXO5ePLo> :e>8_hϑ-,|T79׾$~+5qOPJ@ےka ]5cI~_I~yz˗[Ӑ+<&Fu%i4Μ3 ukIn~K/7IJϳa * bWc9>y77ֻX̖򷒲UW9)2+]\0Nk3b1owgmk`>%Xuc} bVJ9c.cwt5M W) k֤W3I]NS&"MPI-9V?AR(b_Nt3ti  .CX)Zmj @A^Vu<*UM9,.EqAR֮j-kMB a4zR_gV_XY }r((+m(TL]Oq]QS:%_XSEr֎#&3 ^VGgb}Ғ7s{c)TӱdG>#it`"k%zycS۸4fO_C(hVq7aE_|"Ww$ʭk)Lgrl^*-$}4*\9|.ƭ{o"]QPV&ҩ$mVbǠH̐!lba yJvQ0+Ppށ=0ʷZ*1ye\vKyެ̺c:Hb8.pSc} +o#=.mC͊0!=8;*#89zͨ5W.V ΐEvnn% L-U)_VJ>^2=/o3ZYDvFWQ@`\Bi:X[ BxaMv6XZ:}Igc3YFʛź/O7* ZT gaǸ s(Ö agz"ߛ}ŜUl:#6Fzl{)OcRϯ. Lv].<+h*`')O:pER_\ib( xt(;4y<+3?P~߿$zu)OU HaN:bXӵkzٷF+0'bE>F@_=Yt+? )FYS;/sӓ}A O/&qORHb^?maŊžOWQ6(awF6OyŅZ> 1[16^ Z(&eW0TqSTYfCy8)}y%t@E$Xq4z x}FX\LH!Gu,, 7?A!_$~ Wo?MS,^: @{@{JںkOO Jt#+nDmp+Ke/X50/~zZ;W2;OIbm[[a5-ہﻨ__ h}[w^ ]Ua|tce8fu{vߙ~318K+$)BgMн"%|!PX* ß $${hfile}.html; \ done potgen : xml2po -o po/mosquitto/mosquitto.8.pot mosquitto.8.xml xml2po -o po/mosquitto.conf/mosquitto.conf.5.pot mosquitto.conf.5.xml xml2po -o po/mosquitto_ctrl/mosquitto_ctrl.1.pot mosquitto_ctrl.1.xml xml2po -o po/mosquitto_ctrl/mosquitto_ctrl_dynsec.1.pot mosquitto_ctrl_dynsec.1.xml xml2po -o po/mosquitto_passwd/mosquitto_passwd.1.pot mosquitto_passwd.1.xml xml2po -o po/mosquitto_pub/mosquitto_pub.1.pot mosquitto_pub.1.xml xml2po -o po/mosquitto_sub/mosquitto_sub.1.pot mosquitto_sub.1.xml xml2po -o po/mosquitto_sub/mosquitto_rr.1.pot mosquitto_rr.1.xml xml2po -o po/mqtt/mqtt.7.pot mqtt.7.xml xml2po -o po/mosquitto-tls/mosquitto-tls.7.pot mosquitto-tls.7.xml xml2po -o po/libmosquitto/libmosquitto.3.pot libmosquitto.3.xml # To merge new translations do: # /usr/bin/xml2po -p de.po chapter1.xml > chapter1.de.xml mosquitto-2.0.18/man/html.xsl000066400000000000000000000012251450213760600161210ustar00rootroot00000000000000 man.css ansi mosquitto-2.0.18/man/libmosquitto.3.meta000066400000000000000000000001531450213760600201700ustar00rootroot00000000000000.. title: libmosquitto man page .. slug: libmosquitto-3 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/libmosquitto.3.xml000066400000000000000000000013621450213760600200450ustar00rootroot00000000000000 libmosquitto 3 Mosquitto Project Library calls libmosquitto MQTT version 5.0/3.1.1 client library Documentation See Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/manpage.xsl000066400000000000000000000014511450213760600165660ustar00rootroot00000000000000 0 0 https://mosquitto.org/man/ ansi mosquitto-2.0.18/man/mosquitto-tls.7.meta000066400000000000000000000001551450213760600203070ustar00rootroot00000000000000.. title: mosquitto-tls man page .. slug: mosquitto-tls-7 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto-tls.7.xml000066400000000000000000000077601450213760600201720ustar00rootroot00000000000000 mosquitto-tls 7 Mosquitto Project Conventions and miscellaneous mosquitto-tls Configure SSL/TLS support for Mosquitto Description mosquitto provides SSL support for encrypted network connections and authentication. This manual describes how to create the files needed. It is important to use different certificate subject parameters for your CA, server and clients. If the certificates appear identical, even though generated separately, the broker/client will not be able to distinguish between them and you will experience difficult to diagnose errors. Generating certificates The sections below give the openssl commands that can be used to generate certificates, but without any context. The asciicast at https://asciinema.org/a/201826 gives a full run through of how to use those commands. Certificate Authority Generate a certificate authority certificate and key. openssl req -new -x509 -days <duration> -extensions v3_ca -keyout ca.key -out ca.crt Server Generate a server key. openssl genrsa -aes256 -out server.key 2048 Generate a server key without encryption. openssl genrsa -out server.key 2048 Generate a certificate signing request to send to the CA. openssl req -out server.csr -key server.key -new When prompted for the CN (Common Name), please enter either your server (or broker) hostname or domain name. Send the CSR to the CA, or sign it with your CA key: openssl x509 -req -in server.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out server.crt -days <duration> Client Generate a client key. openssl genrsa -aes256 -out client.key 2048 Generate a certificate signing request to send to the CA. openssl req -out client.csr -key client.key -new Send the CSR to the CA, or sign it with your CA key: openssl x509 -req -in client.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out client.crt -days <duration> See Also mosquitto 8 mosquitto-conf 5 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto.8.meta000066400000000000000000000001451450213760600175070ustar00rootroot00000000000000.. title: Mosquitto man page .. slug: mosquitto-8 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto.8.xml000066400000000000000000000761041450213760600173710ustar00rootroot00000000000000 mosquitto 8 Mosquitto Project System management commands mosquitto an MQTT broker mosquitto -c config file -d --daemon -p port number -v Description mosquitto is a broker for the MQTT protocol version 5.0/3.1.1/3.1. Options Load configuration from a file. If not given, then the broker will listen on port 1883 bound to the loopback interface, and the default values as described in mosquitto.conf5 are used. See the option for a description of changes in behaviour from 1.6.x to 2.0. Run mosquitto in the background as a daemon. All other behaviour remains the same. Listen on the port specified. May be specified up to 10 times to open multiple sockets listening on different ports. In version 1.6.x and earlier, the listener defined by (or the default port of 1883) would be bound to all interfaces and so be accessible from any network. It could also be used in combination with . From version 2.0 onwards, the listeners defined with are bound to the loopback interface only, and so can only be connected to from the local machine. If both is used and a listener is defined in a configuration file, then the options are IGNORED. Use verbose logging. This is equivalent to setting to in the configuration file. This overrides and logging options given in the configuration file. Configuration The broker can be configured using a configuration file as described in mosquitto.conf5 and this is the main point of information for mosquitto. The files required for SSL/TLS support are described in mosquitto-tls7. Platform limitations Some versions of Windows have limitations on the number of concurrent connections due to the Windows API being used. In modern versions of Windows, e.g. Windows 10 or Windows Server 2019, this is approximately 8192 connections. In earlier versions of Windows, this limit is 2048 connections. MQTT Support Mosquitto supports MQTT v5.0, v3.1.1, and v3.1. MQTT v5.0 Mosquitto provides full MQTT v5.0 support, but some features are not used directly. The following sections describe the new features and explain where Mosquitto does not make use of a feature. Features Basic MQTT authentication uses username/password checks. Enhanced authentication allows different authentication schemes to be integrated into MQTT, and even those schemes with multiple step processes. Clients request a particular type of authentication and if the broker is configured for that scheme the authentication continues. Mosquitto supports enhanced authentication through plugins. Most MQTT packets now have the concept of a which indicates success or failure, and what the failure was. Mosquitto provides full support for reason codes, but does not make use of the feature which can be used to provide a human readable error string to explain the reason code. The number of "in flight" messages for QoS 1 and QoS 2 can be controlled by both the client and the broker. MQTT v5.0 adds a request/response pattern that allows a client to publish a message and instruct the subscribers of that message where to publish a response. Server redirection is the concept of telling a client to connect to a different MQTT broker, either on CONNECT or with a broker initiated DISCONNECT. Mosquitto does not currently make use of this feature. When multiple clients subscribe to the same shared subscription, only one client out of the group will receive each message which allows for distributing work loads. Packet properties MQTT v5.0 allows properties to be added to packets to control certain behaviour. Unless noted, Mosquitto support the properties listed below. Authentication data Authentication method Maximum packet size Receive maximum Request problem information - supported but not used Request response information - supported but not used Session expiry interval Topic alias maximum User property Content type Correlation data Message expiry interval Payload format indicator Response topic User property Will delay interval Assigned client identifier Authentication data Authentication method Maximum packet size Maximum qos Reason string - supported but not used Receive maximum Response information - supported but not used Retain available Server keep alive Server reference - supported but not used Session expiry interval Shared subscription available Subscription identifiers available Topic alias maximum User property Wildcard subscription available Content type Correlation data Message expiry interval Payload format indicator Response topic Subscription identifier Topic alias User property Reason string - supported but not used User property Subscription identifier User property Reason string - supported but not used Server reference - supported but not used Session expiry interval User property Authentication method Authentication data Reason string - supported but not used User property MQTT v3.1.1 Mosquitto provides full MQTT v3.1.1 support. MQTT v3.1 Mosquitto provides full MQTT v3.1 support. MQTT v3 MQTT v3 is an obsolete version of the protocol that does not support username/password authentication and used the flag in the CONNECT packet which applied only to the start of a session. An MQTT v3 client will be able to successfully connect to a Mosquitto instance that does not require authentication. Broker Status Clients can find information about the broker by subscribing to topics in the $SYS hierarchy as follows. Topics marked as static are only sent once per client on subscription. All other topics are updated every seconds. If is 0, then updates are not sent. Note that if you are using a command line client to interact with the $SYS topics and your shell interprets $ as an environment variable, you need to place the topic in single quotes '$SYS/...' or to escape the dollar symbol: \$SYS/... otherwise the $SYS will be treated as an environment variable. The total number of bytes received since the broker started. The total number of bytes sent since the broker started. (deprecated) The number of currently connected clients. The number of disconnected persistent clients that have been expired and removed through the persistent_client_expiration option. (deprecated) The total number of persistent clients (with clean session disabled) that are registered at the broker but are currently disconnected. The maximum number of clients that have been connected to the broker at the same time. The total number of active and inactive clients currently connected and registered on the broker. When bridges are configured to/from the broker, common practice is to provide a status topic that indicates the state of the connection. This is provided within $SYS/broker/connection/ by default. If the value of the topic is 1 the connection is active, if 0 then it is not active. See the Bridges section below for more information on bridges. The current size of the heap memory in use by mosquitto. Note that this topic may be unavailable depending on compile time options. The largest amount of heap memory used by mosquitto. Note that this topic may be unavailable depending on compile time options. The moving average of the number of CONNECT packets received by the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of connections received in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of bytes received by the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of bytes received in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of bytes sent by the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of bytes sent in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of all types of MQTT messages received by the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of messages received in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of all types of MQTT messages sent by the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of messages send in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of publish messages dropped by the broker over different time intervals. This shows the rate at which durable clients that are disconnected are losing messages. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of messages dropped in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of publish messages received by the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of publish messages received in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of publish messages sent by the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of publish messages sent in 1 minute, averaged over 1, 5 or 15 minutes. The moving average of the number of socket connections opened to the broker over different time intervals. The final "+" of the hierarchy can be 1min, 5min or 15min. The value returned represents the number of socket connections in 1 minute, averaged over 1, 5 or 15 minutes. The number of messages with QoS>0 that are awaiting acknowledgments. The total number of messages of any type received since the broker started. The total number of messages of any type sent since the broker started. The total number of publish messages that have been dropped due to inflight/queuing limits. See the max_inflight_messages and max_queued_messages options in mosquitto.conf5 for more information. The total number of PUBLISH messages received since the broker started. The total number of PUBLISH messages sent since the broker started. The total number of retained messages active on the broker. (deprecated) The number of messages currently held in the message store. This includes retained messages and messages queued for durable clients. The number of bytes currently held by message payloads in the message store. This includes retained messages and messages queued for durable clients. The total number of subscriptions active on the broker. The version of the broker. Static. Wildcard Topic Subscriptions In addition to allowing clients to subscribe to specific topics, mosquitto also allows the use of two wildcards in subscriptions. is the wildcard used to match a single level of hierarchy. For example, for a topic of "a/b/c/d", the following example subscriptions will match: a/b/c/d +/b/c/d a/+/c/d a/+/+/d +/+/+/+ The following subscriptions will not match: a/b/c b/+/c/d +/+/+ The second wildcard is and is used to match all subsequent levels of hierarchy. With a topic of "a/b/c/d", the following example subscriptions will match: a/b/c/d # a/# a/b/# a/b/c/# +/b/c/# The $SYS hierarchy does not match a subscription of "#". If you want to observe the entire $SYS hierarchy, subscribe to $SYS/#. Note that the wildcards must be only ever used on their own, so a subscription of "a/b+/c" is not valid use of a wildcard. The wildcard must only ever be used as the final character of a subscription. Bridges Multiple brokers can be connected together with the bridging functionality. This is useful where it is desirable to share information between locations, but where not all of the information needs to be shared. An example could be where a number of users are running a broker to help record power usage and for a number of other reasons. The power usage could be shared through bridging all of the user brokers to a common broker, allowing the power usage of all users to be collected and compared. The other information would remain local to each broker. For information on configuring bridges, see mosquitto.conf5. Signals On POSIX systems Mosquitto can receive signals and act on them as described below. To send signals, use e.g. kill -HUP <process id of mosquitto> SIGHUP Upon receiving the SIGHUP signal, mosquitto will attempt to reload configuration file data, assuming that the argument was provided when mosquitto was started. Not all configuration parameters can be reloaded without restarting. See mosquitto.conf5 for details. If TLS certificates are in use, then mosquitto will also reload certificate on receiving a SIGHUP. SIGUSR1 Upon receiving the SIGUSR1 signal, mosquitto will write the persistence database to disk. This signal is only acted upon if persistence is enabled. SIGUSR2 The SIGUSR2 signal causes mosquitto to print out the current subscription tree, along with information about where retained messages exist. This is intended as a testing feature only and may be removed at any time. Files /etc/mosquitto/mosquitto.conf Configuration file. See mosquitto.conf5. /var/lib/mosquitto/mosquitto.db Persistent message data storage location if persist enabled. /etc/hosts.allow /etc/hosts.deny Host access control via tcp-wrappers as described in hosts_access5. Bugs mosquitto bug information can be found at See Also mqtt 7 mosquitto-tls 7 mosquitto.conf 5 hosts_access 5 mosquitto_ctrl 1 mosquitto_passwd 1 mosquitto_pub 1 mosquitto_rr 1 mosquitto_sub 1 libmosquitto 3 Thanks Thanks to Andy Stanford-Clark for being one of the people who came up with MQTT in the first place. Thanks to Andy and Nicholas O'Leary for providing clarifications of the protocol. Thanks also to everybody at the Ubuntu UK Podcast and Linux Outlaws for organising OggCamp, where Andy gave a talk that inspired mosquitto. Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto.conf.5.meta000066400000000000000000000001571450213760600204330ustar00rootroot00000000000000.. title: mosquitto.conf man page .. slug: mosquitto-conf-5 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto.conf.5.xml000066400000000000000000002746651450213760600203260ustar00rootroot00000000000000 mosquitto.conf 5 Mosquitto Project File formats and conventions mosquitto.conf the configuration file for mosquitto mosquitto.conf Description mosquitto.conf is the configuration file for mosquitto. This file can reside anywhere as long as mosquitto can read it. By default, mosquitto does not need a configuration file and will use the default values listed below. See mosquitto8 for information on how to load a configuration file. Mosquitto can be instructed to reload the configuration file by sending a SIGHUP signal as described in the Signals section of mosquitto8. Not all configuration options can be reloaded, as detailed in the options below. File Format All lines with a # as the very first character are treated as a comment. Configuration lines start with a variable name. The variable value is separated from the name by a single space. Authentication The authentication options described below allow a wide range of possibilities in conjunction with the listener options. This section aims to clarify the possibilities. An overview is also available at The simplest option is to have no authentication at all. This is the default if no other options are given. Unauthenticated encrypted support is provided by using the certificate based SSL/TLS based options certfile and keyfile. MQTT provides username/password authentication as part of the protocol. Use the password_file option to define the valid usernames and passwords. Be sure to use network encryption if you are using this option otherwise the username and password will be vulnerable to interception. Use the to control whether passwords are required globally or on a per-listener basis. Mosquitto provides the Dynamic Security plugin which handles username/password authentication and access control in a much more flexible way than a password file. See When using certificate based encryption there are three options that affect authentication. The first is require_certificate, which may be set to true or false. If false, the SSL/TLS component of the client will verify the server but there is no requirement for the client to provide anything for the server: authentication is limited to the MQTT built in username/password. If require_certificate is true, the client must provide a valid certificate in order to connect successfully. In this case, the second and third options, use_identity_as_username and use_subject_as_username, become relevant. If set to true, use_identity_as_username causes the Common Name (CN) from the client certificate to be used instead of the MQTT username for access control purposes. The password is not used because it is assumed that only authenticated clients have valid certificates. This means that any CA certificates you include in cafile or capath will be able to issue client certificates that are valid for connecting to your broker. If use_identity_as_username is false, the client must authenticate as normal (if required by password_file) through the MQTT options. The same principle applies for the use_subject_as_username option, but the entire certificate subject is used as the username instead of just the CN. When using pre-shared-key based encryption through the psk_hint and psk_file options, the client must provide a valid identity and key in order to connect to the broker before any MQTT communication takes place. If use_identity_as_username is true, the PSK identity is used instead of the MQTT username for access control purposes. If use_identity_as_username is false, the client may still authenticate using the MQTT username/password if using the password_file option. Both certificate and PSK based encryption are configured on a per-listener basis. Authentication plugins can be created to augment the password_file, acl_file and psk_file options with e.g. SQL based lookups. It is possible to support multiple authentication schemes at once. A config could be created that had a listener for all of the different encryption options described above and hence a large number of ways of authenticating. General Options file path Set the path to an access control list file. If defined, the contents of the file are used to control client access to topics on the broker. If this parameter is defined then only the topics listed will have access. Topic access is added with lines of the format: topic [read|write|readwrite|deny] <topic> The access type is controlled using "read", "write", "readwrite" or "deny". This parameter is optional (unless <topic> includes a space character) - if not given then the access is read/write. <topic> can contain the + or # wildcards as in subscriptions. The "deny" option can used to explicitly deny access to a topic that would otherwise be granted by a broader read/write/readwrite statement. Any "deny" topics are handled before topics that grant read/write access. The first set of topics are applied to anonymous clients, assuming is true. User specific topic ACLs are added after a user line as follows: user <username> The username referred to here is the same as in . It is not the clientid. It is also possible to define ACLs based on pattern substitution within the topic. The form is the same as for the topic keyword, but using pattern as the keyword. pattern [read|write|readwrite|deny] <topic> The patterns available for substition are: %c to match the client id of the client %u to match the username of the client The substitution pattern must be the only text for that level of hierarchy. Pattern ACLs apply to all users even if the "user" keyword has previously been given. Example: pattern write sensor/%u/data Allow access for bridge connection messages: pattern write $SYS/broker/connection/%c/state If the first character of a line of the ACL file is a # it is treated as a comment. If is true, this option applies to the current listener being configured only. If is false, this option applies to all listeners. Reloaded on reload signal. The currently loaded ACLs will be freed and reloaded. Existing subscriptions will be affected after the reload. See also [ true | false ] Boolean value that determines whether clients that connect without providing a username are allowed to connect. If set to false then another means of connection should be created to control authenticated client access. Defaults to false, unless no listeners are defined in the configuration file, in which case it set to true, but connections are only allowed from the local machine. If is true, this option applies to the current listener being configured only. If is false, this option applies to all listeners. In version 1.6.x and earlier, this option defaulted to true unless there was another security option set. Reloaded on reload signal. [ true | false ] This option is deprecated and will be removed in a future version. The behaviour will default to true. If a client is subscribed to multiple subscriptions that overlap, e.g. foo/# and foo/+/baz , then MQTT expects that when the broker receives a message on a topic that matches both subscriptions, such as foo/bar/baz, then the client should only receive the message once. Mosquitto keeps track of which clients a message has been sent to in order to meet this requirement. This option allows this behaviour to be disabled, which may be useful if you have a large number of clients subscribed to the same set of topics and want to minimise memory usage. It can be safely set to true if you know in advance that your clients will never have overlapping subscriptions, otherwise your clients must be able to correctly deal with duplicate messages even when then have QoS=2. Defaults to true. This option applies globally. Reloaded on reload signal. [ true | false ] MQTT 3.1.1 and MQTT 5 allow clients to connect with a zero length client id and have the broker generate a client id for them. Use this option to allow/disallow this behaviour. Defaults to true. See also the option. If is true, this option applies to the current listener being configured only. If is false, this option applies to all listeners. Reloaded on reload signal. [ true | false ] If true then before an ACL check is made, the username/client id of the client needing the check is searched for the presence of either a '+' or '#' character. If either of these characters is found in either the username or client id, then the ACL check is denied before it is sent to the plugin. This check prevents the case where a malicious user could circumvent an ACL check by using one of these characters as their username or client id. This is the same issue as was reported with mosquitto itself as CVE-2017-7650. If you are entirely sure that the plugin you are using is not vulnerable to this attack (i.e. if you never use usernames or client ids in topics) then you can disable this extra check and hence have all ACL checks delivered to your plugin by setting this option to false. Defaults to true. Applies to the current authentication plugin being configured. Not currently reloaded on reload signal. prefix If is true, this option allows you to set a string that will be prefixed to the automatically generated client ids to aid visibility in logs. Defaults to . If is true, this option applies to the current listener being configured only. If is false, this option applies to all listeners. Reloaded on reload signal. seconds The number of seconds that mosquitto will wait between each time it saves the in-memory database to disk. If set to 0, the in-memory database will only be saved when mosquitto exits or when receiving the SIGUSR1 signal. Note that this setting only has an effect if persistence is enabled. Defaults to 1800 seconds (30 minutes). This option applies globally. Reloaded on reload signal. [ true | false ] If true, mosquitto will count the number of subscription changes, retained messages received and queued messages and if the total exceeds then the in-memory database will be saved to disk. If false, mosquitto will save the in-memory database to disk by treating as a time in seconds. This option applies globally. Reloaded on reload signal. [ true | false ] This option affects the scenario when a client subscribes to a topic that has retained messages. It is possible that the client that published the retained message to the topic had access at the time they published, but that access has been subsequently removed. If is set to true, the default, the source of a retained message will be checked for access rights before it is republished. When set to false, no check will be made and the retained message will always be published. This option applies globally, regardless of the option. prefix This option is deprecated and will be removed in a future version. If defined, only clients that have a clientid with a prefix that matches clientid_prefixes will be allowed to connect to the broker. For example, setting "secure-" here would mean a client "secure-client" could connect but another with clientid "mqtt" couldn't. By default, all client ids are valid. This option applies globally. Reloaded on reload signal. Note that currently connected clients will be unaffected by any changes. [ true | false ] If set to true, the log will include entries when clients connect and disconnect. If set to false, these entries will not appear. This option applies globally. Reloaded on reload signal. dir External configuration files may be included by using the include_dir option. This defines a directory that will be searched for config files. All files that end in '.conf' will be loaded as a configuration file. It is best to have this as the last option in the main file. This option will only be processed from the main configuration file. The directory specified must not contain the main configuration file. The configuration files in are loaded in case sensitive alphabetical order, with the upper case of each letter ordered before the lower case of the same letter. Given the files b.conf, A.conf, 01.conf, a.conf, B.conf, and 00.conf inside , the config files would be loaded in this order: 00.conf 01.conf A.conf a.conf B.conf b.conf If this option is used multiple times, then each option is processed completely in the order that they are written in the main configuration file. Assuming a directory one.d containing files B.conf and C.conf, and a second directory two.d containing files A.conf and D.conf, and a config: include_dir one.d include_dir two.d Then the config files would be loaded in this order: # files from one.d B.conf C.conf # files from two.d A.conf D.conf destinations Send log messages to a particular destination. Possible destinations are: . and log to the console on the named output. uses the userspace syslog facility which usually ends up in /var/log/messages or similar. logs to the broker topic '$SYS/broker/log/<severity>', where severity is one of E, W, N, I, M which are error, warning, notice, information and message. Message type severity is used by the subscribe and unsubscribe log_type options and publishes log messages at $SYS/broker/log/M/subscribe and $SYS/broker/log/M/unsubscribe. Debug messages are never logged on topics. The destination requires an additional parameter which is the file to be logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be closed and reopened when the broker receives a HUP signal. Only a single file destination may be configured. The destination is for the automotive `Diagnostic Log and Trace` tool. This requires that Mosquitto has been compiled with DLT support. Use "log_dest none" if you wish to disable logging. Defaults to stderr. This option may be specified multiple times. Note that if the broker is running as a Windows service it will default to "log_dest none" and neither stdout nor stderr logging is available. Reloaded on reload signal. local facility If using syslog logging (not on Windows), messages will be logged to the "daemon" facility by default. Use the option to choose which of local0 to local7 to log to instead. The option value should be an integer value, e.g. "log_facility 5" to use local5. [ true | false ] Boolean value, if set to true a timestamp value will be added to each log entry. The default is true. Reloaded on reload signal. format Set the format of the log timestamp. If left unset, this is the number of seconds since the Unix epoch. This option is a free text string which will be passed to the strftime function as the format specifier. To get an ISO 8601 datetime, for example: log_timestamp_format %Y-%m-%dT%H:%M:%S Reloaded on reload signal. types Choose types of messages to log. Possible types are: debug, error, warning, notice, information, subscribe, unsubscribe, websockets, none, all. Defaults to error, warning, notice and information. This option may be specified multiple times. Note that the debug type (used for decoding incoming/outgoing network packets) is never logged in topics. Reloaded on reload signal. count Outgoing QoS 1 and 2 messages will be allowed in flight until this byte limit is reached. This allows control of outgoing message rate based on message size rather than message count. If the limit is set to 100, messages of over 100 bytes are still allowed, but only a single message can be in flight at once. Defaults to 0. (No limit). See also the option. This option applies globally. Reloaded on reload signal. count The maximum number of outgoing QoS 1 or 2 messages that can be in the process of being transmitted simultaneously. This includes messages currently going through handshakes and messages that are being retried. Defaults to 20. Set to 0 for no maximum. If set to 1, this will guarantee in-order delivery of messages. This option applies globally. Reloaded on reload signal. value For MQTT v5 clients, it is possible to have the server send a "server keepalive" value that will override the keepalive value set by the client. This is intended to be used as a mechanism to say that the server will disconnect the client earlier than it anticipated, and that the client should use the new keepalive value. The max_keepalive option allows you to specify that clients may only connect with keepalive less than or equal to this value, otherwise they will be sent a server keepalive telling them to use max_keepalive. This only applies to MQTT v5 clients. The maximum value allowable, and default value, is 65535. Set to 0 to allow clients to set keepalive = 0, which means no keepalive checks are made and the client will never be disconnected by the broker if no messages are received. You should be very sure this is the behaviour that you want. For MQTT v3.1.1 and v3.1 clients, there is no mechanism to tell the client what keepalive value they should use. If an MQTT v3.1.1 or v3.1 client specifies a keepalive time greater than max_keepalive they will be sent a CONNACK message with the "identifier rejected" reason code, and disconnected. This option applies globally. Reloaded on reload signal. value For MQTT v5 clients, it is possible to have the server send a "maximum packet size" value that will instruct the client it will not accept MQTT packets with size greater than bytes. This applies to the full MQTT packet, not just the payload. Setting this option to a positive value will set the maximum packet size to that number of bytes. If a client sends a packet which is larger than this value, it will be disconnected. This applies to all clients regardless of the protocol version they are using, but v3.1.1 and earlier clients will of course not have received the maximum packet size information. Defaults to no limit. This option applies to all clients, not just those using MQTT v5, but it is not possible to notify clients using MQTT v3.1.1 or MQTT v3.1 of the limit. Setting below 20 bytes is forbidden because it is likely to interfere with normal client operation even with small payloads. This option applies globally. Reloaded on reload signal. count The number of outgoing QoS 1 and 2 messages above those currently in-flight will be queued (per client) by the broker. Once this limit has been reached, subsequent messages will be silently dropped. This is an important option if you are sending messages at a high rate and/or have clients who are slow to respond or may be offline for extended periods of time. Defaults to 0. (No maximum). See also the option. If both max_queued_messages and max_queued_bytes are specified, packets will be queued until the first limit is reached. This option applies globally. Reloaded on reload signal. count The maximum number of QoS 1 or 2 messages to hold in the queue (per client) above those messages that are currently in flight. Defaults to 1000. Set to 0 for no maximum (not recommended). See also the and options. This option applies globally. Reloaded on reload signal. limit This option sets the maximum number of heap memory bytes that the broker will allocate, and hence sets a hard limit on memory use by the broker. Memory requests that exceed this value will be denied. The effect will vary depending on what has been denied. If an incoming message is being processed, then the message will be dropped and the publishing client will be disconnected. If an outgoing message is being sent, then the individual message will be dropped and the receiving client will be disconnected. Defaults to no limit. This option is only available if memory tracking support is compiled in. Reloaded on reload signal. Setting to a lower value and reloading will not result in memory being freed. limit This option sets the maximum publish payload size that the broker will allow. Received messages that exceed this size will not be accepted by the broker. This means that the message will not be forwarded on to subscribing clients, but the QoS flow will be completed for QoS 1 or QoS 2 messages. MQTT v5 clients using QoS 1 or QoS 2 will receive a PUBACK or PUBREC with the "implementation specific error" reason code. The default value is 0, which means that all valid MQTT messages are accepted. MQTT imposes a maximum payload size of 268435455 bytes. This option applies globally. Reloaded on reload signal. file path Set the path to a password file. If defined, the contents of the file are used to control client access to the broker. The file can be created using the mosquitto_passwd1 utility. If mosquitto is compiled without TLS support (it is recommended that TLS support is included), then the password file should be a text file with each line in the format "username:password", where the colon and password are optional but recommended. If is set to false, only users defined in this file will be able to connect. Setting to true when password_fileis defined is valid and could be used with acl_file to have e.g. read only guest/anonymous accounts and defined users that can publish. If is true, this option applies to the current listener being configured only. If is false, this option applies to all listeners. Reloaded on reload signal. The currently loaded username and password data will be freed and reloaded. Clients that are already connected will not be affected. See also mosquitto_passwd1 and [ true | false ] If true, then authentication and access control settings will be controlled on a per-listener basis. The following options are affected: , , , , , . , , Note that if set to true, then a durable client (i.e. with clean session set to false) that has disconnected will use the ACL settings defined for the listener that it was most recently connected to. The default behaviour is for this to be set to false, which maintains the settings behaviour from previous versions of mosquitto. Reloaded on reload signal. [ true | false ] If true, connection, subscription and message data will be written to the disk in mosquitto.db at the location dictated by persistence_location. When mosquitto is restarted, it will reload the information stored in mosquitto.db. The data will be written to disk when mosquitto closes and also at periodic intervals as defined by autosave_interval. Writing of the persistence database may also be forced by sending mosquitto the SIGUSR1 signal. If false, the data will be stored in memory only. Defaults to false. The persistence file may change its format in a new version. The broker can currently read all old formats, but will only save in the latest format. It should always be safe to upgrade, but cautious users may wish to take a copy of the persistence file before installing a new version so that they can roll back to an earlier version if necessary. This option applies globally. Reloaded on reload signal. file name The filename to use for the persistent database. Defaults to mosquitto.db. This option applies globally. Reloaded on reload signal. path The path where the persistence database should be stored. If not given, then the current directory is used. This option applies globally. Reloaded on reload signal. duration This option allows the session of persistent clients (those with clean session set to false) that are not currently connected to be removed if they do not reconnect within a certain time frame. This is a non-standard option in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions. Badly designed clients may set clean session to false whilst using a randomly generated client id. This leads to persistent clients that connect once and never reconnect. This option allows these clients to be removed. This option allows persistent clients (those with clean session set to false) to be removed if they do not reconnect within a certain time frame. The expiration period should be an integer followed by one of h d w m y for hour, day, week, month and year respectively. For example: persistent_client_expiration 2m persistent_client_expiration 14d persistent_client_expiration 1y As this is a non-standard option, the default if not set is to never expire persistent clients. This option applies globally. Reloaded on reload signal. file path Write a pid file to the file specified. If not given (the default), no pid file will be written. If the pid file cannot be written, mosquitto will exit. If mosquitto is being automatically started by an init script it will usually be required to write a pid file. This should then be configured as e.g. /var/run/mosquitto/mosquitto.pid Not reloaded on reload signal. value Options to be passed to the most recent defined in the configuration file. See the specific plugin instructions for details of what options are available. Applies to the current plugin being configured. This is also available as the option, but this use is deprecated and will be removed in a future version. file path Specify an external module to use for authentication and access control. This allows custom username/password and access control functions to be created. Can be specified multiple times to load multiple plugins. The plugins will be processed in the order that they are specified. If , or are used in the config file alongsize , the plugin checks will run after the built in checks. Not currently reloaded on reload signal. See also This is also available as the option, but this use is deprecated and will be removed in a future version. file path Set the path to a pre-shared-key file. This option requires a listener to be have PSK support enabled. If defined, the contents of the file are used to control client access to the broker. Each line should be in the format "identity:key", where the key is a hexadecimal string with no leading "0x". A client connecting to a listener that has PSK support enabled must provide a matching identity and PSK to allow the encrypted connection to proceed. If is true, this option applies to the current listener being configured only. If is false, this option applies to all listeners. Reloaded on reload signal. The currently loaded identity and key data will be freed and reloaded. Clients that are already connected will not be affected. [ true | false ] Set to true to queue messages with QoS 0 when a persistent client is disconnected. When bridges topics are configured with QoS level 1 or 2 incoming QoS 0 messages for these topics are also queued. These messages are included in the limit imposed by max_queued_messages. Defaults to false. Note that the MQTT v3.1.1 spec states that only QoS 1 and 2 messages should be saved in this situation so this is a non-standard option. This option applies globally. Reloaded on reload signal. [ true | false ] If set to false, then retained messages are not supported. Clients that send a message with the retain bit will be disconnected if this option is set to false. Defaults to true. This option applies globally. Reloaded on reload signal. [ true | false ] If set to true, the TCP_NODELAY option will be set on client sockets to disable Nagle's algorithm. This has the effect of reducing latency of some messages at potentially increasing the number of TCP packets being sent. Defaults to false. This option applies globally. Reloaded on reload signal. seconds The integer number of seconds between updates of the $SYS subscription hierarchy, which provides status information about the broker. If unset, defaults to 10 seconds. Set to 0 to disable publishing the $SYS hierarchy completely. This option applies globally. Reloaded on reload signal. [ true | false ] The MQTT specification requires that the QoS of a message delivered to a subscriber is never upgraded to match the QoS of the subscription. Enabling this option changes this behaviour. If is set true, messages sent to a subscriber will always match the QoS of its subscription. This is a non-standard option not provided for by the spec. Defaults to false. This option applies globally. Reloaded on reload signal. username When run as root, change to this user and its primary group on startup. If set to "mosquitto" or left unset, and if the "mosquitto" user does not exist, then mosquitto will change to the "nobody" user instead. If this is set to another value and mosquitto is unable to change to this user and group, it will exit with an error. The user specified must have read/write access to the persistence database if it is to be written. If run as a non-root user, this setting has no effect. Defaults to mosquitto. This setting has no effect on Windows and so you should run mosquitto as the user you wish it to run as. Not reloaded on reload signal. Listeners The network ports that mosquitto listens on can be controlled using listeners. The default listener options can be overridden and further listeners can be created. General Options address This option is deprecated and will be removed in a future version. Use the instead. Listen for incoming network connections on the specified IP address/hostname only. This is useful to restrict access to certain network interfaces. To restrict access to mosquitto to the local host only, use "bind_address localhost". This only applies to the default listener. Use the option to control other listeners. It is recommended to use an explicit rather than rely on the implicit default listener options like this. Not reloaded on reload signal. device Listen for incoming network connections only on the specified interface. This is similar to the option but is useful when an interface has multiple addresses or the address may change. If used at the same time as the for the default listener, or the bind address/host part of the , then will take priority. This option is not available on Windows. Not reloaded on reload signal. directory When a listener is using the websockets protocol, it is possible to serve http data as well. Set to a directory which contains the files you wish to serve. If this option is not specified, then no normal http connections will be possible. Not reloaded on reload signal. port bind address/host/unix socket path Listen for incoming network connection on the specified port. A second optional argument allows the listener to be bound to a specific ip address/hostname. If this variable is used and neither the global nor options are used then the default listener will not be started. The option allows this listener to be bound to a specific IP address by passing an IP address or hostname. For websockets listeners, it is only possible to pass an IP address here. On systems that support Unix Domain Sockets, this option can also be used to create a Unix socket rather than opening a TCP socket. In this case, the port must be set to 0, and the unix socket path must be given. This option may be specified multiple times. See also the option. Not reloaded on reload signal. count Limit the total number of clients connected for the current listener. Set to -1 to have "unlimited" connections. Note that other limits may be imposed that are outside the control of mosquitto. See e.g. limits.conf. Not reloaded on reload signal. value Limit the QoS value allowed for clients connecting to this listener. Defaults to 2, which means any QoS can be used. Set to 0 or 1 to limit to those QoS values. This makes use of an MQTT v5 feature to notify clients of the limitation. MQTT v3.1.1 clients will not be aware of the limitation. Clients publishing to this listener with a too-high QoS will be disconnected. Not reloaded on reload signal. number This option sets the maximum number topic aliases that an MQTT v5 client is allowed to create. This option applies per listener. Defaults to 10. Set to 0 to disallow topic aliases. The maximum value possible is 65535. Not reloaded on reload signal. topic prefix This option is used with the listener option to isolate groups of clients. When a client connects to a listener which uses this option, the string argument is attached to the start of all topics for this client. This prefix is removed when any messages are sent to the client. This means a client connected to a listener with mount point example can only see messages that are published in the topic hierarchy example and below. Not reloaded on reload signal. port number This option is deprecated and will be removed in a future version. Use the instead. Set the network port for the default listener to listen on. Defaults to 1883. Not reloaded on reload signal. It is recommended to use an explicit rather than rely on the implicit default listener options like this. value Set the protocol to accept for the current listener. Can be , the default, or if available. Websockets support is currently disabled by default at compile time. Certificate based TLS may be used with websockets, except that only the , , , , and options are supported. Not reloaded on reload signal. [ ipv4 | ipv6 ] By default, a listener will attempt to listen on all supported IP protocol versions. If you do not have an IPv4 or IPv6 interface you may wish to disable support for either of those protocol versions. In particular, note that due to the limitations of the websockets library, it will only ever attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail if IPv6 is not available. Set to to force the listener to only use IPv4, or set to to force the listener to only use IPv6. If you want support for both IPv4 and IPv6, then do not use the option. Not reloaded on reload signal. [ true | false ] Set to true to replace the clientid that a client connected with its username. This allows authentication to be tied to the clientid, which means that it is possible to prevent one client disconnecting another by using the same clientid. Defaults to false. If a client connects with no username it will be disconnected as not authorised when this option is set to true. Do not use in conjunction with . This does not apply globally, but on a per-listener basis. See also . Not reloaded on reload signal. level Change the websockets logging level. This is a global option, it is not possible to set per listener. This is an integer that is interpreted by libwebsockets as a bit mask for its lws_log_levels enum. See the libwebsockets documentation for more details. To use this option, must also be enabled. Defaults to 0. size Change the websockets headers size. This is a global option, it is not possible to set per listener. This option sets the size of the buffer used in the libwebsockets library when reading HTTP headers. If you are passing large header data such as cookies then you may need to increase this value. If left unset, or set to 0, then the default of 1024 bytes will be used. Certificate based SSL/TLS Support The following options are available for all listeners to configure certificate based SSL support. See also "Pre-shared-key based SSL/TLS support". file path is used to define the path to a file containing the PEM encoded CA certificates that are trusted when checking incoming client certificates. directory path is used to define a directory that contains PEM encoded CA certificates that are trusted when checking incoming client certificates. For to work correctly, the certificates files must have ".pem" as the file ending and you must run "openssl rehash <path to capath>" each time you add/remove a certificate. file path Path to the PEM encoded server certificate. This option and must be present to enable certificate based TLS encryption. The certificate pointed to by this option will be reloaded when Mosquitto receives a SIGHUP signal. This can be used to load new certificates prior to the existing ones expiring. cipher:list The list of allowed ciphers for this listener, for TLS v1.2 and earlier only, each separated with a colon. Available ciphers can be obtained using the "openssl ciphers" command. cipher:list The list of allowed ciphersuites for this listener, for TLS v1.3, each separated with a colon. file path If you have set to true, you can create a certificate revocation list file to revoke access to particular client certificates. If you have done this, use crlfile to point to the PEM encoded revocation file. file path To allow the use of ephemeral DH key exchange, which provides forward security, the listener must load DH parameters. This can be specified with the dhparamfile option. The dhparamfile can be generated with the command e.g. openssl dhparam -out dhparam.pem 2048 file path If equals "pem" this is the path to the PEM encoded server key. This option and must be present to enable certificate based TLS encryption. If is "engine" this represents the engine handle of the private key. The private key pointed to by this option will be reloaded when Mosquitto receives a SIGHUP signal. This can be used to load new keys prior to the existing ones expiring. [ true | false ] By default an SSL/TLS enabled listener will operate in a similar fashion to a https enabled web server, in that the server has a certificate signed by a CA and the client will verify that it is a trusted certificate. The overall aim is encryption of the network traffic. By setting to true, a client connecting to this listener must provide a valid certificate in order for the network connection to proceed. This allows access to the broker to be controlled outside of the mechanisms provided by MQTT. engine A valid openssl engine id. These can be listed with openssl engine command. engine_kpass_sha1 SHA1 of the private key password when using an TLS engine. Some TLS engines such as the TPM engine may require the use of a password in order to be accessed. This option allows a hex encoded SHA1 hash of the password to the engine directly, instead of the user being prompted for the password. [ pem | engine ] Specifies the type of private key in use when making TLS connections.. This can be "pem" or "engine". This parameter is useful when a TPM module is being used and the private key has been created with it. Defaults to "pem", which means normal private key files are used. version Configure the minimum version of the TLS protocol to be used for this listener. Possible values are tlsv1.3, tlsv1.2 and tlsv1.1. If left unset, the default allows TLS v1.3 and v1.2. In Mosquitto version 1.6.x and earlier, this option set the only TLS protocol version that was allowed, rather than the minimum. [ true | false ] If is true, you may set to true to use the CN value from the client certificate as a username. If this is true, the option will not be used for this listener. This takes priority over if both are set to true. See also [ true | false ] If is true, you may set to true to use the complete subject value from the client certificate as a username. If this is true, the option will not be used for this listener. The subject will be generated in a form similar to . See also Pre-shared-key based SSL/TLS Support The following options are available for all listeners to configure pre-shared-key based SSL support. See also "Certificate based SSL/TLS support". cipher:list When using PSK, the encryption ciphers used will be chosen from the list of available PSK ciphers. If you want to control which ciphers are available, use this option. The list of available ciphers can be optained using the "openssl ciphers" command and should be provided in the same format as the output of that command. hint The option enables pre-shared-key support for this listener and also acts as an identifier for this listener. The hint is sent to clients and may be used locally to aid authentication. The hint is a free form string that doesn't have much meaning in itself, so feel free to be creative. If this option is provided, see to define the pre-shared keys to be used or create a security plugin to handle them. version Configure the minimum version of the TLS protocol to be used for this listener. Possible values are tlsv1.3, tlsv1.2 and tlsv1.1. If left unset, the default of allowing TLS v1.3 and v1.2. In Mosquitto version 1.6.x and earlier, this option set the only TLS protocol version that was allowed, rather than the minimum. [ true | false ] Set to have the psk identity sent by the client used as its username. The username will be checked as normal, so or another means of authentication checking must be used. No password will be used. Configuring Bridges Multiple bridges (connections to other brokers) can be configured using the following variables. Bridges cannot currently be reloaded on reload signal. address[:port] [address[:port]] address[:port] [address[:port]] Specify the address and optionally the port of the bridge to connect to. This must be given for each bridge connection. If the port is not specified, the default of 1883 is used. If you use an IPv6 address, then the port is not optional. Multiple host addresses can be specified on the address config. See the option for more details on the behaviour of bridges with multiple addresses. [ true | false ] If a bridge has topics that have "out" direction, the default behaviour is to send an unsubscribe request to the remote broker on that topic. This means that changing a topic direction from "in" to "out" will not keep receiving incoming messages. Sending these unsubscribe requests is not always desirable, setting to false will disable sending the unsubscribe request. Defaults to true. ip address If you need to have the bridge connect over a particular network interface, use bridge_bind_address to tell the bridge which local IP address the socket should bind to, e.g. . value If you wish to restrict the size of messages sent to a remote bridge, use this option. This sets the maximum number of bytes for the total message, including headers and payload. Note that MQTT v5 brokers may provide their own maximum-packet-size property. In this case, the smaller of the two limits will be used. Set to 0 for "unlimited". [ true | false ] Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism for brokers to tell clients that they do not support retained messages, but this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a v3.1.1 or v3.1 broker that does not support retained messages, set the option to false. This will remove the retain bit on all outgoing messages to that bridge, regardless of any other setting. Defaults to true. version Set the version of the MQTT protocol to use with for this bridge. Can be one of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311. [ true | false ] Set the clean session option for this bridge. Setting to false (the default), means that all subscriptions on the remote broker are kept in case of the network connection dropping. If set to true, all subscriptions and messages on the remote broker will be cleaned up if the connection drops. Note that setting to true may cause a large amount of retained messages to be sent each time the bridge reconnects. If you are using bridges with set to false (the default), then you may get unexpected behaviour from incoming topics if you change what topics you are subscribing to. This is because the remote broker keeps the subscription for the old topic. If you have this problem, connect your bridge with set to true, then reconnect with cleansession set to false as normal. [ true | false] The regular covers both the local subscriptions and the remote subscriptions. local_cleansession allows splitting this. Setting false will mean that the local connection will preserve subscription, independent of the remote connection. Defaults to the value of bridge.cleansession unless explicitly specified. name This variable marks the start of a new bridge connection. It is also used to give the bridge a name which is used as the client id on the remote broker. seconds Set the number of seconds after which the bridge should send a ping if no other traffic has occurred. Defaults to 60. A minimum value of 5 seconds is allowed. seconds Set the amount of time a bridge using the lazy start type must be idle before it will be stopped. Defaults to 60 seconds. id Set the clientid to use on the local broker. If not defined, this defaults to . If you are bridging a broker to itself, it is important that local_clientid and remote_clientid do not match. password Configure the password to be used when connecting this bridge to the local broker. This may be important when authentication and ACLs are being used. username Configure the username to be used when connecting this bridge to the local broker. This may be important when authentication and ACLs are being used. [ true | false ] If set to true, publish notification messages to the local and remote brokers giving information about the state of the bridge connection. Retained messages are published to the topic $SYS/broker/connection/<remote_clientid>/state unless otherwise set with s. If the message is 1 then the connection is active, or 0 if the connection has failed. Defaults to true. This uses the Last Will and Testament (LWT) feature. [ true | false ] If set to true, only publish notification messages to the local broker giving information about the state of the bridge connection. Defaults to false. topic Choose the topic on which notifications will be published for this bridge. If not set the messages will be sent on the topic $SYS/broker/connection/<remote_clientid>/state. id Set the client id for this bridge connection. If not defined, this defaults to 'name.hostname', where name is the connection name and hostname is the hostname of this computer. This replaces the old "clientid" option to avoid confusion with local/remote sides of the bridge. "clientid" remains valid for the time being. value Configure a password for the bridge. This is used for authentication purposes when connecting to a broker that supports MQTT v3.1 and up and requires a username and/or password to connect. This option is only valid if a remote_username is also supplied. This replaces the old "password" option to avoid confusion with local/remote sides of the bridge. "password" remains valid for the time being. name Configure a username for the bridge. This is used for authentication purposes when connecting to a broker that supports MQTT v3.1 and up and requires a username and/or password to connect. See also the option. This replaces the old "username" option to avoid confusion with local/remote sides of the bridge. "username" remains valid for the time being. base cap constant Set the amount of time a bridge using the automatic start type will wait until attempting to reconnect. This option can be configured to use a constant delay time in seconds, or to use a backoff mechanism based on "Decorrelated Jitter", which adds a degree of randomness to when the restart occurs, starting at the base and increasing up to the cap. Set a constant timeout of 20 seconds: restart_timeout 20 Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of 60 seconds: restart_timeout 10 30 Defaults to jitter with a base of 5 seconds and cap of 30 seconds. [ true | false ] If the bridge has more than one address given in the address/addresses configuration, the round_robin option defines the behaviour of the bridge on a failure of the bridge connection. If round_robin is false, the default value, then the first address is treated as the main bridge connection. If the connection fails, the other secondary addresses will be attempted in turn. Whilst connected to a secondary bridge, the bridge will periodically attempt to reconnect to the main bridge until successful. If round_robin is true, then all addresses are treated as equals. If a connection fails, the next address will be tried and if successful will remain connected until it fails. [ automatic | lazy | once ] Set the start type of the bridge. This controls how the bridge starts and can be one of three types: automatic, lazy and once. Note that RSMB provides a fourth start type "manual" which isn't currently supported by mosquitto. automatic is the default start type and means that the bridge connection will be started automatically when the broker starts and also restarted after a short delay (30 seconds) if the connection fails. Bridges using the lazy start type will be started automatically when the number of queued messages exceeds the number set with the option. It will be stopped automatically after the time set by the parameter. Use this start type if you wish the connection to only be active when it is needed. A bridge using the once start type will be started automatically when the broker starts but will not be restarted if the connection fails. count Set the number of messages that need to be queued for a bridge with lazy start type to be restarted. Defaults to 10 messages. pattern [[[ out | in | both ] qos-level] local-prefix remote-prefix] Define a topic pattern to be shared between the two brokers. Any topics matching the pattern (which may include wildcards) are shared. The second parameter defines the direction that the messages will be shared in, so it is possible to import messages from a remote broker using in, export messages to a remote broker using out or share messages in both directions. If this parameter is not defined, the default of out is used. The QoS level defines the publish/subscribe QoS level used for this topic and defaults to 0. The local-prefix and remote-prefix options allow topics to be remapped when publishing to and receiving from remote brokers. This allows a topic tree from the local broker to be inserted into the topic tree of the remote broker at an appropriate place. For incoming topics, the bridge will prepend the pattern with the remote prefix and subscribe to the resulting topic on the remote broker. When a matching incoming message is received, the remote prefix will be removed from the topic and then the local prefix added. For outgoing topics, the bridge will prepend the pattern with the local prefix and subscribe to the resulting topic on the local broker. When an outgoing message is processed, the local prefix will be removed from the topic then the remote prefix added. When using topic mapping, an empty prefix can be defined using the place marker "". Using the empty marker for the topic itself is also valid. The table below defines what combination of empty or value is valid. The and show the resulting topics that would be used on the local and remote ends of the bridge. For example, for the first table row if you publish to on the local broker, then the remote broker will receive a message on the topic . Pattern Local Prefix Remote Prefix Validity Full Local Topic Full Remote Topic patternL/R/validL/patternR/pattern patternL/""validL/patternpattern pattern""R/validpatternR/pattern pattern""""valid (no remapping)patternpattern ""localremotevalid (remap single local topic to remote)localremote ""local""invalid """"remoteinvalid """"""invalid To remap an entire topic tree, use e.g.: topic # both 2 local/topic/ remote/topic/ This option can be specified multiple times per bridge. Care must be taken to ensure that loops are not created with this option. If you are experiencing high CPU load from a broker, it is possible that you have a loop where each broker is forever forwarding each other the same messages. See also the option if you have messages arriving on unexpected topics when using incoming topics. The configuration below connects a bridge to the broker at . It subscribes to the remote topic and republishes the messages received to the local topic connection test-mosquitto-org address test.mosquitto.org cleansession true topic clients/total in 0 test/mosquitto/org/ $SYS/broker/ [ true | false ] If try_private is set to true, the bridge will attempt to indicate to the remote broker that it is a bridge not an ordinary client. If successful, this means that loop detection will be more effective and that retained messages will be propagated correctly. Not all brokers support this feature so it may be necessary to set to false if your bridge does not connect properly. Defaults to true. SSL/TLS Support The following options are available for all bridges to configure SSL/TLS support. alpn Configure the application layer protocol negotiation option for the TLS session. Useful for brokers that support both websockets and MQTT on the same port. file path One of or must be provided to allow SSL/TLS support. bridge_cafile is used to define the path to a file containing the PEM encoded CA certificates that have signed the certificate for the remote broker. file path One of or must be provided to allow SSL/TLS support. bridge_capath is used to define the path to a directory containing the PEM encoded CA certificates that have signed the certificate for the remote broker. For bridge_capath to work correctly, the certificate files must have ".crt" as the file ending and you must run "openssl rehash <path to bridge_capath>" each time you add/remove a certificate. file path Path to the PEM encoded client certificate for this bridge, if required by the remote broker. identity Pre-shared-key encryption provides an alternative to certificate based encryption. A bridge can be configured to use PSK with the and options. This is the client identity used with PSK encryption. Only one of certificate and PSK based encryption can be used on one bridge at once. [ true | false ] When using certificate based TLS, the bridge will attempt to verify the hostname provided in the remote certificate matches the host/address being connected to. This may cause problems in testing scenarios, so may be set to true to disable the hostname verification. Setting this option to true means that a malicious third party could potentially impersonate your server, so it should always be set to false in production environments. file path Path to the PEM encoded private key for this bridge, if required by the remote broker. key Pre-shared-key encryption provides an alternative to certificate based encryption. A bridge can be configured to use PSK with the and options. This is the pre-shared-key in hexadecimal format with no "0x". Only one of certificate and PSK based encryption can be used on one bridge at once. [ true | false ] When set to true, the bridge requires OCSP on the TLS connection it opens as client. version Configure the version of the TLS protocol to be used for this bridge. Possible values are tlsv1.3, tlsv1.2 and tlsv1.1. Defaults to tlsv1.2. The remote broker must support the same version of TLS for the connection to succeed. Files mosquitto.conf Bugs mosquitto bug information can be found at See Also mosquitto 8 mosquitto_passwd 1 mosquitto-tls 7 mqtt 7 limits.conf 5 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto_ctrl.1.meta000066400000000000000000000001571450213760600205270ustar00rootroot00000000000000.. title: mosquitto_ctrl man page .. slug: mosquitto_ctrl-1 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto_ctrl.1.xml000066400000000000000000000703021450213760600204000ustar00rootroot00000000000000 mosquitto_ctrl 1 Mosquitto Project Commands mosquitto_ctrl a tool for initialising/configuring a Mosquitto broker instance mosquitto_ctrl connection-options | -o config-file module-name module-command command-options connection-options: hostname socket path port-number username password URL bind-address client-id message-QoS protocol-version file dir file file ciphers version protocol engine pem engine kpass-sha1 hex-key identity ciphers version socks-url mosquitto_ctrl Description mosquitto_ctrl is a tool for helping configure a Mosquitto broker instance. Encrypted Connections mosquitto_ctrl supports TLS encrypted connections. It is strongly recommended that you use an encrypted connection for all remote use of mosquitto_ctrl. To enable TLS connections when using x509 certificates, one of either or must be provided as an option. To enable TLS connections when using TLS-PSK, you must use the and the options. Modules Authentication, and role based access control with users and groups. Uses the dynsec module name. See: mosquitto_ctrl_dynsec 1 mosquitto_ctrl has the ability to load external modules in the form of shared libraries. For example using the module name will try to load the external module or , depending on platform. This allows new functionality to be added to Mosquitto by combining a plugin and mosquitto_ctrl module, without having to recompile any Mosquitto source code. Connection Options The options below may be given on the command line, but may also be placed in a config file located at or . The config file may be specified manually with the option. The config file should have one pair of per line. The values in the config file will be used as defaults and can be overridden by using the command line. The exceptions to this are the message type options, of which only one can be specified. Note also that currently some options cannot be negated, e.g. . Config file lines that have a as the first character are treated as comments and not processed any further. Bind the outgoing connection to a local ip address/hostname. Use this argument if you need to restrict network communication to a particular interface. Define the path to a file containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. See also Define the path to a directory containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. For to work correctly, the certificate files must have ".crt" as the file ending and you must run "openssl rehash <path to capath>" each time you add/remove a certificate. See also Define the path to a file containing a PEM encoded certificate for this client, if required by the server. See also . An openssl compatible list of TLS ciphers to support in the client. See ciphers1 for more information. Enable debug messages. Use an MQTT v5 property with this publish. If you use this option, the client will be set to be an MQTT v5 client. This option has two forms: is the MQTT command/packet identifier and can be one of CONNECT, PUBLISH, PUBREL, DISCONNECT, AUTH, or WILL. The properties available for each command are listed in the Properties section. is the name of the property to add. This is as described in the specification, but with '-' as a word separator. For example: . More details are in the Properties section. is the value of the property to add, with a data type that is property specific. is only used for the property as the first of the two strings in the string pair. In that case, is the second of the strings in the pair. Display usage information. Specify the host to connect to. Defaults to localhost. The id to use for this client. If not given, a client id will be generated depending on the MQTT version being used. For v3.1.1/v3.1, the client generates a client id in the format , where the are replaced with random alphanumeric characters. For v5.0, the client sends a zero length client id, and the server will generate a client id for the client. This option cannot be used at the same time as the argument. When using certificate based encryption, this option disables verification of the server hostname in the server certificate. This can be useful when testing initial server configurations but makes it possible for a malicious third party to impersonate your server through DNS spoofing, for example. Use this option in testing only. If you need to resort to using this option in a production environment, your setup is at fault and there is no point using encryption. Define the path to a file containing a PEM encoded private key for this client, if required by the server. See also . Specifies the type of private key in use when making TLS connections.. This can be "pem" or "engine". This parameter is useful when a TPM module is being used and the private key has been created with it. Defaults to "pem", which means normal private key files are used. See also . Specify specify user, password, hostname, port and topic at once as a URL. The URL must be in the form: mqtt(s)://[username[:password]@]host[:port]/topic If the scheme is mqtt:// then the port defaults to 1883. If the scheme is mqtts:// then the port defaults to 8883. Disable Nagle's algorithm for the socket. This means that latency of sent messages is reduced, which is particularly noticeable for small, reasonably infrequent messages. Using this option may result in more packets being sent than would normally be necessary. config-file Provide a path to a config file to load options from. The config file should have one pair of per line. The values in the config file will be used as defaults and can be overridden by using the command line. The exceptions to this are the message type options, of which only one can be specified. Note also that currently some options cannot be negated, e.g. . Config file lines that have a as the first character are treated as comments and not processed any further. Connect to the port specified. If not given, the default of 1883 for plain MQTT or 8883 for MQTT over TLS will be used. Provide a password to be used for authenticating with the broker. Using this argument without also specifying a username is invalid when using MQTT v3.1 or v3.1.1. See also the option. Specify a SOCKS5 proxy to connect through. "None" and "username" authentication types are supported. The must be of the form . The protocol prefix means that hostnames are resolved by the proxy. The symbols %25, %3A and %40 are URL decoded into %, : and @ respectively, if present in the username or password. If username is not given, then no authentication is attempted. If the port is not given, then the default of 1080 is used. More SOCKS versions may be available in the future, depending on demand, and will use different protocol prefixes as described in curl 1 . Provide the hexadecimal (no leading 0x) pre-shared-key matching the one used on the broker to use TLS-PSK encryption support. must also be provided to enable TLS-PSK. The client identity to use with TLS-PSK support. This may be used instead of a username if the broker is configured to do so. Specify the quality of service to use for messages, from 0, 1 and 2. Defaults to 1. If this argument is given, no runtime errors will be printed. This excludes any error messages given in case of invalid user input (e.g. using without a port). Provide a protocol to use when connecting to a broker that has multiple protocols available on a single port, e.g. MQTT and WebSockets. A valid openssl engine id. These can be listed with openssl engine command. See also . SHA1 of the private key password when using an TLS engine. Some TLS engines such as the TPM engine may require the use of a password in order to be accessed. This option allows a hex encoded SHA1 hash of the password to the engine directly, instead of the user being prompted for the password. See also . Choose which TLS protocol version to use when communicating with the broker. Valid options are , and . The default value is . Must match the protocol version used by the broker. Provide a username to be used for authenticating with the broker. See also the argument. Connect to a broker through a local unix domain socket instead of a TCP socket. This is a replacement for and . For example: See the option in mosquitto.conf 5 to configure Mosquitto to listen on a unix socket. Specify which version of the MQTT protocol should be used when connecting to the rmeote broker. Can be , , , or the more verbose , , or . Defaults to . Properties The / option allows adding properties to different stages of the mosquitto_ctrl run. The properties supported for each command are as follows: Connect (binary data - note treated as a string in mosquitto_ctrl) (UTF-8 string pair) (32-bit unsigned integer) (16-bit unsigned integer) (8-bit unsigned integer) (8-bit unsigned integer) (32-bit unsigned integer, note use instead) (16-bit unsigned integer) (UTF-8 string pair) Publish (UTF-8 string) (binary data - note treated as a string in mosquitto_ctrl) (32-bit unsigned integer) (8-bit unsigned integer) (UTF-8 string) (16-bit unsigned integer) (UTF-8 string pair) Disconnect (32-bit unsigned integer) (UTF-8 string pair) Will properties (UTF-8 string) (binary data - note treated as a string in mosquitto_ctrl) (32-bit unsigned integer) (8-bit unsigned integer) (UTF-8 string) (UTF-8 string pair) (32-bit unsigned integer) Exit Status mosquitto_sub returns zero on success, or non-zero on error. If the connection is refused by the broker at the MQTT level, then the exit code is the CONNACK reason code. If another error occurs, the exit code is a libmosquitto return value. MQTT v3.1.1 CONNACK codes: Success Connection refused: Bad protocol version Connection refused: Identifier rejected Connection refused: Server unavailable Connection refused: Bad username/password Connection refused: Not authorized MQTT v5 CONNACK codes: Success Unspecified error Malformed packet Protocol error Implementation specific error Unsupported protocol version Client ID not valid Bad username or password Not authorized Server unavailable Server busy Banned Server shutting down Bad authentication method Keep alive timeout Session taken over Topic filter invalid Topic name invalid Receive maximum exceeded Topic alias invalid Packet too large Message rate too high Quota exceeded Administrative action Payload format invalid Retain not supported QoS not supported Use another server Server moved Shared subscriptions not supported Connection rate exceeded Maximum connect time Subscription IDs not supported Wildcard subscriptions not supported Bugs mosquitto bug information can be found at See Also mqtt 7 mosquitto_rr 1 mosquitto_pub 1 mosquitto_sub 1 mosquitto 8 libmosquitto 3 mosquitto-tls 7 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto_ctrl_dynsec.1.meta000066400000000000000000000002071450213760600220700ustar00rootroot00000000000000.. title: mosquitto_ctrl dynamic security man page .. slug: mosquitto_ctrl_dynsec-1 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto_ctrl_dynsec.1.xml000066400000000000000000000065631450213760600217550ustar00rootroot00000000000000 mosquitto_ctrl_dynsec 1 Mosquitto Project Commands mosquitto_ctrl_dynsec mosquitto_ctrl module for controlling the Mosquitto Dynamic Security plugin. mosquitto_ctrl connection-options dynsec dynsec-command command-options Description This page describes the dynsec module for mosquitto_ctrl 1. See the mosquitto_ctrl man page for details of the options for connecting to remote brokers, in particular since this module works with authentication and access control, it is crucial that secure encrypted connections are used. Commands Configuration file initialisation mosquitto_ctrl dynsec init config-filename admin-user [admin-password] Bugs mosquitto bug information can be found at See Also mqtt 7 mosquitto_rr 1 mosquitto_pub 1 mosquitto_sub 1 mosquitto 8 libmosquitto 3 mosquitto-tls 7 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto_passwd.1.meta000066400000000000000000000001631450213760600210610ustar00rootroot00000000000000.. title: mosquitto_passwd man page .. slug: mosquitto_passwd-1 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto_passwd.1.xml000066400000000000000000000226251450213760600207420ustar00rootroot00000000000000 mosquitto_passwd 1 Mosquitto Project Commands mosquitto_passwd manage password files for mosquitto mosquitto_passwd hash passwordfile username mosquitto_passwd hash passwordfile username password mosquitto_passwd passwordfile Description mosquitto_passwd is a tool for managing password files for the mosquitto MQTT broker. Usernames must not contain ":". Passwords are stored in a similar format to crypt3. Options Run in batch mode. This allows the password to be provided at the command line which can be convenient but should be used with care because the password will be visible on the command line and in command history. Create a new password file. If the file already exists, it will be overwritten. Delete the specified user from the password file. Choose the hash to use. Can be one of sha512-pbkdf2 or sha512. Defaults to sha512-pbkdf2. The sha512 option is provided for creating password files for use with Mosquitto 1.6 and earlier. This option can be used to upgrade/convert a password file with plain text passwords into one using hashed passwords. It will modify the specified file. It does not detect whether passwords are already hashed, so using it on a password file that already contains hashed passwords will generate new hashes based on the old hashes and render the password file unusable. The password file to modify. The username to add/update/delete. The password to use when in batch mode. Exit Status mosquitto_sub returns zero on success, or non-zero on error. If the connection is refused by the broker at the MQTT level, then the exit code is the CONNACK reason code. If another error occurs, the exit code is a libmosquitto return value. MQTT v3.1.1 CONNACK codes: Success Connection refused: Bad protocol version Connection refused: Identifier rejected Connection refused: Server unavailable Connection refused: Bad username/password Connection refused: Not authorized MQTT v5 CONNACK codes: Success Unspecified error Malformed packet Protocol error Implementation specific error Unsupported protocol version Client ID not valid Bad username or password Not authorized Server unavailable Server busy Banned Server shutting down Bad authentication method Keep alive timeout Session taken over Topic filter invalid Topic name invalid Receive maximum exceeded Topic alias invalid Packet too large Message rate too high Quota exceeded Administrative action Payload format invalid Retain not supported QoS not supported Use another server Server moved Shared subscriptions not supported Connection rate exceeded Maximum connect time Subscription IDs not supported Wildcard subscriptions not supported Examples Add a user to a new password file: mosquitto_passwd -c /etc/mosquitto/passwd ral Delete a user from a password file mosquitto_passwd -D /etc/mosquitto/passwd ral Bugs mosquitto bug information can be found at See Also mosquitto 8 mosquitto.conf 5 mqtt 7 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto_pub.1.meta000066400000000000000000000001551450213760600203470ustar00rootroot00000000000000.. title: mosquitto_pub man page .. slug: mosquitto_pub-1 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto_pub.1.xml000066400000000000000000001134541450213760600202300ustar00rootroot00000000000000 mosquitto_pub 1 Mosquitto Project Commands mosquitto_pub an MQTT version 5/3.1.1/3.1 client for publishing simple messages mosquitto_pub hostname socket path port-number username password message-topic URL bind-address command identifier value client-id client-id-prefix keepalive-time message-QoS count seconds protocol-version session-expiry-interval file message topic payload qos file dir file file ciphers version protocol engine pem engine kpass-sha1 hex-key identity ciphers version socks-url mosquitto_pub Description mosquitto_pub is a simple MQTT version 5/3.1.1 client that will publish a single message on a topic and exit. Encrypted Connections mosquitto_pub supports TLS encrypted connections. It is strongly recommended that you use an encrypted connection for anything more than the most basic setup. To enable TLS connections when using x509 certificates, one of either or can be provided as an option. Alternatively, if the option is used then the OS provided certificates will be loaded and neither or are needed To enable TLS connections when using TLS-PSK, you must use the and the options. Options The options below may be given on the command line, but may also be placed in a config file located at or with one pair of per line. The values in the config file will be used as defaults and can be overridden by using the command line. The exceptions to this are the message type options, of which only one can be specified. Note also that currently some options cannot be negated, e.g. . Config file lines that have a as the first character are treated as comments and not processed any further. Bind the outgoing connection to a local ip address/hostname. Use this argument if you need to restrict network communication to a particular interface. Disable 'clean session' / enable persistent client mode. When this argument is used, the broker will be instructed not to clean existing sessions for the same client id when the client connects, and sessions will never expire when the client disconnects. MQTT v5 clients can change their session expiry interval with the argument. When a session is persisted on the broker, the subscriptions for the client will be maintained after it disconnects, along with subsequent QoS 1 and QoS 2 messages that arrive. When the client reconnects and does not clean the session, it will receive all of the queued messages. If using this option, the client id must be set manually with Define the path to a file containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. See also Define the path to a directory containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. For to work correctly, the certificate files must have ".crt" as the file ending and you must run "openssl rehash <path to capath>" each time you add/remove a certificate. See also Define the path to a file containing a PEM encoded certificate for this client, if required by the server. See also . An openssl compatible list of TLS ciphers to support in the client. See ciphers1 for more information. Enable debug messages. Use an MQTT v5 property with this publish. If you use this option, the client will be set to be an MQTT v5 client. This option has two forms: is the MQTT command/packet identifier and can be one of CONNECT, PUBLISH, PUBREL, DISCONNECT, AUTH, or WILL. The properties available for each command are listed in the Properties section. is the name of the property to add. This is as described in the specification, but with '-' as a word separator. For example: . More details are in the Properties section. is the value of the property to add, with a data type that is property specific. is only used for the property as the first of the two strings in the string pair. In that case, is the second of the strings in the pair. Send the contents of a file as the message. Display usage information. Specify the host to connect to. Defaults to localhost. The id to use for this client. If not given, a client id will be generated depending on the MQTT version being used. For v3.1.1/v3.1, the client generates a client id in the format , where the are replaced with random alphanumeric characters. For v5.0, the client sends a zero length client id, and the server will generate a client id for the client. This option cannot be used at the same time as the argument. Provide a prefix that the client id will be built from by appending the process id of the client. This is useful where the broker is using the clientid_prefixes option. Cannot be used at the same time as the argument. When using certificate based encryption, this option disables verification of the server hostname in the server certificate. This can be useful when testing initial server configurations but makes it possible for a malicious third party to impersonate your server through DNS spoofing, for example. Use this option in testing only. If you need to resort to using this option in a production environment, your setup is at fault and there is no point using encryption. The number of seconds between sending PING commands to the broker for the purposes of informing it we are still connected and functioning. Defaults to 60 seconds. Define the path to a file containing a PEM encoded private key for this client, if required by the server. See also . Specifies the type of private key in use when making TLS connections.. This can be "pem" or "engine". This parameter is useful when a TPM module is being used and the private key has been created with it. Defaults to "pem", which means normal private key files are used. See also . Specify specify user, password, hostname, port and topic at once as a URL. The URL must be in the form: mqtt(s)://[username[:password]@]host[:port]/topic If the scheme is mqtt:// then the port defaults to 1883. If the scheme is mqtts:// then the port defaults to 8883. Send messages read from stdin, splitting separate lines into separate messages. Send a single message from the command line. Send a null (zero length) message. Disable Nagle's algorithm for the socket. This means that latency of sent messages is reduced, which is particularly noticeable for small, reasonably infrequent messages. Using this option may result in more packets being sent than would normally be necessary. Connect to the port specified. If not given, the default of 1883 for plain MQTT or 8883 for MQTT over TLS will be used. Provide a password to be used for authenticating with the broker. Using this argument without also specifying a username is invalid when using MQTT v3.1 or v3.1.1. See also the option. Specify a SOCKS5 proxy to connect through. "None" and "username" authentication types are supported. The must be of the form . The protocol prefix means that hostnames are resolved by the proxy. The symbols %25, %3A and %40 are URL decoded into %, : and @ respectively, if present in the username or password. If username is not given, then no authentication is attempted. If the port is not given, then the default of 1080 is used. More SOCKS versions may be available in the future, depending on demand, and will use different protocol prefixes as described in curl 1 . Provide the hexadecimal (no leading 0x) pre-shared-key matching the one used on the broker to use TLS-PSK encryption support. must also be provided to enable TLS-PSK. The client identity to use with TLS-PSK support. This may be used instead of a username if the broker is configured to do so. Specify the quality of service to use for the message, from 0, 1 and 2. Defaults to 0. If this argument is given, no runtime errors will be printed. This excludes any error messages given in case of invalid user input (e.g. using without a port). If retain is given, the message will be retained as a "last known good" value on the broker. See mqtt7 for more information. Note that zero length payloads are never retained. If you send a zero length payload retained message it will clear any retained message on the topic. If the publish mode is, , or (i.e. the modes where only a single message is sent), then can be used to specify that the message will be published multiple times. See also . If using , then the default behaviour is to publish repeated messages as soon as the previous message is delivered. Use to specify the number of seconds to wait after the previous message was delivered before publishing the next. Does not need to be an integer number of seconds. Note that there is no guarantee as to the actual interval between messages, this option simply defines the minimum time from delivery of one message to the start of the publish of the next. Send a message read from stdin, sending the entire content as a single message. Use SRV lookups to determine which host to connect to. Performs lookups to when used in conjunction with , otherwise uses . The MQTT topic on which to publish the message. See mqtt7 for more information on MQTT topics. Provide a protocol to use when connecting to a broker that has multiple protocols available on a single port, e.g. MQTT and WebSockets. A valid openssl engine id. These can be listed with openssl engine command. See also . SHA1 of the private key password when using an TLS engine. Some TLS engines such as the TPM engine may require the use of a password in order to be accessed. This option allows a hex encoded SHA1 hash of the password to the engine directly, instead of the user being prompted for the password. See also . If used, this will load and trust the OS provided CA certificates. This can be used in conjunction with and and can be used on its own to enable TLS mode. This will be set by default if is used, or if port is 8883 and no other certificate options are used. Choose which TLS protocol version to use when communicating with the broker. Valid options are , and . The default value is . Must match the protocol version used by the broker. Provide a username to be used for authenticating with the broker. See also the argument. Connect to a broker through a local unix domain socket instead of a TCP socket. This is a replacement for and . For example: See the option in mosquitto.conf 5 to configure Mosquitto to listen on a unix socket. Specify which version of the MQTT protocol should be used when connecting to the rmeote broker. Can be , , , or the more verbose , , or . Defaults to . Specify a message that will be stored by the broker and sent out if this client disconnects unexpectedly. This must be used in conjunction with . The QoS to use for the Will. Defaults to 0. This must be used in conjunction with . If given, if the client disconnects unexpectedly the message sent out will be treated as a retained message. This must be used in conjunction with . Note that zero length payloads are never retained. If you send a zero length payload retained message it will clear any retained message on the topic. The topic on which to send a Will, in the event that the client disconnects unexpectedly. Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5 clients only. Set to 0-4294967294 to specify the session will expire in that many seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given. If the session is set to never expire, either with -x or -c, then a client id must be provided. Wills mosquitto_sub can register a message with the broker that will be sent out if it disconnects unexpectedly. See mqtt7 for more information. The minimum requirement for this is to use to specify which topic the will should be sent out on. This will result in a non-retained, zero length message with QoS 0. Use the , and arguments to modify the other will parameters. Properties The / option allows adding properties to different stages of the mosquitto_pub run. The properties supported for each command are as follows: Connect (binary data - note treated as a string in mosquitto_pub) (UTF-8 string pair) (32-bit unsigned integer) (16-bit unsigned integer) (8-bit unsigned integer) (8-bit unsigned integer) (32-bit unsigned integer, note use instead) (16-bit unsigned integer) (UTF-8 string pair) Publish (UTF-8 string) (binary data - note treated as a string in mosquitto_pub) (32-bit unsigned integer) (8-bit unsigned integer) (UTF-8 string) (16-bit unsigned integer) (UTF-8 string pair) Disconnect (32-bit unsigned integer) (UTF-8 string pair) Will properties (UTF-8 string) (binary data - note treated as a string in mosquitto_pub) (32-bit unsigned integer) (8-bit unsigned integer) (UTF-8 string) (UTF-8 string pair) (32-bit unsigned integer) Exit Status mosquitto_sub returns zero on success, or non-zero on error. If the connection is refused by the broker at the MQTT level, then the exit code is the CONNACK reason code. If another error occurs, the exit code is a libmosquitto return value. MQTT v3.1.1 CONNACK codes: Success Connection refused: Bad protocol version Connection refused: Identifier rejected Connection refused: Server unavailable Connection refused: Bad username/password Connection refused: Not authorized MQTT v5 CONNACK codes: Success Unspecified error Malformed packet Protocol error Implementation specific error Unsupported protocol version Client ID not valid Bad username or password Not authorized Server unavailable Server busy Banned Server shutting down Bad authentication method Keep alive timeout Session taken over Topic filter invalid Topic name invalid Receive maximum exceeded Topic alias invalid Packet too large Message rate too high Quota exceeded Administrative action Payload format invalid Retain not supported QoS not supported Use another server Server moved Shared subscriptions not supported Connection rate exceeded Maximum connect time Subscription IDs not supported Wildcard subscriptions not supported Examples Publish temperature information to localhost with QoS 1: mosquitto_pub -t sensors/temperature -m 32 -q 1 Publish timestamp and temperature information to a remote host on a non-standard port and QoS 0: mosquitto_pub -h 192.168.1.1 -p 1885 -t sensors/temperature -m "1266193804 32" Publish light switch status. Message is set to retained because there may be a long period of time between light switch events: mosquitto_pub -r -t switches/kitchen_lights/status -m "on" Send the contents of a file in two ways: mosquitto_pub -t my/topic -f ./data mosquitto_pub -t my/topic -s < ./data Send parsed electricity usage data from a Current Cost meter, reading from stdin with one line/reading as one message: read_cc128.pl | mosquitto_pub -t sensors/cc128 -l Files $XDG_CONFIG_HOME/mosquitto_pub $HOME/.config/mosquitto_pub Configuration file for default options. Bugs mosquitto bug information can be found at See Also mqtt 7 mosquitto_rr 1 mosquitto_sub 1 mosquitto 8 libmosquitto 3 mosquitto-tls 7 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto_rr.1.meta000066400000000000000000000001531450213760600202020ustar00rootroot00000000000000.. title: mosquitto_rr man page .. slug: mosquitto_rr-1 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto_rr.1.xml000066400000000000000000001232721450213760600200640ustar00rootroot00000000000000 mosquitto_rr 1 Mosquitto Project Commands mosquitto_rr an MQTT version 5/3.1.1 client for request/response messaging mosquitto_rr response-topic hostname socket path port-number username password message-topic URL message-topic file message bind-address command identifier value client-id client-id-prefix keepalive-time message-QoS protocol-version message-processing-timeout session-expiry-interval socks-url topic payload qos file dir file file ciphers version protocol engine pem engine kpass-sha1 hex-key identity ciphers version mosquitto_rr Description mosquitto_rr is an MQTT version 5/3.1.1 client that can be used to publish a request message and wait for a response. When using MQTT v5, which is the default, mosquitto_rr will use the Request-Response feature. The important options are , , and one of , , , and . Example: mosquitto_rr -t request-topic -e response-topic -m message Encrypted Connections mosquitto_rr supports TLS encrypted connections. It is strongly recommended that you use an encrypted connection for anything more than the most basic setup. To enable TLS connections when using x509 certificates, one of either or can be provided as an option. Alternatively, if the option is used then the OS provided certificates will be loaded and neither or are needed To enable TLS connections when using TLS-PSK, you must use the and the options. Options The options below may be given on the command line, but may also be placed in a config file located at or with one pair of per line. The values in the config file will be used as defaults and can be overridden by using the command line. The exceptions to this is , which if given in the config file will not be overridden. Note also that currently some options cannot be negated, e.g. . Config file lines that have a as the first character are treated as comments and not processed any further. Bind the outgoing connection to a local ip address/hostname. Use this argument if you need to restrict network communication to a particular interface. Disable 'clean session' / enable persistent client mode. When this argument is used, the broker will be instructed not to clean existing sessions for the same client id when the client connects, and sessions will never expire when the client disconnects. MQTT v5 clients can change their session expiry interval with the argument. When a session is persisted on the broker, the subscriptions for the client will be maintained after it disconnects, along with subsequent QoS 1 and QoS 2 messages that arrive. When the client reconnects and does not clean the session, it will receive all of the queued messages. If using this option, the client id must be set manually with Define the path to a file containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. See also Define the path to a directory containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. For to work correctly, the certificate files must have ".crt" as the file ending and you must run "openssl rehash <path to capath>" each time you add/remove a certificate. See also Define the path to a file containing a PEM encoded certificate for this client, if required by the server. See also . An openssl compatible list of TLS ciphers to support in the client. See ciphers1 for more information. Enable debug messages. Use an MQTT v5 property with this publish. If you use this option, the client will be set to be an MQTT v5 client. This option has two forms: is the MQTT command/packet identifier and can be one of CONNECT, PUBACK, PUBREC, PUBCOMP, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, AUTH, or WILL. The properties available for each command are listed in the Properties section. is the name of the property to add. This is as described in the specification, but with '-' as a word separator. For example: . More details are in the Properties section. is the value of the property to add, with a data type that is property specific. is only used for the property as the first of the two strings in the string pair. In that case, is the second of the strings in the pair. Response topic. The client will subscribe to this topic to wait for a response. Send the contents of a file as the request message. Specify output printing format. This option allows you to choose what information from each message is printed to the screen. See the Output Format section below for full details. This option overrides the option, but does not override the option. Display usage information. Specify the host to connect to. Defaults to localhost. The id to use for this client. If not given, a client id will be generated depending on the MQTT version being used. For v3.1.1/v3.1, the client generates a client id in the format , where the are replaced with random alphanumeric characters. For v5.0, the client sends a zero length client id, and the server will generate a client id for the client. This option cannot be used at the same time as the argument. Provide a prefix that the client id will be built from by appending the process id of the client. This is useful where the broker is using the clientid_prefixes option. Cannot be used at the same time as the argument. When using certificate based encryption, this option disables verification of the server hostname in the server certificate. This can be useful when testing initial server configurations but makes it possible for a malicious third party to impersonate your server through DNS spoofing, for example. Use this option in testing only. If you need to resort to using this option in a production environment, your setup is at fault and there is no point using encryption. The number of seconds between sending PING commands to the broker for the purposes of informing it we are still connected and functioning. Defaults to 60 seconds. Define the path to a file containing a PEM encoded private key for this client, if required by the server. See also . Specifies the type of private key in use when making TLS connections.. This can be "pem" or "engine". This parameter is useful when a TPM module is being used and the private key has been created with it. Defaults to "pem", which means normal private key files are used. See also . Specify specify user, password, hostname, port and topic at once as a URL. The URL must be in the form: mqtt(s)://[username[:password]@]host[:port]/topic If the scheme is mqtt:// then the port defaults to 1883. If the scheme is mqtts:// then the port defaults to 8883. Send a single request message from the command line. Do not append an end of line character to the payload when printing. This allows streaming of payload data from multiple messages directly to another application unmodified. Only really makes sense when not using . Send a null (zero length) request message. Disable Nagle's algorithm for the socket. This means that latency of sent messages is reduced, which is particularly noticeable for small, reasonably infrequent messages. Using this option may result in more packets being sent than would normally be necessary. Connect to the port specified. If not given, the default of 1883 for plain MQTT or 8883 for MQTT over TLS will be used. Provide a password to be used for authenticating with the broker. Using this argument without also specifying a username is invalid when using MQTT v3.1 or v3.1.1. See also the option. When using the JSON output format %j or %J, the default is to print in an unformatted fashion. Specifying prints messages in a prettier, more human readable format. Specify a SOCKS5 proxy to connect through. "None" and "username" authentication types are supported. The must be of the form . The protocol prefix means that hostnames are resolved by the proxy. The symbols %25, %3A and %40 are URL decoded into %, : and @ respectively, if present in the username or password. If username is not given, then no authentication is attempted. If the port is not given, then the default of 1080 is used. More SOCKS versions may be available in the future, depending on demand, and will use different protocol prefixes as described in curl 1 . Provide the hexadecimal (no leading 0x) pre-shared-key matching the one used on the broker to use TLS-PSK encryption support. must also be provided to enable TLS-PSK. The client identity to use with TLS-PSK support. This may be used instead of a username if the broker is configured to do so. Specify the quality of service desired for the incoming messages, from 0, 1 and 2. Defaults to 0. See mqtt7 for more information on QoS. The QoS is identical for all topics subscribed to in a single instance of mosquitto_rr. If this argument is given, no runtime errors will be printed. This excludes any error messages given in case of invalid user input (e.g. using without a port). If this argument is given, messages that are received that have the retain bit set will not be printed. Messages with retain set are "stale", in that it is not known when they were originally published. When subscribing to a wildcard topic there may be a large number of retained messages. This argument suppresses their display. Use SRV lookups to determine which host to connect to. Performs lookups to when used in conjunction with , otherwise uses . Send a request message read from stdin, sending the entire content as a single message. The MQTT topic where the request message will be sent. Provide a protocol to use when connecting to a broker that has multiple protocols available on a single port, e.g. MQTT and WebSockets. A valid openssl engine id. These can be listed with openssl engine command. See also . SHA1 of the private key password when using an TLS engine. Some TLS engines such as the TPM engine may require the use of a password in order to be accessed. This option allows a hex encoded SHA1 hash of the password to the engine directly, instead of the user being prompted for the password. See also . If used, this will load and trust the OS provided CA certificates. This can be used in conjunction with and and can be used on its own to enable TLS mode. This will be set by default if is used, or if port is 8883 and no other certificate options are used. Choose which TLS protocol version to use when communicating with the broker. Valid options are , and . The default value is . Must match the protocol version used by the broker. Provide a username to be used for authenticating with the broker. See also the argument. Connect to a broker through a local unix domain socket instead of a TCP socket. This is a replacement for and . For example: See the option in mosquitto.conf 5 to configure Mosquitto to listen on a unix socket. Print received messages verbosely. With this argument, messages will be printed as "topic payload". When this argument is not given, the messages are printed as "payload". Specify which version of the MQTT protocol should be used when connecting to the rmeote broker. Can be , , , or the more verbose , , or . Defaults to . Provide a timeout as an integer number of seconds. mosquitto_sub will stop processing messages and disconnect after this number of seconds has passed. The timeout starts just after the client has connected to the broker. Specify a message that will be stored by the broker and sent out if this client disconnects unexpectedly. This must be used in conjunction with . The QoS to use for the Will. Defaults to 0. This must be used in conjunction with . If given, if the client disconnects unexpectedly the message sent out will be treated as a retained message. This must be used in conjunction with . The topic on which to send a Will, in the event that the client disconnects unexpectedly. Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5 clients only. Set to 0-4294967294 to specify the session will expire in that many seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given. If the session is set to never expire, either with -x or -c, then a client id must be provided. Output format There are three ways of formatting the output from mosquitto_rr. In all cases a new-line character is appended for each message received unless the argument is passed to mosquitto_rr. Payload-only is the default output format and will print the payload exactly as it is received. Verbose mode is activated with and prints the message topic and the payload, separated by a space. The final option is formatted output, which allows the user to define a custom output format. The behaviour is controlled with the option. The format string is a free text string where interpreted sequences are replaced by different parameters. The available interpreted sequences are described below. Three characters are used to start an interpreted sequence: , and . Sequences starting with are either parameters related to the MQTT message being printed, or are helper sequences to avoid the need to type long date format strings for example. Sequences starting with are passed to the strftime3 function (with the @ replaced with a % - note that only the character immediately after the @ is passed to strftime). This allows the construction of a wide variety of time based outputs. The output options for strftime vary from platform to platform, so please check what is available for your platform. mosquitto_rr does provide one extension to strftime which is , which can be used to obtain the number of nanoseconds passed in the current second. The resolution of this option varies depending on the platform. The final sequence character is , which is used to input some characters that would otherwise be difficult to enter. MQTT related parameters a literal %. the MQTT v5 topic-alias property, if present. the MQTT v5 content-type property, if present. the MQTT v5 correlation-data property, if present. Note that this property is specified as binary data, so may produce non-printable characters. the MQTT v5 message-expiry-interval property, if present. the MQTT v5 payload-format-indicator property, if present. the length of the payload in bytes. the message id (only relevant for messages with QoS>0). the MQTT v5 user-property property, if present. This will be printed in the form key:value. It is possible for any number of user properties to be attached to a message, and to have duplicate keys. the payload raw bytes (may produce non-printable characters depending on the payload). the message QoS. the MQTT v5 response-topic property, if present. the retained flag for the message. the MQTT v5 subscription-identifier property, if present. the message topic. the payload with each byte as a hexadecimal number (lower case). the payload with each byte as a hexadecimal number (upper case). Helpers ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100 JSON output of message parameters and timestamp, with a quoted and escaped payload. For example {"tst":"2020-05-06T22:12:00.000000+0100","topic":"greeting","qos":0,"retain":0,"payload":"hello world"} JSON output of message parameters and timestamp, with a non-quoted and non-escaped payload - this means the payload must itself be valid JSON. For example: {"tst":"2020-05-06T22:12:00.000000+0100","topic":"foo","qos":0,"retain":0,"payload":{"temperature":27.0,"humidity":57}}. If the payload is not valid JSON, then the error message "Error: Message payload is not valid JSON on topic <topic>" will be printed to stderr. ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100 Unix timestamp with nanoseconds, e.g. 1470818943.786368637 Time related parameters a literal @. pass the character represented by to the strftime function as . The options supported are platform dependent. the number of nanoseconds that have passed in the current second, with varying timing resolution depending on platform. Escape characters a literal \. a null character. Can be used to separate different parameters that may contain spaces (e.g. topic, payload) so that processing with tools such as xargs1 is easier. alert/bell. the escape sequence, which can be used with ANSI colour codes to provide coloured output for example. end of line. carriage return. horizontal tab. vertical tab. Wills mosquitto_rr can register a message with the broker that will be sent out if it disconnects unexpectedly. See mqtt7 for more information. The minimum requirement for this is to use to specify which topic the will should be sent out on. This will result in a non-retained, zero length message with QoS 0. Use the , and arguments to modify the other will parameters. Properties The / option allows adding properties to different stages of the mosquitto_rr run. The properties supported for each command are as follows: Connect (binary data - note treated as a string in mosquitto_rr) (UTF-8 string pair) (32-bit unsigned integer) (16-bit unsigned integer) (8-bit unsigned integer) (8-bit unsigned integer) (32-bit unsigned integer, note use instead) (16-bit unsigned integer) (UTF-8 string pair) Publish (UTF-8 string) (binary data - note treated as a string in mosquitto_rr) (32-bit unsigned integer) (8-bit unsigned integer) (UTF-8 string) (16-bit unsigned integer) (UTF-8 string pair) Subscribe (UTF-8 string pair) Unsubscribe (UTF-8 string pair) Disconnect (32-bit unsigned integer) (UTF-8 string pair) Will properties (UTF-8 string) (binary data - note treated as a string in mosquitto_pub) (32-bit unsigned integer) (8-bit unsigned integer) (UTF-8 string) (UTF-8 string pair) (32-bit unsigned integer) Exit Values Success Timed out waiting for message Unspecified failure Files $XDG_CONFIG_HOME/mosquitto_rr $HOME/.config/mosquitto_rr Configuration file for default options. Bugs mosquitto bug information can be found at See Also mqtt 7 mosquitto_pub 1 mosquitto_sub 1 mosquitto 8 libmosquitto 3 mosquitto-tls 7 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mosquitto_sub.1.meta000066400000000000000000000001551450213760600203520ustar00rootroot00000000000000.. title: mosquitto_sub man page .. slug: mosquitto_sub-1 .. category: man .. type: man .. pretty_url: False mosquitto-2.0.18/man/mosquitto_sub.1.xml000066400000000000000000001531271450213760600202340ustar00rootroot00000000000000 mosquitto_sub 1 Mosquitto Project Commands mosquitto_sub an MQTT version 5/3.1.1/3.1 client for subscribing to topics mosquitto_sub hostname socket path port-number username password message-topic URL message-topic bind-address msg-count command identifier value client-id client-id-prefix keepalive-time message-QoS chance filter-out unsub-topic protocol-version message-processing-timeout session-expiry-interval socks-url topic payload qos file dir file file version protocol engine pem engine kpass-sha1 hex-key identity version mosquitto_sub Description mosquitto_sub is a simple MQTT version 5/3.1.1 client that will subscribe to topics and print the messages that it receives. In addition to subscribing to topics, mosquitto_sub can filter out received messages so they are not printed (see the option) or unsubscribe from topics (see the option). Unsubscribing from topics is useful for clients connecting with clean session set to false. Encrypted Connections mosquitto_sub supports TLS encrypted connections. It is strongly recommended that you use an encrypted connection for anything more than the most basic setup. To enable TLS connections when using x509 certificates, one of either or can be provided as an option. Alternatively, if the option is used then the OS provided certificates will be loaded and neither or are needed To enable TLS connections when using TLS-PSK, you must use the and the options. Options The options below may be given on the command line, but may also be placed in a config file located at or with one pair of per line. The values in the config file will be used as defaults and can be overridden by using the command line. The exceptions to this are and , which if given in the config file will not be overridden. Note also that currently some options cannot be negated, e.g. . Config file lines that have a as the first character are treated as comments and not processed any further. Bind the outgoing connection to a local ip address/hostname. Use this argument if you need to restrict network communication to a particular interface. Disable 'clean session' / enable persistent client mode. When this argument is used, the broker will be instructed not to clean existing sessions for the same client id when the client connects, and sessions will never expire when the client disconnects. MQTT v5 clients can change their session expiry interval with the argument. When a session is persisted on the broker, the subscriptions for the client will be maintained after it disconnects, along with subsequent QoS 1 and QoS 2 messages that arrive. When the client reconnects and does not clean the session, it will receive all of the queued messages. If using this option, the client id must be set manually with Define the path to a file containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. See also Define the path to a directory containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. For to work correctly, the certificate files must have ".crt" as the file ending and you must run "openssl rehash <path to capath>" each time you add/remove a certificate. See also Define the path to a file containing a PEM encoded certificate for this client, if required by the server. See also . An openssl compatible list of TLS ciphers to support in the client. See ciphers1 for more information. Disconnect and exit the program immediately after the given count of messages have been received. This may be useful in shell scripts where on a single status value is required, for example. Combine with to print only the first set of fresh messages (i.e. that does not have the retained flag set), or with to filter which topics are processed. Enable debug messages. Use an MQTT v5 property with this publish. If you use this option, the client will be set to be an MQTT v5 client. This option has two forms: is the MQTT command/packet identifier and can be one of CONNECT, PUBACK, PUBREC, PUBCOMP, SUBSCRIBE, UNSUBSCRIBE, DISCONNECT, AUTH, or WILL. The properties available for each command are listed in the Properties section. is the name of the property to add. This is as described in the specification, but with '-' as a word separator. For example: . More details are in the Properties section. is the value of the property to add, with a data type that is property specific. is only used for the property as the first of the two strings in the string pair. In that case, is the second of the strings in the pair. If this option is given, mosquitto_sub will exit immediately that all of its subscriptions have been acknowledged by the broker. In conjunction with this allows a durable client session to be initialised on the broker for future use without requiring any messages to be received. Specify output printing format. This option allows you to choose what information from each message is printed to the screen. See the Output Format section below for full details. This option overrides the option, but does not override the option. Display usage information. Specify the host to connect to. Defaults to localhost. The id to use for this client. If not given, a client id will be generated depending on the MQTT version being used. For v3.1.1/v3.1, the client generates a client id in the format , where the are replaced with random alphanumeric characters. For v5.0, the client sends a zero length client id, and the server will generate a client id for the client. This option cannot be used at the same time as the argument. Provide a prefix that the client id will be built from by appending the process id of the client. This is useful where the broker is using the clientid_prefixes option. Cannot be used at the same time as the argument. When using certificate based encryption, this option disables verification of the server hostname in the server certificate. This can be useful when testing initial server configurations but makes it possible for a malicious third party to impersonate your server through DNS spoofing, for example. Use this option in testing only. If you need to resort to using this option in a production environment, your setup is at fault and there is no point using encryption. The number of seconds between sending PING commands to the broker for the purposes of informing it we are still connected and functioning. Defaults to 60 seconds. Define the path to a file containing a PEM encoded private key for this client, if required by the server. See also . Specifies the type of private key in use when making TLS connections.. This can be "pem" or "engine". This parameter is useful when a TPM module is being used and the private key has been created with it. Defaults to "pem", which means normal private key files are used. See also . Specify specify user, password, hostname, port and topic at once as a URL. The URL must be in the form: mqtt(s)://[username[:password]@]host[:port]/topic If the scheme is mqtt:// then the port defaults to 1883. If the scheme is mqtts:// then the port defaults to 8883. Do not append an end of line character to the payload when printing. This allows streaming of payload data from multiple messages directly to another application unmodified. Only really makes sense when not using . Disable Nagle's algorithm for the socket. This means that latency of sent messages is reduced, which is particularly noticeable for small, reasonably infrequent messages. Using this option may result in more packets being sent than would normally be necessary. Connect to the port specified. If not given, the default of 1883 for plain MQTT or 8883 for MQTT over TLS will be used. Provide a password to be used for authenticating with the broker. Using this argument without also specifying a username is invalid when using MQTT v3.1 or v3.1.1. See also the option. When using the JSON output format %j or %J, the default is to print in an unformatted fashion. Specifying prints messages in a prettier, more human readable format. Specify a SOCKS5 proxy to connect through. "None" and "username" authentication types are supported. The must be of the form . The protocol prefix means that hostnames are resolved by the proxy. The symbols %25, %3A and %40 are URL decoded into %, : and @ respectively, if present in the username or password. If username is not given, then no authentication is attempted. If the port is not given, then the default of 1080 is used. More SOCKS versions may be available in the future, depending on demand, and will use different protocol prefixes as described in curl 1 . Provide the hexadecimal (no leading 0x) pre-shared-key matching the one used on the broker to use TLS-PSK encryption support. must also be provided to enable TLS-PSK. The client identity to use with TLS-PSK support. This may be used instead of a username if the broker is configured to do so. Specify the quality of service desired for the incoming messages, from 0, 1 and 2. Defaults to 0. See mqtt7 for more information on QoS. The QoS is identical for all topics subscribed to in a single instance of mosquitto_sub. If this argument is given, no runtime errors will be printed. This excludes any error messages given in case of invalid user input (e.g. using without a port). If this argument is given, messages that are received that have the retain bit set will not be printed. Messages with retain set are "stale", in that it is not known when they were originally published. When subscribing to a wildcard topic there may be a large number of retained messages. This argument suppresses their display. This option can be used to reduce the proportion of messages that mosquitto_sub prints. The default behaviour is to print all incoming messages. Setting the chance to a floating point value between 0.1 and 100.0 will ensure that on average that percentage of messages will be printed. If this argument is given, the when mosquitto_sub receives a message with the retained bit set, it will send a message to the broker to clear that retained message. This applies to all received messages except those that are filtered out by the option. This option still takes effect even if is used. See also the and options. Remove all retained messages on the server, assuming we have access to do so, and then exit: mosquitto_sub -t '#' --remove-retained --retained-only Remove a whole tree, with the exception of a single topic: mosquitto_sub -t 'bbc/#' -T bbc/bbc1 --remove-retained If this argument is given, only messages that are received that have the retain bit set will be printed. Messages with retain set are "stale", in that it is not known when they were originally published. With this argument in use, the receipt of the first non-stale message will cause the client to exit. See also the option. If this argument is given, the subscriptions will have the "retain as published" option set. This means that the retain flag on an incoming message will be exactly as set by the publishing client, rather than indicating whether the message is fresh/stale. This option is not valid for MQTT v3.1/v3.1.1 clients. Use SRV lookups to determine which host to connect to. Performs lookups to when used in conjunction with , otherwise uses . The MQTT topic to subscribe to. See mqtt7 for more information on MQTT topics. This option may be repeated to subscribe to multiple topics. Suppress printing of topics that match the filter. This allows subscribing to a wildcard topic and only printing a partial set of the wildcard hierarchy. For example, subscribe to the BBC tree, but suppress output from Radio 3: mosquitto_sub -t bbc/# -T bbc/radio3 This option may be repeated to filter out multiple topics or topic trees. Provide a protocol to use when connecting to a broker that has multiple protocols available on a single port, e.g. MQTT and WebSockets. A valid openssl engine id. These can be listed with openssl engine command. See also . SHA1 of the private key password when using an TLS engine. Some TLS engines such as the TPM engine may require the use of a password in order to be accessed. This option allows a hex encoded SHA1 hash of the password to the engine directly, instead of the user being prompted for the password. See also . If used, this will load and trust the OS provided CA certificates. This can be used in conjunction with and and can be used on its own to enable TLS mode. This will be set by default if is used, or if port is 8883 and no other certificate options are used. Choose which TLS protocol version to use when communicating with the broker. Valid options are , and . The default value is . Must match the protocol version used by the broker. Provide a username to be used for authenticating with the broker. See also the argument. Connect to a broker through a local unix domain socket instead of a TCP socket. This is a replacement for and . For example: See the option in mosquitto.conf 5 to configure Mosquitto to listen on a unix socket. A topic that will be unsubscribed from. This may be used on its own or in conjunction with the option and only makes sense when used in conjunction with . If used with then subscriptions will be processed before unsubscriptions. Note that it is only possible to unsubscribe from subscriptions that have previously been made. It is not possible to punch holes in wildcard subscriptions. For example, subscribing to and then unsubscribing from as shown below will still result in messages matching the being delivered to the client. mosquitto_sub -t sensors/# -U sensors/+/temperature -v Note also that because retained messages are published by the broker on receipt of a SUBSCRIBE command, subscribing and unsubscribing to the same topic may result in messages being received at the client. This option may be repeated to unsubscribe from multiple topics. Print received messages verbosely. With this argument, messages will be printed as "topic payload". When this argument is not given, the messages are printed as "payload". Specify which version of the MQTT protocol should be used when connecting to the remote broker. Can be , , , or the more verbose , , or . Defaults to . Provide a timeout as an integer number of seconds. mosquitto_sub will stop processing messages and disconnect after this number of seconds has passed. The timeout starts just after the client has connected to the broker. Specify a message that will be stored by the broker and sent out if this client disconnects unexpectedly. This must be used in conjunction with . The QoS to use for the Will. Defaults to 0. This must be used in conjunction with . If given, if the client disconnects unexpectedly the message sent out will be treated as a retained message. This must be used in conjunction with . The topic on which to send a Will, in the event that the client disconnects unexpectedly. Set the session-expiry-interval property on the CONNECT packet. Applies to MQTT v5 clients only. Set to 0-4294967294 to specify the session will expire in that many seconds after the client disconnects, or use -1, 4294967295, or ∞ for a session that does not expire. Defaults to -1 if -c is also given, or 0 if -c not given. If the session is set to never expire, either with -x or -c, then a client id must be provided. Output Format There are three ways of formatting the output from mosquitto_sub. In all cases a new-line character is appended for each message received unless the argument is passed to mosquitto_sub. Payload-only is the default output format and will print the payload exactly as it is received. Verbose mode is activated with and prints the message topic and the payload, separated by a space. The final option is formatted output, which allows the user to define a custom output format. The behaviour is controlled with the option. The format string is a free text string where interpreted sequences are replaced by different parameters. The available interpreted sequences are described below. Three characters are used to start an interpreted sequence: , and . Sequences starting with are either parameters related to the MQTT message being printed, or are helper sequences to avoid the need to type long date format strings for example. Sequences starting with are passed to the strftime3 function (with the @ replaced with a % - note that only the character immediately after the @ is passed to strftime). This allows the construction of a wide variety of time based outputs. The output options for strftime vary from platform to platform, so please check what is available for your platform. mosquitto_sub does provide one extension to strftime which is , which can be used to obtain the number of nanoseconds passed in the current second. The resolution of this option varies depending on the platform. The final sequence character is , which is used to input some characters that would otherwise be difficult to enter. Flag characters The parameters %A, %C, %E, %F, %I, %l, %m, %p, %R, %S, %t, %x, and %X can have optional flags immediately after the % character. The value should be zero padded. This applies to the parameters %A, %E, %F, %l, %m, %S, %X, and %x. It will be ignored for other parameters. If used with the flag, the flag will be ignored. The value will be left aligned to the field width, padded with blanks. The default is right alignment, with either 0 or blank padding. Field width Some of the MQTT related parameters can be formatted with an option to set their field width in a similar way to regular printf style formats, i.e. this sets the minimum width when printing this parameter. This applies to the options %A, %C, %E, %F, %I, %l, %m, %p, %R, %S, %t, %x, %X. For example would set the minimum topic field width to 10 characters. Maximum width Some of the MQTT related parameters can be formatted with an option to set a maximum field width in a similar way to regular printf style formats. This applies to the options %C, %I, %R, %t. For example would set the minimum topic field width to 10 characters, and the maximum topic width to 10 characters, i.e. the field will always be exactly 10 characters long. MQTT related parameters a literal %. the MQTT v5 topic-alias property, if present. the MQTT v5 content-type property, if present. the MQTT v5 correlation-data property, if present. Note that this property is specified as binary data, so may produce non-printable characters. the MQTT v5 message-expiry-interval property, if present. the MQTT v5 payload-format-indicator property, if present. the length of the payload in bytes. the message id (only relevant for messages with QoS>0). the MQTT v5 user-property property, if present. This will be printed in the form key:value. It is possible for any number of user properties to be attached to a message, and to have duplicate keys. the payload raw bytes (may produce non-printable characters depending on the payload). the message QoS. the MQTT v5 response-topic property, if present. the retained flag for the message. the MQTT v5 subscription-identifier property, if present. the message topic. the payload with each byte as a hexadecimal number (lower case). the payload with each byte as a hexadecimal number (upper case). Helpers ISO-8601 format date and time, e.g. 2016-08-10T09:47:38+0100 JSON output of message parameters and timestamp, with a quoted and escaped payload. For example {"tst":"2020-05-06T22:12:00.000000+0100","topic":"greeting","qos":0,"retain":0,"payload":"hello world"} JSON output of message parameters and timestamp, with a non-quoted and non-escaped payload - this means the payload must itself be valid JSON. For example: {"tst":"2020-05-06T22:12:00.000000+0100","topic":"foo","qos":0,"retain":0,"payload":{"temperature":27.0,"humidity":57}}. If the payload is not valid JSON, then the error message "Error: Message payload is not valid JSON on topic <topic>" will be printed to stderr. Unix timestamp with nanoseconds, e.g. 1470818943.786368637 Time related parameters a literal @. pass the character represented by to the strftime function as . The options supported are platform dependent. the number of nanoseconds that have passed in the current second, with varying timing resolution depending on platform. Escape characters a literal \. a null character. Can be used to separate different parameters that may contain spaces (e.g. topic, payload) so that processing with tools such as xargs1 is easier. alert/bell. the escape sequence, which can be used with ANSI colour codes to provide coloured output for example. end of line. carriage return. horizontal tab. vertical tab. Wills mosquitto_sub can register a message with the broker that will be sent out if it disconnects unexpectedly. See mqtt7 for more information. The minimum requirement for this is to use to specify which topic the will should be sent out on. This will result in a non-retained, zero length message with QoS 0. Use the , and arguments to modify the other will parameters. Properties The / option allows adding properties to different stages of the mosquitto_sub run. The properties supported for each command are as follows: Connect (binary data - note treated as a string in mosquitto_sub) (UTF-8 string) (32-bit unsigned integer) (16-bit unsigned integer) (8-bit unsigned integer) (8-bit unsigned integer) (32-bit unsigned integer, note use instead) (16-bit unsigned integer) (UTF-8 string pair) Subscribe (UTF-8 string pair) Unsubscribe (UTF-8 string pair) Disconnect (32-bit unsigned integer) (UTF-8 string pair) Will properties (UTF-8 string) (binary data - note treated as a string in mosquitto_sub) (32-bit unsigned integer) (8-bit unsigned integer) (UTF-8 string) (UTF-8 string pair) (32-bit unsigned integer) Exit Status mosquitto_sub returns zero on success, or non-zero on error. If the connection is refused by the broker at the MQTT level, then the exit code is the CONNACK reason code. If another error occurs, the exit code is a libmosquitto return value. MQTT v3.1.1 CONNACK codes: Success Connection refused: Bad protocol version Connection refused: Identifier rejected Connection refused: Server unavailable Connection refused: Bad username/password Connection refused: Not authorized MQTT v5 CONNACK codes: Success Unspecified error Malformed packet Protocol error Implementation specific error Unsupported protocol version Client ID not valid Bad username or password Not authorized Server unavailable Server busy Banned Server shutting down Bad authentication method Keep alive timeout Session taken over Topic filter invalid Topic name invalid Receive maximum exceeded Topic alias invalid Packet too large Message rate too high Quota exceeded Administrative action Payload format invalid Retain not supported QoS not supported Use another server Server moved Shared subscriptions not supported Connection rate exceeded Maximum connect time Subscription IDs not supported Wildcard subscriptions not supported Examples Note that these really are examples - the subscriptions will work if you run them as shown, but there must be something publishing messages on those topics for you to receive anything. Subscribe to temperature information on localhost with QoS 1: mosquitto_sub -t sensors/temperature -q 1 Subscribe to hard drive temperature updates on multiple machines/hard drives. This expects each machine to be publishing its hard drive temperature to sensors/machines/HOSTNAME/temperature/HD_NAME. mosquitto_sub -t sensors/machines/+/temperature/+ Subscribe to all broker status messages: mosquitto_sub -v -t \$SYS/# Specify the output format as "ISO-8601 date : topic : payload in hex" mosquitto_sub -F '@Y-@m-@dT@H:@M:@S@z : %t : %x' -t '#' Specify the output format as "seconds since epoch.nanoseconds : retained flag : qos : mid : payload length" mosquitto_sub -F '%@s.@N : %r : %q : %m : %l' -q 2 -t '#' Topic and payload output, but with colour where supported. mosquitto_sub -F '\e[92m%t \e[96m%p\e[0m' -q 2 -t '#' Exit Values Success Timed out waiting for message Unspecified failure Files $XDG_CONFIG_HOME/mosquitto_sub $HOME/.config/mosquitto_sub Configuration file for default options. Bugs mosquitto bug information can be found at See Also mqtt 7 mosquitto_pub 1 mosquitto_rr 1 mosquitto 8 libmosquitto 3 mosquitto-tls 7 Author Roger Light roger@atchoo.org mosquitto-2.0.18/man/mqtt.7.meta000066400000000000000000000001571450213760600164320ustar00rootroot00000000000000.. title: MQTT man page .. slug: mqtt-7 .. category: man .. type: man .. pretty_url: False .. hide_title: True mosquitto-2.0.18/man/mqtt.7.xml000066400000000000000000000203161450213760600163030ustar00rootroot00000000000000 mqtt 7 Mosquitto Project Conventions and miscellaneous mqtt MQ Telemetry Transport MQTT Description MQTT is a lightweight publish/subscribe messaging protocol. It is useful for use with low power sensors, but is applicable to many scenarios. This manual describes some of the features of MQTT version 3.1.1/3.1, to assist end users in getting the most out of the protocol. For more complete information on MQTT, see http://mqtt.org/. Publish/Subscribe The MQTT protocol is based on the principle of publishing messages and subscribing to topics, or "pub/sub". Multiple clients connect to a broker and subscribe to topics that they are interested in. Clients also connect to the broker and publish messages to topics. Many clients may subscribe to the same topics and do with the information as they please. The broker and MQTT act as a simple, common interface for everything to connect to. This means that you if you have clients that dump subscribed messages to a database, to Twitter, Cosm or even a simple text file, then it becomes very simple to add new sensors or other data input to a database, Twitter or so on. Topics/Subscriptions Messages in MQTT are published on topics. There is no need to configure a topic, publishing on it is enough. Topics are treated as a hierarchy, using a slash (/) as a separator. This allows sensible arrangement of common themes to be created, much in the same way as a filesystem. For example, multiple computers may all publish their hard drive temperature information on the following topic, with their own computer and hard drive name being replaced as appropriate: sensors/COMPUTER_NAME/temperature/HARDDRIVE_NAME Clients can receive messages by creating subscriptions. A subscription may be to an explicit topic, in which case only messages to that topic will be received, or it may include wildcards. Two wildcards are available, or . can be used as a wildcard for a single level of hierarchy. It could be used with the topic above to get information on all computers and hard drives as follows: sensors/+/temperature/+ As another example, for a topic of "a/b/c/d", the following example subscriptions will match: a/b/c/d +/b/c/d a/+/c/d a/+/+/d +/+/+/+ The following subscriptions will not match: a/b/c b/+/c/d +/+/+ can be used as a wildcard for all remaining levels of hierarchy. This means that it must be the final character in a subscription. With a topic of "a/b/c/d", the following example subscriptions will match: a/b/c/d # a/# a/b/# a/b/c/# +/b/c/# Zero length topic levels are valid, which can lead to some slightly non-obvious behaviour. For example, a topic of "a//topic" would correctly match against a subscription of "a/+/topic". Likewise, zero length topic levels can exist at both the beginning and the end of a topic string, so "/a/topic" would match against a subscription of "+/a/topic", "#" or "/#", and a topic "a/topic/" would match against a subscription of "a/topic/+" or "a/topic/#". Quality of Service MQTT defines three levels of Quality of Service (QoS). The QoS defines how hard the broker/client will try to ensure that a message is received. Messages may be sent at any QoS level, and clients may attempt to subscribe to topics at any QoS level. This means that the client chooses the maximum QoS it will receive. For example, if a message is published at QoS 2 and a client is subscribed with QoS 0, the message will be delivered to that client with QoS 0. If a second client is also subscribed to the same topic, but with QoS 2, then it will receive the same message but with QoS 2. For a second example, if a client is subscribed with QoS 2 and a message is published on QoS 0, the client will receive it on QoS 0. Higher levels of QoS are more reliable, but involve higher latency and have higher bandwidth requirements. 0: The broker/client will deliver the message once, with no confirmation. 1: The broker/client will deliver the message at least once, with confirmation required. 2: The broker/client will deliver the message exactly once by using a four step handshake. Retained Messages All messages may be set to be retained. This means that the broker will keep the message even after sending it to all current subscribers. If a new subscription is made that matches the topic of the retained message, then the message will be sent to the client. This is useful as a "last known good" mechanism. If a topic is only updated infrequently, then without a retained message, a newly subscribed client may have to wait a long time to receive an update. With a retained message, the client will receive an instant update. Clean session / Durable connections On connection, a client sets the "clean session" flag, which is sometimes also known as the "clean start" flag. If clean session is set to false, then the connection is treated as durable. This means that when the client disconnects, any subscriptions it has will remain and any subsequent QoS 1 or 2 messages will be stored until it connects again in the future. If clean session is true, then all subscriptions will be removed for the client when it disconnects. Wills When a client connects to a broker, it may inform the broker that it has a will. This is a message that it wishes the broker to send when the client disconnects unexpectedly. The will message has a topic, QoS and retain status just the same as any other message. See Also mosquitto 8 mosquitto_pub 1 mosquitto_sub 1 Author Roger Light roger@atchoo.org mosquitto-2.0.18/misc/000077500000000000000000000000001450213760600146055ustar00rootroot00000000000000mosquitto-2.0.18/misc/currentcost/000077500000000000000000000000001450213760600171605ustar00rootroot00000000000000mosquitto-2.0.18/misc/currentcost/cc128_log_mysql.pl000077500000000000000000000026661450213760600224400ustar00rootroot00000000000000#!/usr/bin/perl # Log CurrentCost power meter data to a mysql database. # Assumes data is coming in on MQTT topic sensors/cc128 # and in format timestamp,temperature,ch1_data # e.g. 1276605752,12.7,86 # To create database, table and user: # # CREATE DATABASE powermeter; # USE 'powermeter'; # CREATE TABLE powermeter ( # `id` INT NOT NULL auto_increment, # `timestamp` INT NOT NULL, # `temperature` FLOAT NOT NULL DEFAULT 0.0, # `ch1` INT NOT NULL DEFAULT 0, # PRIMARY KEY (`id`), # UNIQUE KEY `timestamp` (`timestamp`) # ) ENGINE=InnoDB DEFAULT CHARSET=utf8; # # CREATE USER 'powermeter'@'localhost' IDENTIFIED BY ''; # GRANT ALL ON powermeter.* to 'powermeter'@'localhost'; use strict; use DBI(); use FileHandle; local $| = 1; my $dbname = "powermeter"; my $dbhost = "localhost"; my $dbusername = "powermeter"; my $dbpassword = ""; my $dbtable = "powermeter"; my $subclient = "mosquitto_sub -t sensors/cc128"; open(SUB, "$subclient|"); SUB->autoflush(1); my $dbh = DBI->connect("DBI:mysql:database=$dbname;host=$dbhost", "$dbusername", "$dbpassword", {'RaiseError' => 1}); my $query = "INSERT INTO powermeter (timestamp, temperature, ch1) VALUES (?,?,?)"; my @vals; my ($timestamp, $temperature, $ch1); while (my $line = ) { @vals = split(/,/, $line); $timestamp = @vals[0]; $temperature = @vals[1]; $ch1 = @vals[2]; $dbh->do($query, undef, $timestamp, $temperature, $ch1); } $dbh->disconnect(); mosquitto-2.0.18/misc/currentcost/cc128_parse.pl000077500000000000000000000024471450213760600215410ustar00rootroot00000000000000#!/usr/bin/perl -w # Read raw cc128 data and republish without xml. # Probably only works if you have a single channel. use strict; use HTTP::Date "str2time"; use FileHandle; local $| = 1; my $subclient = "mosquitto_sub -t sensors/cc128/raw -q 1"; my $pubclient = "mosquitto_pub -t sensors/cc128 -q 1 -l"; my $pubclient_ch1 = "mosquitto_pub -t sensors/cc128/ch1 -q 1 -l"; open(SUB, "$subclient|"); open(PUB, "|$pubclient"); open(PUB_CH1, "|$pubclient_ch1"); SUB->autoflush(1); PUB->autoflush(1); PUB_CH1->autoflush(1); while (my $line = ) { #CC128-v0.120000215.7003112100108 if ($line =~ m# *([\-\d.]+)0[0-9]*10*(\d+) $now){ $r_stamp -= 86400; } print PUB "$r_stamp,$temp,$watts\n"; print PUB_CH1 "$r_stamp $watts\n"; } } mosquitto-2.0.18/misc/currentcost/cc128_read.pl000077500000000000000000000007111450213760600213320ustar00rootroot00000000000000#!/usr/bin/perl -w # Reads data from a Current Cost device via serial port. # Spawns use strict; use Device::SerialPort qw( :PARAM :STAT 0.07 ); my $pubclient = "mosquitto_pub -t sensors/cc128/raw -q 1 -l"; my $PORT = "/dev/ttyUSB0"; local $| = 1; my $ob = Device::SerialPort->new($PORT); $ob->baudrate(57600); $ob->write_settings; open(SERIAL, "+<$PORT"); open(MQTT, "|$pubclient"); while (my $line = ) { print(MQTT "$line"); } close(MQTT); mosquitto-2.0.18/misc/currentcost/cc128_read.py000077500000000000000000000006071450213760600213530ustar00rootroot00000000000000#!/usr/bin/python -u import mosquitto import serial usb = serial.Serial(port='/dev/ttyUSB0', baudrate=57600) mosq = mosquitto.Mosquitto() mosq.connect("localhost") mosq.loop_start() running = True try: while running: line = usb.readline() mosq.publish("sensors/cc128/raw", line) except usb.SerialException, e: running = False mosq.disconnect() mosq.loop_stop() mosquitto-2.0.18/misc/currentcost/gnome-panel/000077500000000000000000000000001450213760600213625ustar00rootroot00000000000000mosquitto-2.0.18/misc/currentcost/gnome-panel/CurrentCostMQTT.py000077500000000000000000000042111450213760600247160ustar00rootroot00000000000000#!/usr/bin/env python import gnomeapplet import gtk import mosquitto import sys class CurrentCostMQTT(gnomeapplet.Applet): def on_message(self, mosq, obj, msg): # Message format is "power" self.label.set_text(msg.payload+"W") def set_label(self, val): self.label.set_text(val) def on_change_background(self, applet, type, color, pixmap): applet.set_style(None) applet.modify_style(gtk.RcStyle()) if type == gnomeapplet.COLOR_BACKGROUND: applet.modify_bg(gtk.STATE_NORMAL, color) elif type == gnomeapplet.PIXMAP_BACKGROUND: style = applet.get_style().copy() style.bg_pixmap[gtk.STATE_NORMAL] = pixmap applet.set_style(style) def show_menu(self, widget, event): print "menu" def __init__(self, applet, iid): self.applet = applet self.label = gtk.Label("0W") self.event_box = gtk.EventBox() self.event_box.add(self.label) self.event_box.set_events(gtk.gdk.BUTTON_PRESS_MASK) self.event_box.connect("button_press_event", self.show_menu) self.applet.add(self.event_box) self.applet.set_background_widget(applet) self.applet.show_all() self.mosq = mosquitto.Mosquitto() self.mosq.on_message = self.on_message self.mosq.connect("localhost") self.mosq.loop_start() self.mosq.subscribe("sensors/cc128/ch1", 0) self.applet.connect('change-background', self.on_change_background) def CurrentCostMQTT_factory(applet, iid): CurrentCostMQTT(applet, iid) return gtk.TRUE if len(sys.argv) == 2: if sys.argv[1] == "-d": #Debug mode main_window = gtk.Window(gtk.WINDOW_TOPLEVEL) main_window.set_title("Python Applet") main_window.connect("destroy", gtk.main_quit) app = gnomeapplet.Applet() CurrentCostMQTT_factory(app,None) app.reparent(main_window) main_window.show_all() gtk.main() sys.exit() if __name__ == '__main__': gnomeapplet.bonobo_factory("OAFIID:CurrentCostMQTT_Factory", gnomeapplet.Applet.__gtype__, "MQTT", "0", CurrentCostMQTT_factory) mosquitto-2.0.18/misc/currentcost/gnome-panel/CurrentCostMQTT.server000066400000000000000000000021711450213760600255740ustar00rootroot00000000000000 mosquitto-2.0.18/misc/currentcost/gnome-panel/currentcost.png000066400000000000000000000123041450213760600244430ustar00rootroot00000000000000PNG  IHDR00WsRGBbKGD pHYs ftIME m,tEXtCommentCreated with GIMPWIDAThޭk$uz{z;;"wK.IEG l  LcH,0A r DKb `ǰ2 7AEe>fv=ﮪ{ɇ]1< (TssFxiVNcrv-SՕA.z^ !<霻+@bEv(VFdDx^7Z^nF%֣kOMx̙v% B#,^'d2PAdxE hP1VRj^,};I?Y<ųÉYiT{:p:ܘLǽ_L&{8F# +J{g2`D *jQk?M;]t0F6ky0I^= j8ջPζAy$1F$!c+@%Mi[+nk:RVY/7G3wl`w?}t|\GQX9D֖<B@‰s,ϘfyNARUI8mnm_?o|<j=ywN'r]UQ3oux웍I*;OGƫbUCpbU1:1LVj8ZÓO>/:G'@INpZxڱUm}Ν;cm4&Iypzh93<<~؇+ޣjqLףsZM=GV*daLͿ\[VUED 'Da-[{.~h[RȳdV]Z͖ 'h6A 4=-T*"xʊ;9!6aooϤi\A"'o IRL8K)z7ݴF)FC`gy&+7]4$Ϧ"B`n*G3 aJ<N#L&M&VmUzeFb3'ZƙvTJِ2ɊReՔZu !3 !W(PUEAfIb8d6Som8<@=P3e`^t$">E^}j`ueRݏVU1 tc v(O&5.GGGNck5QܯE^V%ix<&21ưBZ)qxL1Ȼ93~(Q @ 6"ɳ\888޳n5$˦X1+&ςh4Tk ,XɊ`I!3^ $! Zk++gi[2qn5 ;T*)׮>ƕK1w ZƗ@imm8d!Ld;'J,^0޹c(p͓b… TR3M9>d: kkkji>t@Ze9VVɳ cM |ys~ܡޮc,]`6899;7R}cȋe($8d фh\r$jG* BP<ˉίR#\rJ\1rwNseϳzRyW\\B) ce3E#X 9.l[ۻg9KV8>:PUiƀDxXW9z?6έr~c7߾CT~1]k^bX_Y%#;s$Ke̼!:viѐmjCEt5@Ygփ#DZfcE:-ȲIN_{|Ρl3NԈ-z)ZP,G1Qs҅~dݭTVOOy.,gOdc1 ޳O\RZsrpp@$[_΍ڵLjr=X[.Pc 4x,(t>k$T]{Oei7h5f޾6A X1~~lc-8&ZʹՎ^v#1%xp}{}B1B~V;F,JjMⓓSvvwKb-W"OCvVhpZԳ`xL$8x{qw7x\(kA T\i(K(^TU1jİO|QD_wMg +p~V>rۈEޕVJ*q=a0`ELm0J|G<ua:ɸ,^Z"" xSGk DB]B\๛?="`<^"jA5Kj1N؅ x2xѯ3a8b#KN?G3D,'8+\)j.:>̫"e/\SO i0o>uO?''=ob z&fKhe;歧{[ Fc$lO*Ad.vPpyfW;ܳ^oR"B%1j`Z\⹕O{&Grm1zeZv)CDNx&b>,c7Ixң"5x2a%Ix,+m{߽OdeIVJ\^x+~Mݮ^p lVv\R# w^?V*xPΈHyQ@8Q~<rɳO?+/_訫X޽G̥֛_[bl_ zL&u2*\N%w3fJ-+r5& b} 1"jQ ̲}gϾܫ [^m}ORL\>v[}uwgr8bmm-lllH2 :f(5jZs4(zfŸ*˝֋|,lΓO=E=[җoX_wwÃse{f3<JY,T4#DQk ,ԫKϷj|Sq^zyp0O+?ճ޼K?9=]ZQIH̚2o8J} ERINKzsy.+wyxpt*:_7?:>?=̈ (zG,)d^QrIY_~鿊ޛo37{+z/u։Wf/*ս^ _WIgDg`9GvjO*ieV1Oo~wG}U[?xn*SOK:4/_/RowkvtyN^sO\/wg?Ddq}?Pb} }aQӾZhIENDB`mosquitto-2.0.18/misc/letsencrypt/000077500000000000000000000000001450213760600171615ustar00rootroot00000000000000mosquitto-2.0.18/misc/letsencrypt/mosquitto-copy.sh000077500000000000000000000024651450213760600225430ustar00rootroot00000000000000#!/bin/sh # This is an example deploy renewal hook for certbot that copies newly updated # certificates to the Mosquitto certificates directory and sets the ownership # and permissions so only the mosquitto user can access them, then signals # Mosquitto to reload certificates. # RENEWED_DOMAINS will match the domains being renewed for that certificate, so # may be just "example.com", or multiple domains "www.example.com example.com" # depending on your certificate. # Place this script in /etc/letsencrypt/renewal-hooks/deploy/ and make it # executable after editing it to your needs. # Set which domain this script will be run for MY_DOMAIN=example.com # Set the directory that the certificates will be copied to. CERTIFICATE_DIR=/etc/mosquitto/certs for D in ${RENEWED_DOMAINS}; do if [ "${D}" = "${MY_DOMAIN}" ]; then # Copy new certificate to Mosquitto directory cp ${RENEWED_LINEAGE}/fullchain.pem ${CERTIFICATE_DIR}/server.pem cp ${RENEWED_LINEAGE}/privkey.pem ${CERTIFICATE_DIR}/server.key # Set ownership to Mosquitto chown mosquitto: ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key # Ensure permissions are restrictive chmod 0600 ${CERTIFICATE_DIR}/server.pem ${CERTIFICATE_DIR}/server.key # Tell Mosquitto to reload certificates and configuration pkill -HUP -x mosquitto fi done mosquitto-2.0.18/mosquitto.conf000066400000000000000000001170011450213760600165650ustar00rootroot00000000000000# Config file for mosquitto # # See mosquitto.conf(5) for more information. # # Default values are shown, uncomment to change. # # Use the # character to indicate a comment, but only if it is the # very first character on the line. # ================================================================= # General configuration # ================================================================= # Use per listener security settings. # # It is recommended this option be set before any other options. # # If this option is set to true, then all authentication and access control # options are controlled on a per listener basis. The following options are # affected: # # acl_file # allow_anonymous # allow_zero_length_clientid # auto_id_prefix # password_file # plugin # plugin_opt_* # psk_file # # Note that if set to true, then a durable client (i.e. with clean session set # to false) that has disconnected will use the ACL settings defined for the # listener that it was most recently connected to. # # The default behaviour is for this to be set to false, which maintains the # setting behaviour from previous versions of mosquitto. #per_listener_settings false # This option controls whether a client is allowed to connect with a zero # length client id or not. This option only affects clients using MQTT v3.1.1 # and later. If set to false, clients connecting with a zero length client id # are disconnected. If set to true, clients will be allocated a client id by # the broker. This means it is only useful for clients with clean session set # to true. #allow_zero_length_clientid true # If allow_zero_length_clientid is true, this option allows you to set a prefix # to automatically generated client ids to aid visibility in logs. # Defaults to 'auto-' #auto_id_prefix auto- # This option affects the scenario when a client subscribes to a topic that has # retained messages. It is possible that the client that published the retained # message to the topic had access at the time they published, but that access # has been subsequently removed. If check_retain_source is set to true, the # default, the source of a retained message will be checked for access rights # before it is republished. When set to false, no check will be made and the # retained message will always be published. This affects all listeners. #check_retain_source true # QoS 1 and 2 messages will be allowed inflight per client until this limit # is exceeded. Defaults to 0. (No maximum) # See also max_inflight_messages #max_inflight_bytes 0 # The maximum number of QoS 1 and 2 messages currently inflight per # client. # This includes messages that are partway through handshakes and # those that are being retried. Defaults to 20. Set to 0 for no # maximum. Setting to 1 will guarantee in-order delivery of QoS 1 # and 2 messages. #max_inflight_messages 20 # For MQTT v5 clients, it is possible to have the server send a "server # keepalive" value that will override the keepalive value set by the client. # This is intended to be used as a mechanism to say that the server will # disconnect the client earlier than it anticipated, and that the client should # use the new keepalive value. The max_keepalive option allows you to specify # that clients may only connect with keepalive less than or equal to this # value, otherwise they will be sent a server keepalive telling them to use # max_keepalive. This only applies to MQTT v5 clients. The default, and maximum # value allowable, is 65535. # # Set to 0 to allow clients to set keepalive = 0, which means no keepalive # checks are made and the client will never be disconnected by the broker if no # messages are received. You should be very sure this is the behaviour that you # want. # # For MQTT v3.1.1 and v3.1 clients, there is no mechanism to tell the client # what keepalive value they should use. If an MQTT v3.1.1 or v3.1 client # specifies a keepalive time greater than max_keepalive they will be sent a # CONNACK message with the "identifier rejected" reason code, and disconnected. # #max_keepalive 65535 # For MQTT v5 clients, it is possible to have the server send a "maximum packet # size" value that will instruct the client it will not accept MQTT packets # with size greater than max_packet_size bytes. This applies to the full MQTT # packet, not just the payload. Setting this option to a positive value will # set the maximum packet size to that number of bytes. If a client sends a # packet which is larger than this value, it will be disconnected. This applies # to all clients regardless of the protocol version they are using, but v3.1.1 # and earlier clients will of course not have received the maximum packet size # information. Defaults to no limit. Setting below 20 bytes is forbidden # because it is likely to interfere with ordinary client operation, even with # very small payloads. #max_packet_size 0 # QoS 1 and 2 messages above those currently in-flight will be queued per # client until this limit is exceeded. Defaults to 0. (No maximum) # See also max_queued_messages. # If both max_queued_messages and max_queued_bytes are specified, packets will # be queued until the first limit is reached. #max_queued_bytes 0 # Set the maximum QoS supported. Clients publishing at a QoS higher than # specified here will be disconnected. #max_qos 2 # The maximum number of QoS 1 and 2 messages to hold in a queue per client # above those that are currently in-flight. Defaults to 1000. Set # to 0 for no maximum (not recommended). # See also queue_qos0_messages. # See also max_queued_bytes. #max_queued_messages 1000 # # This option sets the maximum number of heap memory bytes that the broker will # allocate, and hence sets a hard limit on memory use by the broker. Memory # requests that exceed this value will be denied. The effect will vary # depending on what has been denied. If an incoming message is being processed, # then the message will be dropped and the publishing client will be # disconnected. If an outgoing message is being sent, then the individual # message will be dropped and the receiving client will be disconnected. # Defaults to no limit. #memory_limit 0 # This option sets the maximum publish payload size that the broker will allow. # Received messages that exceed this size will not be accepted by the broker. # The default value is 0, which means that all valid MQTT messages are # accepted. MQTT imposes a maximum payload size of 268435455 bytes. #message_size_limit 0 # This option allows the session of persistent clients (those with clean # session set to false) that are not currently connected to be removed if they # do not reconnect within a certain time frame. This is a non-standard option # in MQTT v3.1. MQTT v3.1.1 and v5.0 allow brokers to remove client sessions. # # Badly designed clients may set clean session to false whilst using a randomly # generated client id. This leads to persistent clients that connect once and # never reconnect. This option allows these clients to be removed. This option # allows persistent clients (those with clean session set to false) to be # removed if they do not reconnect within a certain time frame. # # The expiration period should be an integer followed by one of h d w m y for # hour, day, week, month and year respectively. For example # # persistent_client_expiration 2m # persistent_client_expiration 14d # persistent_client_expiration 1y # # The default if not set is to never expire persistent clients. #persistent_client_expiration # Write process id to a file. Default is a blank string which means # a pid file shouldn't be written. # This should be set to /var/run/mosquitto/mosquitto.pid if mosquitto is # being run automatically on boot with an init script and # start-stop-daemon or similar. #pid_file # Set to true to queue messages with QoS 0 when a persistent client is # disconnected. These messages are included in the limit imposed by # max_queued_messages and max_queued_bytes # Defaults to false. # This is a non-standard option for the MQTT v3.1 spec but is allowed in # v3.1.1. #queue_qos0_messages false # Set to false to disable retained message support. If a client publishes a # message with the retain bit set, it will be disconnected if this is set to # false. #retain_available true # Disable Nagle's algorithm on client sockets. This has the effect of reducing # latency of individual messages at the potential cost of increasing the number # of packets being sent. #set_tcp_nodelay false # Time in seconds between updates of the $SYS tree. # Set to 0 to disable the publishing of the $SYS tree. #sys_interval 10 # The MQTT specification requires that the QoS of a message delivered to a # subscriber is never upgraded to match the QoS of the subscription. Enabling # this option changes this behaviour. If upgrade_outgoing_qos is set true, # messages sent to a subscriber will always match the QoS of its subscription. # This is a non-standard option explicitly disallowed by the spec. #upgrade_outgoing_qos false # When run as root, drop privileges to this user and its primary # group. # Set to root to stay as root, but this is not recommended. # If set to "mosquitto", or left unset, and the "mosquitto" user does not exist # then it will drop privileges to the "nobody" user instead. # If run as a non-root user, this setting has no effect. # Note that on Windows this has no effect and so mosquitto should be started by # the user you wish it to run as. #user mosquitto # ================================================================= # Listeners # ================================================================= # Listen on a port/ip address combination. By using this variable # multiple times, mosquitto can listen on more than one port. If # this variable is used and neither bind_address nor port given, # then the default listener will not be started. # The port number to listen on must be given. Optionally, an ip # address or host name may be supplied as a second argument. In # this case, mosquitto will attempt to bind the listener to that # address and so restrict access to the associated network and # interface. By default, mosquitto will listen on all interfaces. # Note that for a websockets listener it is not possible to bind to a host # name. # # On systems that support Unix Domain Sockets, it is also possible # to create a # Unix socket rather than opening a TCP socket. In # this case, the port number should be set to 0 and a unix socket # path must be provided, e.g. # listener 0 /tmp/mosquitto.sock # # listener port-number [ip address/host name/unix socket path] #listener # By default, a listener will attempt to listen on all supported IP protocol # versions. If you do not have an IPv4 or IPv6 interface you may wish to # disable support for either of those protocol versions. In particular, note # that due to the limitations of the websockets library, it will only ever # attempt to open IPv6 sockets if IPv6 support is compiled in, and so will fail # if IPv6 is not available. # # Set to `ipv4` to force the listener to only use IPv4, or set to `ipv6` to # force the listener to only use IPv6. If you want support for both IPv4 and # IPv6, then do not use the socket_domain option. # #socket_domain # Bind the listener to a specific interface. This is similar to # the [ip address/host name] part of the listener definition, but is useful # when an interface has multiple addresses or the address may change. If used # with the [ip address/host name] part of the listener definition, then the # bind_interface option will take priority. # Not available on Windows. # # Example: bind_interface eth0 #bind_interface # When a listener is using the websockets protocol, it is possible to serve # http data as well. Set http_dir to a directory which contains the files you # wish to serve. If this option is not specified, then no normal http # connections will be possible. #http_dir # The maximum number of client connections to allow. This is # a per listener setting. # Default is -1, which means unlimited connections. # Note that other process limits mean that unlimited connections # are not really possible. Typically the default maximum number of # connections possible is around 1024. #max_connections -1 # The listener can be restricted to operating within a topic hierarchy using # the mount_point option. This is achieved be prefixing the mount_point string # to all topics for any clients connected to this listener. This prefixing only # happens internally to the broker; the client will not see the prefix. #mount_point # Choose the protocol to use when listening. # This can be either mqtt or websockets. # Certificate based TLS may be used with websockets, except that only the # cafile, certfile, keyfile, ciphers, and ciphers_tls13 options are supported. #protocol mqtt # Set use_username_as_clientid to true to replace the clientid that a client # connected with with its username. This allows authentication to be tied to # the clientid, which means that it is possible to prevent one client # disconnecting another by using the same clientid. # If a client connects with no username it will be disconnected as not # authorised when this option is set to true. # Do not use in conjunction with clientid_prefixes. # See also use_identity_as_username. # This does not apply globally, but on a per-listener basis. #use_username_as_clientid # Change the websockets headers size. This is a global option, it is not # possible to set per listener. This option sets the size of the buffer used in # the libwebsockets library when reading HTTP headers. If you are passing large # header data such as cookies then you may need to increase this value. If left # unset, or set to 0, then the default of 1024 bytes will be used. #websockets_headers_size # ----------------------------------------------------------------- # Certificate based SSL/TLS support # ----------------------------------------------------------------- # The following options can be used to enable certificate based SSL/TLS support # for this listener. Note that the recommended port for MQTT over TLS is 8883, # but this must be set manually. # # See also the mosquitto-tls man page and the "Pre-shared-key based SSL/TLS # support" section. Only one of certificate or PSK encryption support can be # enabled for any listener. # Both of certfile and keyfile must be defined to enable certificate based # TLS encryption. # Path to the PEM encoded server certificate. #certfile # Path to the PEM encoded keyfile. #keyfile # If you wish to control which encryption ciphers are used, use the ciphers # option. The list of available ciphers can be optained using the "openssl # ciphers" command and should be provided in the same format as the output of # that command. This applies to TLS 1.2 and earlier versions only. Use # ciphers_tls1.3 for TLS v1.3. #ciphers # Choose which TLS v1.3 ciphersuites are used for this listener. # Defaults to "TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_128_GCM_SHA256" #ciphers_tls1.3 # If you have require_certificate set to true, you can create a certificate # revocation list file to revoke access to particular client certificates. If # you have done this, use crlfile to point to the PEM encoded revocation file. #crlfile # To allow the use of ephemeral DH key exchange, which provides forward # security, the listener must load DH parameters. This can be specified with # the dhparamfile option. The dhparamfile can be generated with the command # e.g. "openssl dhparam -out dhparam.pem 2048" #dhparamfile # By default an TLS enabled listener will operate in a similar fashion to a # https enabled web server, in that the server has a certificate signed by a CA # and the client will verify that it is a trusted certificate. The overall aim # is encryption of the network traffic. By setting require_certificate to true, # the client must provide a valid certificate in order for the network # connection to proceed. This allows access to the broker to be controlled # outside of the mechanisms provided by MQTT. #require_certificate false # cafile and capath define methods of accessing the PEM encoded # Certificate Authority certificates that will be considered trusted when # checking incoming client certificates. # cafile defines the path to a file containing the CA certificates. # capath defines a directory that will be searched for files # containing the CA certificates. For capath to work correctly, the # certificate files must have ".crt" as the file ending and you must run # "openssl rehash " each time you add/remove a certificate. #cafile #capath # If require_certificate is true, you may set use_identity_as_username to true # to use the CN value from the client certificate as a username. If this is # true, the password_file option will not be used for this listener. #use_identity_as_username false # ----------------------------------------------------------------- # Pre-shared-key based SSL/TLS support # ----------------------------------------------------------------- # The following options can be used to enable PSK based SSL/TLS support for # this listener. Note that the recommended port for MQTT over TLS is 8883, but # this must be set manually. # # See also the mosquitto-tls man page and the "Certificate based SSL/TLS # support" section. Only one of certificate or PSK encryption support can be # enabled for any listener. # The psk_hint option enables pre-shared-key support for this listener and also # acts as an identifier for this listener. The hint is sent to clients and may # be used locally to aid authentication. The hint is a free form string that # doesn't have much meaning in itself, so feel free to be creative. # If this option is provided, see psk_file to define the pre-shared keys to be # used or create a security plugin to handle them. #psk_hint # When using PSK, the encryption ciphers used will be chosen from the list of # available PSK ciphers. If you want to control which ciphers are available, # use the "ciphers" option. The list of available ciphers can be optained # using the "openssl ciphers" command and should be provided in the same format # as the output of that command. #ciphers # Set use_identity_as_username to have the psk identity sent by the client used # as its username. Authentication will be carried out using the PSK rather than # the MQTT username/password and so password_file will not be used for this # listener. #use_identity_as_username false # ================================================================= # Persistence # ================================================================= # If persistence is enabled, save the in-memory database to disk # every autosave_interval seconds. If set to 0, the persistence # database will only be written when mosquitto exits. See also # autosave_on_changes. # Note that writing of the persistence database can be forced by # sending mosquitto a SIGUSR1 signal. #autosave_interval 1800 # If true, mosquitto will count the number of subscription changes, retained # messages received and queued messages and if the total exceeds # autosave_interval then the in-memory database will be saved to disk. # If false, mosquitto will save the in-memory database to disk by treating # autosave_interval as a time in seconds. #autosave_on_changes false # Save persistent message data to disk (true/false). # This saves information about all messages, including # subscriptions, currently in-flight messages and retained # messages. # retained_persistence is a synonym for this option. #persistence false # The filename to use for the persistent database, not including # the path. #persistence_file mosquitto.db # Location for persistent database. # Default is an empty string (current directory). # Set to e.g. /var/lib/mosquitto if running as a proper service on Linux or # similar. #persistence_location # ================================================================= # Logging # ================================================================= # Places to log to. Use multiple log_dest lines for multiple # logging destinations. # Possible destinations are: stdout stderr syslog topic file dlt # # stdout and stderr log to the console on the named output. # # syslog uses the userspace syslog facility which usually ends up # in /var/log/messages or similar. # # topic logs to the broker topic '$SYS/broker/log/', # where severity is one of D, E, W, N, I, M which are debug, error, # warning, notice, information and message. Message type severity is used by # the subscribe/unsubscribe log_types and publishes log messages to # $SYS/broker/log/M/susbcribe or $SYS/broker/log/M/unsubscribe. # # The file destination requires an additional parameter which is the file to be # logged to, e.g. "log_dest file /var/log/mosquitto.log". The file will be # closed and reopened when the broker receives a HUP signal. Only a single file # destination may be configured. # # The dlt destination is for the automotive `Diagnostic Log and Trace` tool. # This requires that Mosquitto has been compiled with DLT support. # # Note that if the broker is running as a Windows service it will default to # "log_dest none" and neither stdout nor stderr logging is available. # Use "log_dest none" if you wish to disable logging. #log_dest stderr # Types of messages to log. Use multiple log_type lines for logging # multiple types of messages. # Possible types are: debug, error, warning, notice, information, # none, subscribe, unsubscribe, websockets, all. # Note that debug type messages are for decoding the incoming/outgoing # network packets. They are not logged in "topics". #log_type error #log_type warning #log_type notice #log_type information # If set to true, client connection and disconnection messages will be included # in the log. #connection_messages true # If using syslog logging (not on Windows), messages will be logged to the # "daemon" facility by default. Use the log_facility option to choose which of # local0 to local7 to log to instead. The option value should be an integer # value, e.g. "log_facility 5" to use local5. #log_facility # If set to true, add a timestamp value to each log message. #log_timestamp true # Set the format of the log timestamp. If left unset, this is the number of # seconds since the Unix epoch. # This is a free text string which will be passed to the strftime function. To # get an ISO 8601 datetime, for example: # log_timestamp_format %Y-%m-%dT%H:%M:%S #log_timestamp_format # Change the websockets logging level. This is a global option, it is not # possible to set per listener. This is an integer that is interpreted by # libwebsockets as a bit mask for its lws_log_levels enum. See the # libwebsockets documentation for more details. "log_type websockets" must also # be enabled. #websockets_log_level 0 # ================================================================= # Security # ================================================================= # If set, only clients that have a matching prefix on their # clientid will be allowed to connect to the broker. By default, # all clients may connect. # For example, setting "secure-" here would mean a client "secure- # client" could connect but another with clientid "mqtt" couldn't. #clientid_prefixes # Boolean value that determines whether clients that connect # without providing a username are allowed to connect. If set to # false then a password file should be created (see the # password_file option) to control authenticated client access. # # Defaults to false, unless there are no listeners defined in the configuration # file, in which case it is set to true, but connections are only allowed from # the local machine. #allow_anonymous false # ----------------------------------------------------------------- # Default authentication and topic access control # ----------------------------------------------------------------- # Control access to the broker using a password file. This file can be # generated using the mosquitto_passwd utility. If TLS support is not compiled # into mosquitto (it is recommended that TLS support should be included) then # plain text passwords are used, in which case the file should be a text file # with lines in the format: # username:password # The password (and colon) may be omitted if desired, although this # offers very little in the way of security. # # See the TLS client require_certificate and use_identity_as_username options # for alternative authentication options. If a plugin is used as well as # password_file, the plugin check will be made first. #password_file # Access may also be controlled using a pre-shared-key file. This requires # TLS-PSK support and a listener configured to use it. The file should be text # lines in the format: # identity:key # The key should be in hexadecimal format without a leading "0x". # If an plugin is used as well, the plugin check will be made first. #psk_file # Control access to topics on the broker using an access control list # file. If this parameter is defined then only the topics listed will # have access. # If the first character of a line of the ACL file is a # it is treated as a # comment. # Topic access is added with lines of the format: # # topic [read|write|readwrite|deny] # # The access type is controlled using "read", "write", "readwrite" or "deny". # This parameter is optional (unless contains a space character) - if # not given then the access is read/write. can contain the + or # # wildcards as in subscriptions. # # The "deny" option can used to explicity deny access to a topic that would # otherwise be granted by a broader read/write/readwrite statement. Any "deny" # topics are handled before topics that grant read/write access. # # The first set of topics are applied to anonymous clients, assuming # allow_anonymous is true. User specific topic ACLs are added after a # user line as follows: # # user # # The username referred to here is the same as in password_file. It is # not the clientid. # # # If is also possible to define ACLs based on pattern substitution within the # topic. The patterns available for substition are: # # %c to match the client id of the client # %u to match the username of the client # # The substitution pattern must be the only text for that level of hierarchy. # # The form is the same as for the topic keyword, but using pattern as the # keyword. # Pattern ACLs apply to all users even if the "user" keyword has previously # been given. # # If using bridges with usernames and ACLs, connection messages can be allowed # with the following pattern: # pattern write $SYS/broker/connection/%c/state # # pattern [read|write|readwrite] # # Example: # # pattern write sensor/%u/data # # If an plugin is used as well as acl_file, the plugin check will be # made first. #acl_file # ----------------------------------------------------------------- # External authentication and topic access plugin options # ----------------------------------------------------------------- # External authentication and access control can be supported with the # plugin option. This is a path to a loadable plugin. See also the # plugin_opt_* options described below. # # The plugin option can be specified multiple times to load multiple # plugins. The plugins will be processed in the order that they are specified # here. If the plugin option is specified alongside either of # password_file or acl_file then the plugin checks will be made first. # # If the per_listener_settings option is false, the plugin will be apply to all # listeners. If per_listener_settings is true, then the plugin will apply to # the current listener being defined only. # # This option is also available as `auth_plugin`, but this use is deprecated # and will be removed in the future. # #plugin # If the plugin option above is used, define options to pass to the # plugin here as described by the plugin instructions. All options named # using the format plugin_opt_* will be passed to the plugin, for example: # # This option is also available as `auth_opt_*`, but this use is deprecated # and will be removed in the future. # # plugin_opt_db_host # plugin_opt_db_port # plugin_opt_db_username # plugin_opt_db_password # ================================================================= # Bridges # ================================================================= # A bridge is a way of connecting multiple MQTT brokers together. # Create a new bridge using the "connection" option as described below. Set # options for the bridges using the remaining parameters. You must specify the # address and at least one topic to subscribe to. # # Each connection must have a unique name. # # The address line may have multiple host address and ports specified. See # below in the round_robin description for more details on bridge behaviour if # multiple addresses are used. Note that if you use an IPv6 address, then you # are required to specify a port. # # The direction that the topic will be shared can be chosen by # specifying out, in or both, where the default value is out. # The QoS level of the bridged communication can be specified with the next # topic option. The default QoS level is 0, to change the QoS the topic # direction must also be given. # # The local and remote prefix options allow a topic to be remapped when it is # bridged to/from the remote broker. This provides the ability to place a topic # tree in an appropriate location. # # For more details see the mosquitto.conf man page. # # Multiple topics can be specified per connection, but be careful # not to create any loops. # # If you are using bridges with cleansession set to false (the default), then # you may get unexpected behaviour from incoming topics if you change what # topics you are subscribing to. This is because the remote broker keeps the # subscription for the old topic. If you have this problem, connect your bridge # with cleansession set to true, then reconnect with cleansession set to false # as normal. #connection #address [:] [[:]] #topic [[[out | in | both] qos-level] local-prefix remote-prefix] # If you need to have the bridge connect over a particular network interface, # use bridge_bind_address to tell the bridge which local IP address the socket # should bind to, e.g. `bridge_bind_address 192.168.1.10` #bridge_bind_address # If a bridge has topics that have "out" direction, the default behaviour is to # send an unsubscribe request to the remote broker on that topic. This means # that changing a topic direction from "in" to "out" will not keep receiving # incoming messages. Sending these unsubscribe requests is not always # desirable, setting bridge_attempt_unsubscribe to false will disable sending # the unsubscribe request. #bridge_attempt_unsubscribe true # Set the version of the MQTT protocol to use with for this bridge. Can be one # of mqttv50, mqttv311 or mqttv31. Defaults to mqttv311. #bridge_protocol_version mqttv311 # Set the clean session variable for this bridge. # When set to true, when the bridge disconnects for any reason, all # messages and subscriptions will be cleaned up on the remote # broker. Note that with cleansession set to true, there may be a # significant amount of retained messages sent when the bridge # reconnects after losing its connection. # When set to false, the subscriptions and messages are kept on the # remote broker, and delivered when the bridge reconnects. #cleansession false # Set the amount of time a bridge using the lazy start type must be idle before # it will be stopped. Defaults to 60 seconds. #idle_timeout 60 # Set the keepalive interval for this bridge connection, in # seconds. #keepalive_interval 60 # Set the clientid to use on the local broker. If not defined, this defaults to # 'local.'. If you are bridging a broker to itself, it is important # that local_clientid and clientid do not match. #local_clientid # If set to true, publish notification messages to the local and remote brokers # giving information about the state of the bridge connection. Retained # messages are published to the topic $SYS/broker/connection//state # unless the notification_topic option is used. # If the message is 1 then the connection is active, or 0 if the connection has # failed. # This uses the last will and testament feature. #notifications true # Choose the topic on which notification messages for this bridge are # published. If not set, messages are published on the topic # $SYS/broker/connection//state #notification_topic # Set the client id to use on the remote end of this bridge connection. If not # defined, this defaults to 'name.hostname' where name is the connection name # and hostname is the hostname of this computer. # This replaces the old "clientid" option to avoid confusion. "clientid" # remains valid for the time being. #remote_clientid # Set the password to use when connecting to a broker that requires # authentication. This option is only used if remote_username is also set. # This replaces the old "password" option to avoid confusion. "password" # remains valid for the time being. #remote_password # Set the username to use when connecting to a broker that requires # authentication. # This replaces the old "username" option to avoid confusion. "username" # remains valid for the time being. #remote_username # Set the amount of time a bridge using the automatic start type will wait # until attempting to reconnect. # This option can be configured to use a constant delay time in seconds, or to # use a backoff mechanism based on "Decorrelated Jitter", which adds a degree # of randomness to when the restart occurs. # # Set a constant timeout of 20 seconds: # restart_timeout 20 # # Set backoff with a base (start value) of 10 seconds and a cap (upper limit) of # 60 seconds: # restart_timeout 10 30 # # Defaults to jitter with a base of 5 and cap of 30 #restart_timeout 5 30 # If the bridge has more than one address given in the address/addresses # configuration, the round_robin option defines the behaviour of the bridge on # a failure of the bridge connection. If round_robin is false, the default # value, then the first address is treated as the main bridge connection. If # the connection fails, the other secondary addresses will be attempted in # turn. Whilst connected to a secondary bridge, the bridge will periodically # attempt to reconnect to the main bridge until successful. # If round_robin is true, then all addresses are treated as equals. If a # connection fails, the next address will be tried and if successful will # remain connected until it fails #round_robin false # Set the start type of the bridge. This controls how the bridge starts and # can be one of three types: automatic, lazy and once. Note that RSMB provides # a fourth start type "manual" which isn't currently supported by mosquitto. # # "automatic" is the default start type and means that the bridge connection # will be started automatically when the broker starts and also restarted # after a short delay (30 seconds) if the connection fails. # # Bridges using the "lazy" start type will be started automatically when the # number of queued messages exceeds the number set with the "threshold" # parameter. It will be stopped automatically after the time set by the # "idle_timeout" parameter. Use this start type if you wish the connection to # only be active when it is needed. # # A bridge using the "once" start type will be started automatically when the # broker starts but will not be restarted if the connection fails. #start_type automatic # Set the number of messages that need to be queued for a bridge with lazy # start type to be restarted. Defaults to 10 messages. # Must be less than max_queued_messages. #threshold 10 # If try_private is set to true, the bridge will attempt to indicate to the # remote broker that it is a bridge not an ordinary client. If successful, this # means that loop detection will be more effective and that retained messages # will be propagated correctly. Not all brokers support this feature so it may # be necessary to set try_private to false if your bridge does not connect # properly. #try_private true # Some MQTT brokers do not allow retained messages. MQTT v5 gives a mechanism # for brokers to tell clients that they do not support retained messages, but # this is not possible for MQTT v3.1.1 or v3.1. If you need to bridge to a # v3.1.1 or v3.1 broker that does not support retained messages, set the # bridge_outgoing_retain option to false. This will remove the retain bit on # all outgoing messages to that bridge, regardless of any other setting. #bridge_outgoing_retain true # If you wish to restrict the size of messages sent to a remote bridge, use the # bridge_max_packet_size option. This sets the maximum number of bytes for # the total message, including headers and payload. # Note that MQTT v5 brokers may provide their own maximum-packet-size property. # In this case, the smaller of the two limits will be used. # Set to 0 for "unlimited". #bridge_max_packet_size 0 # ----------------------------------------------------------------- # Certificate based SSL/TLS support # ----------------------------------------------------------------- # Either bridge_cafile or bridge_capath must be defined to enable TLS support # for this bridge. # bridge_cafile defines the path to a file containing the # Certificate Authority certificates that have signed the remote broker # certificate. # bridge_capath defines a directory that will be searched for files containing # the CA certificates. For bridge_capath to work correctly, the certificate # files must have ".crt" as the file ending and you must run "openssl rehash # " each time you add/remove a certificate. #bridge_cafile #bridge_capath # If the remote broker has more than one protocol available on its port, e.g. # MQTT and WebSockets, then use bridge_alpn to configure which protocol is # requested. Note that WebSockets support for bridges is not yet available. #bridge_alpn # When using certificate based encryption, bridge_insecure disables # verification of the server hostname in the server certificate. This can be # useful when testing initial server configurations, but makes it possible for # a malicious third party to impersonate your server through DNS spoofing, for # example. Use this option in testing only. If you need to resort to using this # option in a production environment, your setup is at fault and there is no # point using encryption. #bridge_insecure false # Path to the PEM encoded client certificate, if required by the remote broker. #bridge_certfile # Path to the PEM encoded client private key, if required by the remote broker. #bridge_keyfile # ----------------------------------------------------------------- # PSK based SSL/TLS support # ----------------------------------------------------------------- # Pre-shared-key encryption provides an alternative to certificate based # encryption. A bridge can be configured to use PSK with the bridge_identity # and bridge_psk options. These are the client PSK identity, and pre-shared-key # in hexadecimal format with no "0x". Only one of certificate and PSK based # encryption can be used on one # bridge at once. #bridge_identity #bridge_psk # ================================================================= # External config files # ================================================================= # External configuration files may be included by using the # include_dir option. This defines a directory that will be searched # for config files. All files that end in '.conf' will be loaded as # a configuration file. It is best to have this as the last option # in the main file. This option will only be processed from the main # configuration file. The directory specified must not contain the # main configuration file. # Files within include_dir will be loaded sorted in case-sensitive # alphabetical order, with capital letters ordered first. If this option is # given multiple times, all of the files from the first instance will be # processed before the next instance. See the man page for examples. #include_dir mosquitto-2.0.18/plugins/000077500000000000000000000000001450213760600153335ustar00rootroot00000000000000mosquitto-2.0.18/plugins/CMakeLists.txt000066400000000000000000000002161450213760600200720ustar00rootroot00000000000000add_subdirectory(dynamic-security) if(NOT WIN32) add_subdirectory(message-timestamp) endif(NOT WIN32) add_subdirectory(payload-modification) mosquitto-2.0.18/plugins/Makefile000066400000000000000000000012221450213760600167700ustar00rootroot00000000000000DIRS= \ auth-by-ip \ deny-protocol-version \ dynamic-security \ message-timestamp \ payload-modification .PHONY : all binary check clean reallyclean test install uninstall all : set -e; for d in ${DIRS}; do $(MAKE) -C $${d}; done binary : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done clean : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done reallyclean : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done check : test test : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done install : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done uninstall : set -e; for d in ${DIRS}; do $(MAKE) -C $${d} $@; done mosquitto-2.0.18/plugins/README.md000066400000000000000000000027131450213760600166150ustar00rootroot00000000000000# Plugins This directory contains plugins for use with Mosquitto. ## Dynamic security This is a fully functioning plugin that implements authentication and access control, with configuration via a $CONTROL topic. See the readme in dynamic-security for more information. ## Message timestamp This is an **example** plugin to demonstrate how it is possible to attach MQTT v5 properties to messages after they have been received, and before they are sent on to subscribers. This plugin attaches a user-property property to each message which contains the ISO-8601 timestamp of the time the message was received by the broker. This means it is possible for MQTT v5 clients to see how old a retained message is, for example. ## Payload modification This is an **example** plugin to demonstrate how it is possible to modify the payload of messages after they have been received, and before they are sent on to subscribers. If you are considering using this feature, you should be very certain you have verified the payload is the correct format before modifying it. This plugin adds the text string "hello " to the beginning of each payload, so with anything other than simple plain text messages it will corrupt the payload contents. ## Authenticate by IP address This is an **example** plugin that demonstrates a basic authentication callback that allows clients based on their IP address. Password based authentication is preferred over this very simple type of access control. mosquitto-2.0.18/plugins/auth-by-ip/000077500000000000000000000000001450213760600173125ustar00rootroot00000000000000mosquitto-2.0.18/plugins/auth-by-ip/CMakeLists.txt000066400000000000000000000010261450213760600220510ustar00rootroot00000000000000include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH}) add_library(mosquitto_auth_by_ip MODULE mosquitto_auth_by_ip.c) set_target_properties(mosquitto_auth_by_ip PROPERTIES POSITION_INDEPENDENT_CODE 1 ) set_target_properties(mosquitto_auth_by_ip PROPERTIES PREFIX "") # Don't install, these are example plugins only. #install(TARGETS mosquitto_auth_by_ip RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") mosquitto-2.0.18/plugins/auth-by-ip/Makefile000066400000000000000000000012141450213760600207500ustar00rootroot00000000000000include ../../config.mk .PHONY : all binary check clean reallyclean test install uninstall PLUGIN_NAME=mosquitto_auth_by_ip all : binary binary : ${PLUGIN_NAME}.so ${PLUGIN_NAME}.so : ${PLUGIN_NAME}.c $(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) -fPIC -shared $< -o $@ reallyclean : clean clean: -rm -f *.o ${PLUGIN_NAME}.so *.gcda *.gcno check: test test: install: ${PLUGIN_NAME}.so # Don't install, these are examples only. #$(INSTALL) -d "${DESTDIR}$(libdir)" #$(INSTALL) ${STRIP_OPTS} ${PLUGIN_NAME}.so "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" uninstall : -rm -f "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" mosquitto-2.0.18/plugins/auth-by-ip/mosquitto_auth_by_ip.c000066400000000000000000000046711450213760600237350ustar00rootroot00000000000000/* Copyright (c) 2021 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 Contributors: Roger Light - initial implementation and documentation. */ /* * This is an example plugin showing how to use the basic authentication * callback to allow/disallow client connections based on client IP addresses. * * This is an extremely basic type of access control, password based or similar * authentication is preferred. * * Compile with: * gcc -I -fPIC -shared mosquitto_auth_by_ip.c -o mosquitto_auth_by_ip.so * * Use in config with: * * plugin /path/to/mosquitto_auth_by_ip.so * * Note that this only works on Mosquitto 2.0 or later. */ #include "config.h" #include #include #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mosquitto.h" #include "mqtt_protocol.h" static mosquitto_plugin_id_t *mosq_pid = NULL; static int basic_auth_callback(int event, void *event_data, void *userdata) { struct mosquitto_evt_basic_auth *ed = event_data; const char *ip_address; UNUSED(event); UNUSED(userdata); ip_address = mosquitto_client_address(ed->client); if(!strcmp(ip_address, "127.0.0.1")){ /* Only allow connections from localhost */ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } } int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { int i; for(i=0; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR EDL-1.0 Contributors: Roger Light - initial implementation and documentation. */ /* * This is an example plugin showing how to deny access based on the version of * the protocol spec a client connects with. It does no other authentication * checks. * * It could be used with other authentication plugins by specifying it in the * config file before another plugin, for example: * * plugin /usr/lib/mosquitto_deny_protocol_version.so * plugin /usr/lib/mosquitto_dynamic_security.so * * or: * * plugin /usr/lib/mosquitto_deny_protocol_version.so * password_file pwfile * * It will *not* work on its own. * * In Mosquitto 2.1, this can be achieved with the `accept_protocol_version` * option instead. * * * To compile: * * gcc -I -fPIC -shared mosquitto_deny_protocol_version.c -o mosquitto_deny_protocol_version.so * * Note that this only works on Mosquitto 2.0 or later. */ #include "config.h" #include #include #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mosquitto.h" #include "mqtt_protocol.h" static mosquitto_plugin_id_t *mosq_pid = NULL; int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { int i; for(i=0; iclient); if(protocol_version == 5 || protocol_version == 4){ /* Allow access to MQTT v5.0 and v3.1.1 - this passes on responsibility * for the actual auth checks to the next plugin/password file in the * config list. If no other plugins/password file is defined, then * access will be denied. */ return MOSQ_ERR_PLUGIN_DEFER; }else{ /* Deny access to all others */ return MOSQ_ERR_AUTH; } } int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *opts, int opt_count) { UNUSED(user_data); UNUSED(opts); UNUSED(opt_count); mosq_pid = identifier; return mosquitto_callback_register(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL, NULL); } int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *opts, int opt_count) { UNUSED(user_data); UNUSED(opts); UNUSED(opt_count); return mosquitto_callback_unregister(mosq_pid, MOSQ_EVT_BASIC_AUTH, basic_auth_callback, NULL); } mosquitto-2.0.18/plugins/deny-protocol-version/test.conf000066400000000000000000000001201450213760600234330ustar00rootroot00000000000000listener 1883 plugin ./mosquitto_deny_protocol_version.so password_file pwfile mosquitto-2.0.18/plugins/deny-protocol-version/test.sh000077500000000000000000000001731450213760600231330ustar00rootroot00000000000000#!/bin/sh ../../apps/mosquitto_passwd/mosquitto_passwd -c -b pwfile username password ../../src/mosquitto -c test.conf -v mosquitto-2.0.18/plugins/dynamic-security/000077500000000000000000000000001450213760600206245ustar00rootroot00000000000000mosquitto-2.0.18/plugins/dynamic-security/CMakeLists.txt000066400000000000000000000023671450213760600233740ustar00rootroot00000000000000if (CJSON_FOUND AND WITH_TLS) add_definitions("-DWITH_CJSON") set( CLIENT_INC ${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include ${STDBOOL_H_PATH} ${STDINT_H_PATH} ${PTHREAD_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR} ${mosquitto_SOURCE_DIR}/deps ${mosquitto_SOURCE_DIR}/src ${CJSON_INCLUDE_DIRS} ) set( CLIENT_DIR ${mosquitto_BINARY_DIR}/lib ${CJSON_DIR}) include_directories(${CLIENT_INC}) link_directories(${CLIENT_DIR} ${mosquitto_SOURCE_DIR}) add_library(mosquitto_dynamic_security MODULE acl.c auth.c clients.c clientlist.c dynamic_security.h groups.c grouplist.c json_help.c json_help.h plugin.c roles.c rolelist.c sub_matches_sub.c) set_target_properties(mosquitto_dynamic_security PROPERTIES POSITION_INDEPENDENT_CODE 1 ) set_target_properties(mosquitto_dynamic_security PROPERTIES PREFIX "") target_link_libraries(mosquitto_dynamic_security ${CJSON_LIBRARIES} ${OPENSSL_LIBRARIES}) if(WIN32) target_link_libraries(mosquitto_dynamic_security mosquitto) install(TARGETS mosquitto_dynamic_security DESTINATION "${CMAKE_INSTALL_BINDIR}") else() install(TARGETS mosquitto_dynamic_security RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") endif() endif() mosquitto-2.0.18/plugins/dynamic-security/Makefile000066400000000000000000000045331450213760600222710ustar00rootroot00000000000000include ../../config.mk .PHONY : all binary check clean reallyclean test install uninstall PLUGIN_NAME=mosquitto_dynamic_security LOCAL_CPPFLAGS=-I../../src/ -DWITH_CJSON OBJS= \ acl.o \ auth.o \ clients.o \ clientlist.o \ groups.o \ grouplist.o \ json_help.o \ plugin.o \ roles.o \ rolelist.o \ sub_matches_sub.o ifeq ($(WITH_CJSON),yes) ifeq ($(WITH_TLS),yes) ALL_DEPS:= binary else ALL_DEPS:= endif else ALL_DEPS:= endif all : ${ALL_DEPS} binary : ${PLUGIN_NAME}.so ${PLUGIN_NAME}.so : ${OBJS} ${CROSS_COMPILE}${CC} $(PLUGIN_LDFLAGS) -fPIC -shared $^ -o $@ -lcjson acl.o : acl.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ auth.o : auth.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ clients.o : clients.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ clientlist.o : clientlist.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ groups.o : groups.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ grouplist.o : grouplist.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ json_help.o : json_help.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ plugin.o : plugin.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ roles.o : roles.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ rolelist.o : rolelist.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ sub_matches_sub.o : sub_matches_sub.c dynamic_security.h ${CROSS_COMPILE}${CC} $(LOCAL_CPPFLAGS) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) -c $< -o $@ reallyclean : clean clean: -rm -f *.o ${PLUGIN_NAME}.so *.gcda *.gcno check: test test: install: all ifeq ($(WITH_CJSON),yes) ifeq ($(WITH_TLS),yes) $(INSTALL) -d "${DESTDIR}$(libdir)" $(INSTALL) ${STRIP_OPTS} ${PLUGIN_NAME}.so "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" endif endif uninstall : -rm -f "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" mosquitto-2.0.18/plugins/dynamic-security/README.md000066400000000000000000000234531450213760600221120ustar00rootroot00000000000000# Mosquitto Dynamic Security This document describes a topic based mechanism for controlling security in Mosquitto. JSON commands are published to topics like `$CONTROL//v1` ## Clients When a client connects to Mosquitto, it can optionally provide a username. The username maps the client instance to a client on the broker, if it exists. Multiple clients can make use of the same username, and hence the same broker client. ## Groups Broker clients can be defined as belonging to zero or more broker groups. ## Roles Roles can be applied to a client or a group, and define what that client/group is allowed to do, for example what topics it may or may not publish or subscribe to. ## Commands ### Set default ACL access Sets the default access behaviour for the different ACL types, assuming there are no matching ACLs for a topic. By default, publishClientSend and subscribe default to deny, and publishClientReceive and unsubscribe default to allow. Command: ``` { "commands":[ { "command": "setDefaultACLAccess", "acls":[ { "acltype": "publishClientSend", "allow": false }, { "acltype": "publishClientReceive", "allow": true }, { "acltype": "subscribe", "allow": false }, { "acltype": "unsubscribe", "allow": true } ] } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec setDefaultACLAccess subscribe deny ``` ### Get default ACL access Gets the default access behaviour for the different ACL types. Command: ``` { "commands":[ { "command": "getDefaultACLAccess" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec getDefaultACLAccess ``` ## Create Client Command: ``` { "commands":[ { "command": "createClient", "username": "new username", "password": "new password", "clientid": "", # Optional "textname": "", # Optional "textdescription": "", # Optional "groups": [ { "groupname": "group", "priority": 1 } ], # Optional, groups must exist "roles": [ { "rolename": "role", "priority": -1 } ] # Optional, roles must exist } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec createClient username password ``` ## Delete Client Command: ``` { "commands":[ { "command": "deleteClient", "username": "username to delete" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec deleteClient username ``` ## Enable Client Command: ``` { "commands":[ { "command": "enableClient", "username": "username to enable" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec enableClient username ``` ## Disable Client Stop a client from being able to log in, and kick any clients with matching username that are currently connected. Command: ``` { "commands":[ { "command": "disableClient", "username": "username to disable" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec disableClient username ``` ## Get Client Command: ``` { "commands":[ { "command": "getClient", "username": "required username" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec getClient username ``` ## List Clients Command: ``` { "commands":[ { "command": "listClients", "verbose": false, "count": -1, # -1 for all, or a positive integer for a limited count "offset": 0 # Where in the list to start } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec listClients 10 20 ``` ## Modify Existing Client Command: ``` { "commands":[ { "command": "modifyClient", "username": "username to modify" "clientid": "new clientid, or empty string to clear", # Optional "password": "new password", # Optional "textname": "", # Optional "textdescription": "", # Optional "roles": [ { "rolename": "role", "priority": 1 } ], # Optional "groups": [ { "groupname": "group", "priority": 1 } ], # Optional } ] } ``` Modifying clients isn't currently possible with mosquitto_ctrl. ## Set Client id Command: ``` { "commands":[ { "command": "setClientId", "username": "username to change", "clientid": "new clientid" # Optional, if blank or missing then client id will be removed. } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec setClientId username clientId ``` ## Set Client Password Command: ``` { "commands":[ { "command": "setClientPassword", "username": "username to change", "password": "new password" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec setClientPassword username password ``` ## Add Client Role Command: ``` { "commands":[ { "command": "addClientRole", "username": "client to add role to", "rolename": "role to add", "priority": -1 # Optional priority } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec addClientRole username rolename ``` ## Remove Client Role Command: ``` { "commands":[ { "command": "removeClientRole", "username": "client to remove role from", "rolename": "role to remove" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec removeClientRole username rolename ``` ## Add Client to a Group Command: ``` { "commands":[ { "command": "addGroupClient", "groupname": "group to add client to", "username": "client to add to group", "priority": -1 # Priority of the group for the client } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec addGroupClient groupname username ``` ## Create Group Command: ``` { "commands":[ { "command": "createGroup", "groupname": "new group", "roles": [ { "rolename": "role", "priority": 1 } ] # Optional, roles must exist } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec createGroup groupname ``` ## Delete Group Command: ``` { "commands":[ { "command": "deleteGroup", "groupname: "group to delete" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec deleteGroup groupname ``` ## Get Group Command: ``` { "commands":[ { "command": "getGroup", "groupname: "group to get" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec getGroup groupname ``` ## List Groups Command: ``` { "commands":[ { "command": "listGroups", "verbose": false, "count": -1, # -1 for all, or a positive integer for a limited count "offset": 0 # Where in the list to start } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec listGroups ``` ## Modify Group Command: ``` { "commands":[ { "command": "modifyGroup", "groupname": "group to modify", "textname": "", # Optional "textdescription": "", # Optional "roles": [ { "rolename": "role", "priority": 1 } ], # Optional "clients": [ { "username": "client", "priority": 1 } ] # Optional } ] } ``` Modifying groups isn't currently possible with mosquitto_ctrl. ## Remove Client from a Group Command: ``` { "commands":[ { "command": "removeGroupClient", "groupname": "group to remove client from", "username": "client to remove from group" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec removeGroupClient groupname username ``` ## Add Group Role Command: ``` { "commands":[ { "command": "addGroupRole", "groupname": "group to add role to", "rolename": "role to add", "priority": -1 # Optional priority } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec addGroupRole groupname rolename ``` ## Remove Group Role Command: ``` { "commands":[ { "command": "removeGroupRole", "groupname": "group", "rolename": "role" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec removeGroupRole groupname rolename ``` ## Set Group for Anonymous Clients Command: ``` { "commands":[ { "command": "setAnonymousGroup", "groupname": "group" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec setAnonymousGroup groupname ``` ## Get Group for Anonymous Clients Command: ``` { "commands":[ { "command": "getAnonymousGroup" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec getAnonymousGroup ``` ## Create Role Command: ``` { "commands":[ { "command": "createRole", "rolename": "new role", "textname": "", # Optional "textdescription": "", # Optional "acls": [ { "acltype": "subscribePattern", "topic": "topic/#", "priority": -1, "allow": true} ] # Optional } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec createRole rolename ``` ## Get Role Command: ``` { "commands":[ { "command": "getRole", "rolename": "role", } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec getRole rolename ``` ## List Roles Command: ``` { "commands":[ { "command": "listRoles", "verbose": false, "count": -1, # -1 for all, or a positive integer for a limited count "offset": 0 # Where in the list to start } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec listRoles ``` ## Modify Role Command: ``` { "commands":[ { "command": "modifyRole", "rolename": "role to modify" "textname": "", # Optional "textdescription": "", # Optional "acls": [ { "acltype": "subscribePattern", "topic": "topic/#", "priority": -1, "allow": true } ] # Optional } ] } ``` Modifying roles isn't currently possible with mosquitto_ctrl. ## Delete Role Command: ``` { "commands":[ { "command": "deleteRole", "rolename": "role" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec deleteRole rolename ``` ## Add Role ACL Command: ``` { "commands":[ { "command": "addRoleACL", "rolename": "role", "acltype": "subscribePattern", "topic": "topic/#", "priority": -1, "allow": true } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec addRoleACL rolename subscribeLiteral topic/# deny ``` ## Remove Role ACL Command: ``` { "commands":[ { "command": "removeRoleACL", "rolename": "role", "acltype": "subscribePattern", "topic": "topic/#" } ] } ``` mosquitto_ctrl example: ``` mosquitto_ctrl dynsec removeRoleACL rolename subscribeLiteral topic/# ``` mosquitto-2.0.18/plugins/dynamic-security/acl.c000066400000000000000000000155661450213760600215440ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "dynamic_security.h" #include "mosquitto.h" #include "mosquitto_broker.h" #include "mosquitto_plugin.h" typedef int (*MOSQ_FUNC_acl_check)(struct mosquitto_evt_acl_check *, struct dynsec__rolelist *); /* FIXME - CACHE! */ /* ################################################################ * # * # ACL check - publish broker to client * # * ################################################################ */ static int acl_check_publish_c_recv(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; struct dynsec__acl *acl, *acl_tmp = NULL; bool result; HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ HASH_ITER(hh, rolelist->role->acls.publish_c_recv, acl, acl_tmp){ mosquitto_topic_matches_sub(acl->topic, ed->topic, &result); if(result){ if(acl->allow){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } } } return MOSQ_ERR_NOT_FOUND; } /* ################################################################ * # * # ACL check - publish client to broker * # * ################################################################ */ static int acl_check_publish_c_send(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; struct dynsec__acl *acl, *acl_tmp = NULL; bool result; HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ HASH_ITER(hh, rolelist->role->acls.publish_c_send, acl, acl_tmp){ mosquitto_topic_matches_sub(acl->topic, ed->topic, &result); if(result){ if(acl->allow){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } } } return MOSQ_ERR_NOT_FOUND; } /* ################################################################ * # * # ACL check - subscribe * # * ################################################################ */ static int acl_check_subscribe(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; struct dynsec__acl *acl, *acl_tmp = NULL; size_t len; len = strlen(ed->topic); HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ HASH_FIND(hh, rolelist->role->acls.subscribe_literal, ed->topic, len, acl); if(acl){ if(acl->allow){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } HASH_ITER(hh, rolelist->role->acls.subscribe_pattern, acl, acl_tmp){ if(sub_acl_check(acl->topic, ed->topic)){ if(acl->allow){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } } } return MOSQ_ERR_NOT_FOUND; } /* ################################################################ * # * # ACL check - unsubscribe * # * ################################################################ */ static int acl_check_unsubscribe(struct mosquitto_evt_acl_check *ed, struct dynsec__rolelist *base_rolelist) { struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; struct dynsec__acl *acl, *acl_tmp = NULL; size_t len; len = strlen(ed->topic); HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ HASH_FIND(hh, rolelist->role->acls.unsubscribe_literal, ed->topic, len, acl); if(acl){ if(acl->allow){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } HASH_ITER(hh, rolelist->role->acls.unsubscribe_pattern, acl, acl_tmp){ if(sub_acl_check(acl->topic, ed->topic)){ if(acl->allow){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } } } return MOSQ_ERR_NOT_FOUND; } /* ################################################################ * # * # ACL check - generic check * # * ################################################################ */ static int acl_check(struct mosquitto_evt_acl_check *ed, MOSQ_FUNC_acl_check check, bool acl_default_access) { struct dynsec__client *client; struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL; const char *username; int rc; username = mosquitto_client_username(ed->client); if(username){ client = dynsec_clients__find(username); if(client == NULL) return MOSQ_ERR_PLUGIN_DEFER; /* Client roles */ rc = check(ed, client->rolelist); if(rc != MOSQ_ERR_NOT_FOUND){ return rc; } HASH_ITER(hh, client->grouplist, grouplist, grouplist_tmp){ rc = check(ed, grouplist->group->rolelist); if(rc != MOSQ_ERR_NOT_FOUND){ return rc; } } }else if(dynsec_anonymous_group){ /* If we have a group for anonymous users, use that for checking. */ rc = check(ed, dynsec_anonymous_group->rolelist); if(rc != MOSQ_ERR_NOT_FOUND){ return rc; } } if(acl_default_access == false){ return MOSQ_ERR_PLUGIN_DEFER; }else{ if(!strncmp(ed->topic, "$CONTROL", strlen("$CONTROL"))){ /* We never give fall through access to $CONTROL topics, they must * be granted explicitly. */ return MOSQ_ERR_PLUGIN_DEFER; }else{ return MOSQ_ERR_SUCCESS; } } } /* ################################################################ * # * # ACL check - plugin callback * # * ################################################################ */ int dynsec__acl_check_callback(int event, void *event_data, void *userdata) { struct mosquitto_evt_acl_check *ed = event_data; UNUSED(event); UNUSED(userdata); /* ACL checks are made in the order below until a match occurs, at which * point the decision is made. * * User roles in priority order highest to lowest. * Roles have their ACLs checked in priority order, highest to lowest * Groups are processed in priority order highest to lowest * Group roles are processed in priority order, highest to lowest * Roles have their ACLs checked in priority order, highest to lowest */ switch(ed->access){ case MOSQ_ACL_SUBSCRIBE: return acl_check(event_data, acl_check_subscribe, default_access.subscribe); break; case MOSQ_ACL_UNSUBSCRIBE: return acl_check(event_data, acl_check_unsubscribe, default_access.unsubscribe); break; case MOSQ_ACL_WRITE: /* Client to broker */ return acl_check(event_data, acl_check_publish_c_send, default_access.publish_c_send); break; case MOSQ_ACL_READ: return acl_check(event_data, acl_check_publish_c_recv, default_access.publish_c_recv); break; default: return MOSQ_ERR_PLUGIN_DEFER; } return MOSQ_ERR_PLUGIN_DEFER; } mosquitto-2.0.18/plugins/dynamic-security/auth.c000066400000000000000000000111671450213760600217370ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include "dynamic_security.h" #include "mosquitto.h" #include "mosquitto_broker.h" /* ################################################################ * # * # Base64 encoding/decoding * # * ################################################################ */ int dynsec_auth__base64_encode(unsigned char *in, int in_len, char **encoded) { BIO *bmem, *b64; BUF_MEM *bptr = NULL; if(in_len < 0) return 1; b64 = BIO_new(BIO_f_base64()); if(b64 == NULL) return 1; BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bmem = BIO_new(BIO_s_mem()); if(bmem == NULL){ BIO_free_all(b64); return 1; } b64 = BIO_push(b64, bmem); BIO_write(b64, in, in_len); if(BIO_flush(b64) != 1){ BIO_free_all(b64); return 1; } BIO_get_mem_ptr(b64, &bptr); *encoded = mosquitto_malloc(bptr->length+1); if(!(*encoded)){ BIO_free_all(b64); return 1; } memcpy(*encoded, bptr->data, bptr->length); (*encoded)[bptr->length] = '\0'; BIO_free_all(b64); return 0; } int dynsec_auth__base64_decode(char *in, unsigned char **decoded, int *decoded_len) { BIO *bmem, *b64; size_t slen; slen = strlen(in); b64 = BIO_new(BIO_f_base64()); if(!b64){ return 1; } BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bmem = BIO_new(BIO_s_mem()); if(!bmem){ BIO_free_all(b64); return 1; } b64 = BIO_push(b64, bmem); BIO_write(bmem, in, (int)slen); if(BIO_flush(bmem) != 1){ BIO_free_all(b64); return 1; } *decoded = mosquitto_calloc(slen, 1); if(!(*decoded)){ BIO_free_all(b64); return 1; } *decoded_len = BIO_read(b64, *decoded, (int)slen); BIO_free_all(b64); if(*decoded_len <= 0){ mosquitto_free(*decoded); *decoded = NULL; *decoded_len = 0; return 1; } return 0; } /* ################################################################ * # * # Password functions * # * ################################################################ */ int dynsec_auth__pw_hash(struct dynsec__client *client, const char *password, unsigned char *password_hash, int password_hash_len, bool new_password) { const EVP_MD *digest; int iterations; if(new_password){ if(RAND_bytes(client->pw.salt, sizeof(client->pw.salt)) != 1){ return MOSQ_ERR_UNKNOWN; } iterations = PW_DEFAULT_ITERATIONS; }else{ iterations = client->pw.iterations; } if(iterations < 1){ return MOSQ_ERR_INVAL; } client->pw.iterations = iterations; digest = EVP_get_digestbyname("sha512"); if(!digest){ return MOSQ_ERR_UNKNOWN; } return !PKCS5_PBKDF2_HMAC(password, (int)strlen(password), client->pw.salt, sizeof(client->pw.salt), iterations, digest, password_hash_len, password_hash); } /* ################################################################ * # * # Username/password check * # * ################################################################ */ static int memcmp_const(const void *a, const void *b, size_t len) { size_t i; int rc = 0; if(!a || !b) return 1; for(i=0; iusername == NULL || ed->password == NULL) return MOSQ_ERR_PLUGIN_DEFER; client = dynsec_clients__find(ed->username); if(client){ if(client->disabled){ return MOSQ_ERR_AUTH; } if(client->clientid){ clientid = mosquitto_client_id(ed->client); if(clientid == NULL || strcmp(client->clientid, clientid)){ return MOSQ_ERR_AUTH; } } if(client->pw.valid && dynsec_auth__pw_hash(client, ed->password, password_hash, sizeof(password_hash), false) == MOSQ_ERR_SUCCESS){ if(memcmp_const(client->pw.password_hash, password_hash, sizeof(password_hash)) == 0){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } }else{ return MOSQ_ERR_PLUGIN_DEFER; } }else{ return MOSQ_ERR_PLUGIN_DEFER; } } mosquitto-2.0.18/plugins/dynamic-security/clientlist.c000066400000000000000000000077071450213760600231550ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto.h" #include "mosquitto_broker.h" #include "json_help.h" #include "dynamic_security.h" /* ################################################################ * # * # Plugin global variables * # * ################################################################ */ /* ################################################################ * # * # Function declarations * # * ################################################################ */ /* ################################################################ * # * # Local variables * # * ################################################################ */ /* ################################################################ * # * # Utility functions * # * ################################################################ */ static int dynsec_clientlist__cmp(void *a, void *b) { struct dynsec__clientlist *clientlist_a = a; struct dynsec__clientlist *clientlist_b = b; return strcmp(clientlist_a->client->username, clientlist_b->client->username); } void dynsec_clientlist__kick_all(struct dynsec__clientlist *base_clientlist) { struct dynsec__clientlist *clientlist, *clientlist_tmp; HASH_ITER(hh, base_clientlist, clientlist, clientlist_tmp){ mosquitto_kick_client_by_username(clientlist->client->username, false); } } cJSON *dynsec_clientlist__all_to_json(struct dynsec__clientlist *base_clientlist) { struct dynsec__clientlist *clientlist, *clientlist_tmp; cJSON *j_clients, *j_client; j_clients = cJSON_CreateArray(); if(j_clients == NULL) return NULL; HASH_ITER(hh, base_clientlist, clientlist, clientlist_tmp){ j_client = cJSON_CreateObject(); if(j_client == NULL){ cJSON_Delete(j_clients); return NULL; } cJSON_AddItemToArray(j_clients, j_client); if(cJSON_AddStringToObject(j_client, "username", clientlist->client->username) == NULL || (clientlist->priority != -1 && cJSON_AddIntToObject(j_client, "priority", clientlist->priority) == NULL) ){ cJSON_Delete(j_clients); return NULL; } } return j_clients; } int dynsec_clientlist__add(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client, int priority) { struct dynsec__clientlist *clientlist; HASH_FIND(hh, *base_clientlist, client->username, strlen(client->username), clientlist); if(clientlist != NULL){ /* Client is already in the group */ return MOSQ_ERR_SUCCESS; } clientlist = mosquitto_malloc(sizeof(struct dynsec__clientlist)); if(clientlist == NULL){ return MOSQ_ERR_NOMEM; } clientlist->client = client; clientlist->priority = priority; HASH_ADD_KEYPTR_INORDER(hh, *base_clientlist, client->username, strlen(client->username), clientlist, dynsec_clientlist__cmp); return MOSQ_ERR_SUCCESS; } void dynsec_clientlist__cleanup(struct dynsec__clientlist **base_clientlist) { struct dynsec__clientlist *clientlist, *clientlist_tmp; HASH_ITER(hh, *base_clientlist, clientlist, clientlist_tmp){ HASH_DELETE(hh, *base_clientlist, clientlist); mosquitto_free(clientlist); } } void dynsec_clientlist__remove(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client) { struct dynsec__clientlist *clientlist; HASH_FIND(hh, *base_clientlist, client->username, strlen(client->username), clientlist); if(clientlist){ HASH_DELETE(hh, *base_clientlist, clientlist); mosquitto_free(clientlist); } } mosquitto-2.0.18/plugins/dynamic-security/clients.c000066400000000000000000001145341450213760600224410ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto.h" #include "mosquitto_broker.h" #include "json_help.h" #include "dynamic_security.h" /* ################################################################ * # * # Function declarations * # * ################################################################ */ static int dynsec__remove_client_from_all_groups(const char *username); static void client__remove_all_roles(struct dynsec__client *client); /* ################################################################ * # * # Local variables * # * ################################################################ */ static struct dynsec__client *local_clients = NULL; /* ################################################################ * # * # Utility functions * # * ################################################################ */ static int client_cmp(void *a, void *b) { struct dynsec__client *client_a = a; struct dynsec__client *client_b = b; return strcmp(client_a->username, client_b->username); } struct dynsec__client *dynsec_clients__find(const char *username) { struct dynsec__client *client = NULL; if(username){ HASH_FIND(hh, local_clients, username, strlen(username), client); } return client; } static void client__free_item(struct dynsec__client *client) { struct dynsec__client *client_found; if(client == NULL) return; client_found = dynsec_clients__find(client->username); if(client_found){ HASH_DEL(local_clients, client_found); } dynsec_rolelist__cleanup(&client->rolelist); dynsec__remove_client_from_all_groups(client->username); mosquitto_free(client->text_name); mosquitto_free(client->text_description); mosquitto_free(client->clientid); mosquitto_free(client->username); mosquitto_free(client); } void dynsec_clients__cleanup(void) { struct dynsec__client *client, *client_tmp; HASH_ITER(hh, local_clients, client, client_tmp){ client__free_item(client); } } /* ################################################################ * # * # Config file load and save * # * ################################################################ */ int dynsec_clients__config_load(cJSON *tree) { cJSON *j_clients, *j_client, *j_roles, *j_role; struct dynsec__client *client; struct dynsec__role *role; unsigned char *buf; int buf_len; int priority; j_clients = cJSON_GetObjectItem(tree, "clients"); if(j_clients == NULL){ return 0; } if(cJSON_IsArray(j_clients) == false){ return 1; } cJSON_ArrayForEach(j_client, j_clients){ if(cJSON_IsObject(j_client) == true){ /* Username */ char *username; json_get_string(j_client, "username", &username, false); if(!username){ continue; } client = dynsec_clients__find(username); if(client){ continue; } client = mosquitto_calloc(1, sizeof(struct dynsec__client)); if(client == NULL){ return MOSQ_ERR_NOMEM; } client->username = mosquitto_strdup(username); if(client->username == NULL){ mosquitto_free(client); continue; } bool disabled; if(json_get_bool(j_client, "disabled", &disabled, false, false) == MOSQ_ERR_SUCCESS){ client->disabled = disabled; } /* Salt */ char *salt, *password; int iterations; json_get_string(j_client, "salt", &salt, false); json_get_string(j_client, "password", &password, false); json_get_int(j_client, "iterations", &iterations, false, -1); if(salt && password && iterations > 0){ client->pw.iterations = iterations; if(dynsec_auth__base64_decode(salt, &buf, &buf_len) != MOSQ_ERR_SUCCESS || buf_len != sizeof(client->pw.salt)){ mosquitto_free(client->username); mosquitto_free(client); continue; } memcpy(client->pw.salt, buf, (size_t)buf_len); mosquitto_free(buf); if(dynsec_auth__base64_decode(password, &buf, &buf_len) != MOSQ_ERR_SUCCESS || buf_len != sizeof(client->pw.password_hash)){ mosquitto_free(client->username); mosquitto_free(client); continue; } memcpy(client->pw.password_hash, buf, (size_t)buf_len); mosquitto_free(buf); client->pw.valid = true; }else{ client->pw.valid = false; } /* Client id */ char *clientid; json_get_string(j_client, "clientid", &clientid, false); if(clientid){ client->clientid = mosquitto_strdup(clientid); if(client->clientid == NULL){ mosquitto_free(client->username); mosquitto_free(client); continue; } } /* Text name */ char *textname; json_get_string(j_client, "textname", &textname, false); if(textname){ client->text_name = mosquitto_strdup(textname); if(client->text_name == NULL){ mosquitto_free(client->clientid); mosquitto_free(client->username); mosquitto_free(client); continue; } } /* Text description */ char *textdescription; json_get_string(j_client, "textdescription", &textdescription, false); if(textdescription){ client->text_description = mosquitto_strdup(textdescription); if(client->text_description == NULL){ mosquitto_free(client->text_name); mosquitto_free(client->clientid); mosquitto_free(client->username); mosquitto_free(client); continue; } } /* Roles */ j_roles = cJSON_GetObjectItem(j_client, "roles"); if(j_roles && cJSON_IsArray(j_roles)){ cJSON_ArrayForEach(j_role, j_roles){ if(cJSON_IsObject(j_role)){ char *rolename; json_get_string(j_role, "rolename", &rolename, false); if(rolename){ json_get_int(j_role, "priority", &priority, true, -1); role = dynsec_roles__find(rolename); dynsec_rolelist__client_add(client, role, priority); } } } } HASH_ADD_KEYPTR(hh, local_clients, client->username, strlen(client->username), client); } } HASH_SORT(local_clients, client_cmp); return 0; } static int dynsec__config_add_clients(cJSON *j_clients) { struct dynsec__client *client, *client_tmp; cJSON *j_client, *j_roles, *jtmp; char *buf; HASH_ITER(hh, local_clients, client, client_tmp){ j_client = cJSON_CreateObject(); if(j_client == NULL) return 1; cJSON_AddItemToArray(j_clients, j_client); if(cJSON_AddStringToObject(j_client, "username", client->username) == NULL || (client->clientid && cJSON_AddStringToObject(j_client, "clientid", client->clientid) == NULL) || (client->text_name && cJSON_AddStringToObject(j_client, "textname", client->text_name) == NULL) || (client->text_description && cJSON_AddStringToObject(j_client, "textdescription", client->text_description) == NULL) || (client->disabled && cJSON_AddBoolToObject(j_client, "disabled", true) == NULL) ){ return 1; } j_roles = dynsec_rolelist__all_to_json(client->rolelist); if(j_roles == NULL){ return 1; } cJSON_AddItemToObject(j_client, "roles", j_roles); if(client->pw.valid){ if(dynsec_auth__base64_encode(client->pw.password_hash, sizeof(client->pw.password_hash), &buf) != MOSQ_ERR_SUCCESS){ return 1; } jtmp = cJSON_CreateString(buf); mosquitto_free(buf); if(jtmp == NULL) return 1; cJSON_AddItemToObject(j_client, "password", jtmp); if(dynsec_auth__base64_encode(client->pw.salt, sizeof(client->pw.salt), &buf) != MOSQ_ERR_SUCCESS){ return 1; } jtmp = cJSON_CreateString(buf); mosquitto_free(buf); if(jtmp == NULL) return 1; cJSON_AddItemToObject(j_client, "salt", jtmp); if(cJSON_AddIntToObject(j_client, "iterations", client->pw.iterations) == NULL){ return 1; } } } return 0; } int dynsec_clients__config_save(cJSON *tree) { cJSON *j_clients; if((j_clients = cJSON_AddArrayToObject(tree, "clients")) == NULL){ return 1; } if(dynsec__config_add_clients(j_clients)){ return 1; } return 0; } int dynsec_clients__process_create(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username, *password, *clientid = NULL; char *text_name, *text_description; struct dynsec__client *client; int rc; cJSON *j_groups, *j_group; int priority; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "password", &password, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Invalid/missing password", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "clientid", &clientid, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Invalid/missing client id", correlation_data); return MOSQ_ERR_INVAL; } if(clientid && mosquitto_validate_utf8(clientid, (int)strlen(clientid)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Client ID not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textname", &text_name, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Invalid/missing textname", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textdescription", &text_description, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Invalid/missing textdescription", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client){ dynsec__command_reply(j_responses, context, "createClient", "Client already exists", correlation_data); return MOSQ_ERR_SUCCESS; } client = mosquitto_calloc(1, sizeof(struct dynsec__client)); if(client == NULL){ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } client->username = mosquitto_strdup(username); if(client->username == NULL){ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); client__free_item(client); return MOSQ_ERR_NOMEM; } if(text_name){ client->text_name = mosquitto_strdup(text_name); if(client->text_name == NULL){ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); client__free_item(client); return MOSQ_ERR_NOMEM; } } if(text_description){ client->text_description = mosquitto_strdup(text_description); if(client->text_description == NULL){ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); client__free_item(client); return MOSQ_ERR_NOMEM; } } if(password){ if(dynsec_auth__pw_hash(client, password, client->pw.password_hash, sizeof(client->pw.password_hash), true)){ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); client__free_item(client); return MOSQ_ERR_NOMEM; } client->pw.valid = true; } if(clientid && strlen(clientid) > 0){ client->clientid = mosquitto_strdup(clientid); if(client->clientid == NULL){ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); client__free_item(client); return MOSQ_ERR_NOMEM; } } rc = dynsec_rolelist__load_from_json(command, &client->rolelist); if(rc == MOSQ_ERR_SUCCESS || rc == ERR_LIST_NOT_FOUND){ }else if(rc == MOSQ_ERR_NOT_FOUND){ dynsec__command_reply(j_responses, context, "createClient", "Role not found", correlation_data); client__free_item(client); return MOSQ_ERR_INVAL; }else{ if(rc == MOSQ_ERR_INVAL){ dynsec__command_reply(j_responses, context, "createClient", "'roles' not an array or missing/invalid rolename", correlation_data); }else{ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); } client__free_item(client); return MOSQ_ERR_INVAL; } /* Must add user before groups, otherwise adding groups will fail */ HASH_ADD_KEYPTR_INORDER(hh, local_clients, client->username, strlen(client->username), client, client_cmp); j_groups = cJSON_GetObjectItem(command, "groups"); if(j_groups && cJSON_IsArray(j_groups)){ cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group)){ char *groupname; json_get_string(j_group, "groupname", &groupname, false); if(groupname){ json_get_int(j_group, "priority", &priority, true, -1); rc = dynsec_groups__add_client(username, groupname, priority, false); if(rc == ERR_GROUP_NOT_FOUND){ dynsec__command_reply(j_responses, context, "createClient", "Group not found", correlation_data); client__free_item(client); return MOSQ_ERR_INVAL; }else if(rc != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createClient", "Internal error", correlation_data); client__free_item(client); return MOSQ_ERR_INVAL; } } } } } dynsec__config_save(); dynsec__command_reply(j_responses, context, "createClient", NULL, correlation_data); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | createClient | username=%s | password=%s", admin_clientid, admin_username, username, password?"*****":"no password"); return MOSQ_ERR_SUCCESS; } int dynsec_clients__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username; struct dynsec__client *client; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "deleteClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client){ dynsec__remove_client_from_all_groups(username); client__remove_all_roles(client); client__free_item(client); dynsec__config_save(); dynsec__command_reply(j_responses, context, "deleteClient", NULL, correlation_data); /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | deleteClient | username=%s", admin_clientid, admin_username, username); return MOSQ_ERR_SUCCESS; }else{ dynsec__command_reply(j_responses, context, "deleteClient", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } } int dynsec_clients__process_disable(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username; struct dynsec__client *client; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "disableClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "disableClient", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "disableClient", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } client->disabled = true; mosquitto_kick_client_by_username(username, false); dynsec__config_save(); dynsec__command_reply(j_responses, context, "disableClient", NULL, correlation_data); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | disableClient | username=%s", admin_clientid, admin_username, username); return MOSQ_ERR_SUCCESS; } int dynsec_clients__process_enable(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username; struct dynsec__client *client; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "enableClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "enableClient", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "enableClient", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } client->disabled = false; dynsec__config_save(); dynsec__command_reply(j_responses, context, "enableClient", NULL, correlation_data); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | enableClient | username=%s", admin_clientid, admin_username, username); return MOSQ_ERR_SUCCESS; } int dynsec_clients__process_set_id(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username, *clientid, *clientid_heap = NULL; struct dynsec__client *client; size_t slen; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setClientId", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setClientId", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "clientid", &clientid, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setClientId", "Invalid/missing client ID", correlation_data); return MOSQ_ERR_INVAL; } if(clientid){ slen = strlen(clientid); if(mosquitto_validate_utf8(clientid, (int)slen) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setClientId", "Client ID not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(slen > 0){ clientid_heap = mosquitto_strdup(clientid); if(clientid_heap == NULL){ dynsec__command_reply(j_responses, context, "setClientId", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } }else{ clientid_heap = NULL; } } client = dynsec_clients__find(username); if(client == NULL){ mosquitto_free(clientid_heap); dynsec__command_reply(j_responses, context, "setClientId", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } mosquitto_free(client->clientid); client->clientid = clientid_heap; dynsec__config_save(); dynsec__command_reply(j_responses, context, "setClientId", NULL, correlation_data); /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | setClientId | username=%s | clientid=%s", admin_clientid, admin_username, username, client->clientid); return MOSQ_ERR_SUCCESS; } static int client__set_password(struct dynsec__client *client, const char *password) { if(dynsec_auth__pw_hash(client, password, client->pw.password_hash, sizeof(client->pw.password_hash), true) == MOSQ_ERR_SUCCESS){ client->pw.valid = true; return MOSQ_ERR_SUCCESS; }else{ client->pw.valid = false; /* FIXME - this should fail safe without modifying the existing password */ return MOSQ_ERR_NOMEM; } } int dynsec_clients__process_set_password(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username, *password; struct dynsec__client *client; int rc; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setClientPassword", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setClientPassword", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "password", &password, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setClientPassword", "Invalid/missing password", correlation_data); return MOSQ_ERR_INVAL; } if(strlen(password) == 0){ dynsec__command_reply(j_responses, context, "setClientPassword", "Empty password is not allowed", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "setClientPassword", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } rc = client__set_password(client, password); if(rc == MOSQ_ERR_SUCCESS){ dynsec__config_save(); dynsec__command_reply(j_responses, context, "setClientPassword", NULL, correlation_data); /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | setClientPassword | username=%s | password=******", admin_clientid, admin_username, username); }else{ dynsec__command_reply(j_responses, context, "setClientPassword", "Internal error", correlation_data); } return rc; } static void client__add_new_roles(struct dynsec__client *client, struct dynsec__rolelist *base_rolelist) { struct dynsec__rolelist *rolelist, *rolelist_tmp; HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ dynsec_rolelist__client_add(client, rolelist->role, rolelist->priority); } } static void client__remove_all_roles(struct dynsec__client *client) { struct dynsec__rolelist *rolelist, *rolelist_tmp; HASH_ITER(hh, client->rolelist, rolelist, rolelist_tmp){ dynsec_rolelist__client_remove(client, rolelist->role); } } int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username; char *clientid = NULL; char *password = NULL; char *text_name = NULL, *text_description = NULL; bool have_clientid = false, have_text_name = false, have_text_description = false, have_rolelist = false, have_password = false; struct dynsec__client *client; struct dynsec__group *group; struct dynsec__rolelist *rolelist = NULL; char *str; int rc; int priority; cJSON *j_group, *j_groups; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "modifyClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "modifyClient", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "modifyClient", "Client not found", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "clientid", &str, false) == MOSQ_ERR_SUCCESS){ have_clientid = true; if(str && strlen(str) > 0){ clientid = mosquitto_strdup(str); if(clientid == NULL){ dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } }else{ clientid = NULL; } } if(json_get_string(command, "password", &password, false) == MOSQ_ERR_SUCCESS){ if(strlen(password) > 0){ have_password = true; } } if(json_get_string(command, "textname", &str, false) == MOSQ_ERR_SUCCESS){ have_text_name = true; text_name = mosquitto_strdup(str); if(text_name == NULL){ dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } } if(json_get_string(command, "textdescription", &str, false) == MOSQ_ERR_SUCCESS){ have_text_description = true; text_description = mosquitto_strdup(str); if(text_description == NULL){ dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } } rc = dynsec_rolelist__load_from_json(command, &rolelist); if(rc == MOSQ_ERR_SUCCESS){ have_rolelist = true; }else if(rc == ERR_LIST_NOT_FOUND){ /* There was no list in the JSON, so no modification */ }else if(rc == MOSQ_ERR_NOT_FOUND){ dynsec__command_reply(j_responses, context, "modifyClient", "Role not found", correlation_data); rc = MOSQ_ERR_INVAL; goto error; }else{ if(rc == MOSQ_ERR_INVAL){ dynsec__command_reply(j_responses, context, "modifyClient", "'roles' not an array or missing/invalid rolename", correlation_data); }else{ dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data); } rc = MOSQ_ERR_INVAL; goto error; } j_groups = cJSON_GetObjectItem(command, "groups"); if(j_groups && cJSON_IsArray(j_groups)){ /* Iterate through list to check all groups are valid */ cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group)){ char *groupname; json_get_string(j_group, "groupname", &groupname, false); if(groupname){ group = dynsec_groups__find(groupname); if(group == NULL){ dynsec__command_reply(j_responses, context, "modifyClient", "'groups' contains an object with a 'groupname' that does not exist", correlation_data); rc = MOSQ_ERR_INVAL; goto error; } }else{ dynsec__command_reply(j_responses, context, "modifyClient", "'groups' contains an object with an invalid 'groupname'", correlation_data); rc = MOSQ_ERR_INVAL; goto error; } } } dynsec__remove_client_from_all_groups(username); cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group)){ char *groupname; json_get_string(j_group, "groupname", &groupname, false); if(groupname){ json_get_int(j_group, "priority", &priority, true, -1); dynsec_groups__add_client(username, groupname, priority, false); } } } } if(have_password){ /* FIXME - This is the one call that will result in modification on internal error - note that groups have already been modified */ rc = client__set_password(client, password); if(rc != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "modifyClient", "Internal error", correlation_data); mosquitto_kick_client_by_username(username, false); /* If this fails we have the situation that the password is set as * invalid, but the config isn't saved, so restarting the broker * *now* will mean the client can log in again. This might be * "good", but is inconsistent, so save the config to be * consistent. */ dynsec__config_save(); rc = MOSQ_ERR_NOMEM; goto error; } } if(have_clientid){ mosquitto_free(client->clientid); client->clientid = clientid; } if(have_text_name){ mosquitto_free(client->text_name); client->text_name = text_name; } if(have_text_description){ mosquitto_free(client->text_description); client->text_description = text_description; } if(have_rolelist){ client__remove_all_roles(client); client__add_new_roles(client, rolelist); dynsec_rolelist__cleanup(&rolelist); } dynsec__config_save(); dynsec__command_reply(j_responses, context, "modifyClient", NULL, correlation_data); /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyClient | username=%s", admin_clientid, admin_username, username); return MOSQ_ERR_SUCCESS; error: mosquitto_free(clientid); mosquitto_free(text_name); mosquitto_free(text_description); dynsec_rolelist__cleanup(&rolelist); return rc; } static int dynsec__remove_client_from_all_groups(const char *username) { struct dynsec__grouplist *grouplist, *grouplist_tmp; struct dynsec__client *client; client = dynsec_clients__find(username); if(client){ HASH_ITER(hh, client->grouplist, grouplist, grouplist_tmp){ dynsec_groups__remove_client(username, grouplist->group->groupname, false); } } return MOSQ_ERR_SUCCESS; } static cJSON *add_client_to_json(struct dynsec__client *client, bool verbose) { cJSON *j_client = NULL, *j_groups, *j_roles; if(verbose){ j_client = cJSON_CreateObject(); if(j_client == NULL){ return NULL; } if(cJSON_AddStringToObject(j_client, "username", client->username) == NULL || (client->clientid && cJSON_AddStringToObject(j_client, "clientid", client->clientid) == NULL) || (client->text_name && cJSON_AddStringToObject(j_client, "textname", client->text_name) == NULL) || (client->text_description && cJSON_AddStringToObject(j_client, "textdescription", client->text_description) == NULL) || (client->disabled && cJSON_AddBoolToObject(j_client, "disabled", client->disabled) == NULL) ){ cJSON_Delete(j_client); return NULL; } j_roles = dynsec_rolelist__all_to_json(client->rolelist); if(j_roles == NULL){ cJSON_Delete(j_client); return NULL; } cJSON_AddItemToObject(j_client, "roles", j_roles); j_groups = dynsec_grouplist__all_to_json(client->grouplist); if(j_groups == NULL){ cJSON_Delete(j_client); return NULL; } cJSON_AddItemToObject(j_client, "groups", j_groups); }else{ j_client = cJSON_CreateString(client->username); if(j_client == NULL){ return NULL; } } return j_client; } int dynsec_clients__process_get(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username; struct dynsec__client *client; cJSON *tree, *j_client, *j_data; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "getClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "getClient", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "getClient", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "getClient", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } if(cJSON_AddStringToObject(tree, "command", "getClient") == NULL || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getClient", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } j_client = add_client_to_json(client, true); if(j_client == NULL){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getClient", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(j_data, "client", j_client); cJSON_AddItemToArray(j_responses, tree); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getClient | username=%s", admin_clientid, admin_username, username); return MOSQ_ERR_SUCCESS; } int dynsec_clients__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { bool verbose; struct dynsec__client *client, *client_tmp; cJSON *tree, *j_clients, *j_client, *j_data; int i, count, offset; const char *admin_clientid, *admin_username; json_get_bool(command, "verbose", &verbose, true, false); json_get_int(command, "count", &count, true, -1); json_get_int(command, "offset", &offset, true, 0); tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "listClients", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } if(cJSON_AddStringToObject(tree, "command", "listClients") == NULL || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL || cJSON_AddIntToObject(j_data, "totalCount", (int)HASH_CNT(hh, local_clients)) == NULL || (j_clients = cJSON_AddArrayToObject(j_data, "clients")) == NULL || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "listClients", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } i = 0; HASH_ITER(hh, local_clients, client, client_tmp){ if(i>=offset){ j_client = add_client_to_json(client, verbose); if(j_client == NULL){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "listClients", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToArray(j_clients, j_client); if(count >= 0){ count--; if(count <= 0){ break; } } } i++; } cJSON_AddItemToArray(j_responses, tree); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | listClients | verbose=%s | count=%d | offset=%d", admin_clientid, admin_username, verbose?"true":"false", count, offset); return MOSQ_ERR_SUCCESS; } int dynsec_clients__process_add_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username, *rolename; struct dynsec__client *client; struct dynsec__role *role; int priority; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addClientRole", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addClientRole", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addClientRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addClientRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } json_get_int(command, "priority", &priority, true, -1); client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "addClientRole", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "addClientRole", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } if(dynsec_rolelist__client_add(client, role, priority) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addClientRole", "Internal error", correlation_data); return MOSQ_ERR_UNKNOWN; } dynsec__config_save(); dynsec__command_reply(j_responses, context, "addClientRole", NULL, correlation_data); /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addClientRole | username=%s | rolename=%s | priority=%d", admin_clientid, admin_username, username, rolename, priority); return MOSQ_ERR_SUCCESS; } int dynsec_clients__process_remove_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username, *rolename; struct dynsec__client *client; struct dynsec__role *role; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeClientRole", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeClientRole", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeClientRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeClientRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "removeClientRole", "Client not found", correlation_data); return MOSQ_ERR_SUCCESS; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "removeClientRole", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } dynsec_rolelist__client_remove(client, role); dynsec__config_save(); dynsec__command_reply(j_responses, context, "removeClientRole", NULL, correlation_data); /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeClientRole | username=%s | rolename=%s", admin_clientid, admin_username, username, rolename); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/plugins/dynamic-security/dynamic_security.h000066400000000000000000000262571450213760600243640ustar00rootroot00000000000000#ifndef DYNAMIC_SECURITY_H #define DYNAMIC_SECURITY_H /* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include #include #include "mosquitto.h" #include "password_mosq.h" /* ################################################################ * # * # ACL types * # * ################################################################ */ #define ACL_TYPE_PUB_C_RECV "publishClientReceive" #define ACL_TYPE_PUB_C_SEND "publishClientSend" #define ACL_TYPE_SUB_GENERIC "subscribe" #define ACL_TYPE_SUB_LITERAL "subscribeLiteral" #define ACL_TYPE_SUB_PATTERN "subscribePattern" #define ACL_TYPE_UNSUB_GENERIC "unsubscribe" #define ACL_TYPE_UNSUB_LITERAL "unsubscribeLiteral" #define ACL_TYPE_UNSUB_PATTERN "unsubscribePattern" /* ################################################################ * # * # Error codes * # * ################################################################ */ #define ERR_USER_NOT_FOUND 10000 #define ERR_GROUP_NOT_FOUND 10001 #define ERR_LIST_NOT_FOUND 10002 /* ################################################################ * # * # Datatypes * # * ################################################################ */ struct dynsec__clientlist{ UT_hash_handle hh; struct dynsec__client *client; int priority; }; struct dynsec__grouplist{ UT_hash_handle hh; struct dynsec__group *group; int priority; }; struct dynsec__rolelist{ UT_hash_handle hh; char *rolename; struct dynsec__role *role; int priority; }; struct dynsec__client{ UT_hash_handle hh; struct mosquitto_pw pw; struct dynsec__rolelist *rolelist; struct dynsec__grouplist *grouplist; char *username; char *clientid; char *text_name; char *text_description; bool disabled; }; struct dynsec__group{ UT_hash_handle hh; struct dynsec__rolelist *rolelist; struct dynsec__clientlist *clientlist; char *groupname; char *text_name; char *text_description; }; struct dynsec__acl{ UT_hash_handle hh; char *topic; int priority; bool allow; }; struct dynsec__acls{ struct dynsec__acl *publish_c_send; struct dynsec__acl *publish_c_recv; struct dynsec__acl *subscribe_literal; struct dynsec__acl *subscribe_pattern; struct dynsec__acl *unsubscribe_literal; struct dynsec__acl *unsubscribe_pattern; }; struct dynsec__role{ UT_hash_handle hh; struct dynsec__acls acls; struct dynsec__clientlist *clientlist; struct dynsec__grouplist *grouplist; char *rolename; char *text_name; char *text_description; }; struct dynsec__acl_default_access{ bool publish_c_send; bool publish_c_recv; bool subscribe; bool unsubscribe; }; extern struct dynsec__group *dynsec_anonymous_group; extern struct dynsec__acl_default_access default_access; /* ################################################################ * # * # Plugin Functions * # * ################################################################ */ void dynsec__config_save(void); int dynsec__handle_control(cJSON *j_responses, struct mosquitto *context, cJSON *commands); void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data); /* ################################################################ * # * # ACL Functions * # * ################################################################ */ int dynsec__acl_check_callback(int event, void *event_data, void *userdata); bool sub_acl_check(const char *acl, const char *sub); /* ################################################################ * # * # Auth Functions * # * ################################################################ */ int dynsec_auth__base64_encode(unsigned char *in, int in_len, char **encoded); int dynsec_auth__base64_decode(char *in, unsigned char **decoded, int *decoded_len); int dynsec_auth__pw_hash(struct dynsec__client *client, const char *password, unsigned char *password_hash, int password_hash_len, bool new_password); int dynsec_auth__basic_auth_callback(int event, void *event_data, void *userdata); /* ################################################################ * # * # Client Functions * # * ################################################################ */ void dynsec_clients__cleanup(void); int dynsec_clients__config_load(cJSON *tree); int dynsec_clients__config_save(cJSON *tree); int dynsec_clients__process_add_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_create(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_disable(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_enable(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_get(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_remove_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_set_id(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_clients__process_set_password(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); struct dynsec__client *dynsec_clients__find(const char *username); /* ################################################################ * # * # Client List Functions * # * ################################################################ */ cJSON *dynsec_clientlist__all_to_json(struct dynsec__clientlist *base_clientlist); int dynsec_clientlist__add(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client, int priority); void dynsec_clientlist__cleanup(struct dynsec__clientlist **base_clientlist); void dynsec_clientlist__remove(struct dynsec__clientlist **base_clientlist, struct dynsec__client *client); void dynsec_clientlist__kick_all(struct dynsec__clientlist *base_clientlist); /* ################################################################ * # * # Group Functions * # * ################################################################ */ void dynsec_groups__cleanup(void); int dynsec_groups__config_load(cJSON *tree); int dynsec_groups__add_client(const char *username, const char *groupname, int priority, bool update_config); int dynsec_groups__config_save(cJSON *tree); int dynsec_groups__process_add_client(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_add_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_create(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_get(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_remove_client(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_remove_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_get_anonymous_group(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__process_set_anonymous_group(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_groups__remove_client(const char *username, const char *groupname, bool update_config); struct dynsec__group *dynsec_groups__find(const char *groupname); /* ################################################################ * # * # Group List Functions * # * ################################################################ */ cJSON *dynsec_grouplist__all_to_json(struct dynsec__grouplist *base_grouplist); int dynsec_grouplist__add(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group, int priority); void dynsec_grouplist__cleanup(struct dynsec__grouplist **base_grouplist); void dynsec_grouplist__remove(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group); /* ################################################################ * # * # Role Functions * # * ################################################################ */ void dynsec_roles__cleanup(void); int dynsec_roles__config_load(cJSON *tree); int dynsec_roles__config_save(cJSON *tree); int dynsec_roles__process_add_acl(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_roles__process_create(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_roles__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_roles__process_get(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_roles__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_roles__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); int dynsec_roles__process_remove_acl(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data); struct dynsec__role *dynsec_roles__find(const char *rolename); /* ################################################################ * # * # Role List Functions * # * ################################################################ */ int dynsec_rolelist__client_add(struct dynsec__client *client, struct dynsec__role *role, int priority); int dynsec_rolelist__client_remove(struct dynsec__client *client, struct dynsec__role *role); int dynsec_rolelist__group_add(struct dynsec__group *group, struct dynsec__role *role, int priority); void dynsec_rolelist__group_remove(struct dynsec__group *group, struct dynsec__role *role); int dynsec_rolelist__load_from_json(cJSON *command, struct dynsec__rolelist **rolelist); void dynsec_rolelist__cleanup(struct dynsec__rolelist **base_rolelist); cJSON *dynsec_rolelist__all_to_json(struct dynsec__rolelist *base_rolelist); #endif mosquitto-2.0.18/plugins/dynamic-security/grouplist.c000066400000000000000000000073451450213760600230310ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto.h" #include "mosquitto_broker.h" #include "json_help.h" #include "dynamic_security.h" /* ################################################################ * # * # Plugin global variables * # * ################################################################ */ /* ################################################################ * # * # Function declarations * # * ################################################################ */ /* ################################################################ * # * # Local variables * # * ################################################################ */ /* ################################################################ * # * # Utility functions * # * ################################################################ */ static int dynsec_grouplist__cmp(void *a, void *b) { int prio; struct dynsec__grouplist *grouplist_a = a; struct dynsec__grouplist *grouplist_b = b; prio = grouplist_b->priority - grouplist_a->priority; if(prio == 0){ return strcmp(grouplist_a->group->groupname, grouplist_b->group->groupname); }else{ return prio; } } cJSON *dynsec_grouplist__all_to_json(struct dynsec__grouplist *base_grouplist) { struct dynsec__grouplist *grouplist, *grouplist_tmp; cJSON *j_groups, *j_group; j_groups = cJSON_CreateArray(); if(j_groups == NULL) return NULL; HASH_ITER(hh, base_grouplist, grouplist, grouplist_tmp){ j_group = cJSON_CreateObject(); if(j_group == NULL){ cJSON_Delete(j_groups); return NULL; } cJSON_AddItemToArray(j_groups, j_group); if(cJSON_AddStringToObject(j_group, "groupname", grouplist->group->groupname) == NULL || (grouplist->priority != -1 && cJSON_AddIntToObject(j_group, "priority", grouplist->priority) == NULL) ){ cJSON_Delete(j_groups); return NULL; } } return j_groups; } int dynsec_grouplist__add(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group, int priority) { struct dynsec__grouplist *grouplist; HASH_FIND(hh, *base_grouplist, group->groupname, strlen(group->groupname), grouplist); if(grouplist != NULL){ /* Group is already in the list */ return MOSQ_ERR_SUCCESS; } grouplist = mosquitto_malloc(sizeof(struct dynsec__grouplist)); if(grouplist == NULL){ return MOSQ_ERR_NOMEM; } grouplist->group = group; grouplist->priority = priority; HASH_ADD_KEYPTR_INORDER(hh, *base_grouplist, grouplist->group->groupname, strlen(grouplist->group->groupname), grouplist, dynsec_grouplist__cmp); return MOSQ_ERR_SUCCESS; } void dynsec_grouplist__cleanup(struct dynsec__grouplist **base_grouplist) { struct dynsec__grouplist *grouplist, *grouplist_tmp; HASH_ITER(hh, *base_grouplist, grouplist, grouplist_tmp){ HASH_DELETE(hh, *base_grouplist, grouplist); mosquitto_free(grouplist); } } void dynsec_grouplist__remove(struct dynsec__grouplist **base_grouplist, struct dynsec__group *group) { struct dynsec__grouplist *grouplist; HASH_FIND(hh, *base_grouplist, group->groupname, strlen(group->groupname), grouplist); if(grouplist){ HASH_DELETE(hh, *base_grouplist, grouplist); mosquitto_free(grouplist); } } mosquitto-2.0.18/plugins/dynamic-security/groups.c000066400000000000000000001104411450213760600223100ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto.h" #include "mosquitto_broker.h" #include "json_help.h" #include "dynamic_security.h" /* ################################################################ * # * # Plugin global variables * # * ################################################################ */ struct dynsec__group *dynsec_anonymous_group = NULL; /* ################################################################ * # * # Function declarations * # * ################################################################ */ static int dynsec__remove_all_clients_from_group(struct dynsec__group *group); static int dynsec__remove_all_roles_from_group(struct dynsec__group *group); static cJSON *add_group_to_json(struct dynsec__group *group); /* ################################################################ * # * # Local variables * # * ################################################################ */ static struct dynsec__group *local_groups = NULL; /* ################################################################ * # * # Utility functions * # * ################################################################ */ static void group__kick_all(struct dynsec__group *group) { if(group == dynsec_anonymous_group){ mosquitto_kick_client_by_username(NULL, false); } dynsec_clientlist__kick_all(group->clientlist); } static int group_cmp(void *a, void *b) { struct dynsec__group *group_a = a; struct dynsec__group *group_b = b; return strcmp(group_a->groupname, group_b->groupname); } struct dynsec__group *dynsec_groups__find(const char *groupname) { struct dynsec__group *group = NULL; if(groupname){ HASH_FIND(hh, local_groups, groupname, strlen(groupname), group); } return group; } static void group__free_item(struct dynsec__group *group) { struct dynsec__group *found_group = NULL; if(group == NULL) return; found_group = dynsec_groups__find(group->groupname); if(found_group){ HASH_DEL(local_groups, found_group); } dynsec__remove_all_clients_from_group(group); mosquitto_free(group->text_name); mosquitto_free(group->text_description); mosquitto_free(group->groupname); dynsec_rolelist__cleanup(&group->rolelist); mosquitto_free(group); } int dynsec_groups__process_add_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *groupname, *rolename; struct dynsec__group *group; struct dynsec__role *role; int priority; const char *admin_clientid, *admin_username; int rc; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupRole", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupRole", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } json_get_int(command, "priority", &priority, true, -1); group = dynsec_groups__find(groupname); if(group == NULL){ dynsec__command_reply(j_responses, context, "addGroupRole", "Group not found", correlation_data); return MOSQ_ERR_SUCCESS; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "addGroupRole", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); rc = dynsec_rolelist__group_add(group, role, priority); if(rc == MOSQ_ERR_SUCCESS){ /* Continue */ }else if(rc == MOSQ_ERR_ALREADY_EXISTS){ dynsec__command_reply(j_responses, context, "addGroupRole", "Group is already in this role", correlation_data); return MOSQ_ERR_ALREADY_EXISTS; }else{ dynsec__command_reply(j_responses, context, "addGroupRole", "Internal error", correlation_data); return MOSQ_ERR_UNKNOWN; } mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupRole | groupname=%s | rolename=%s | priority=%d", admin_clientid, admin_username, groupname, rolename, priority); dynsec__config_save(); dynsec__command_reply(j_responses, context, "addGroupRole", NULL, correlation_data); /* Enforce any changes */ group__kick_all(group); return MOSQ_ERR_SUCCESS; } void dynsec_groups__cleanup(void) { struct dynsec__group *group, *group_tmp = NULL; HASH_ITER(hh, local_groups, group, group_tmp){ group__free_item(group); } } /* ################################################################ * # * # Config file load * # * ################################################################ */ int dynsec_groups__config_load(cJSON *tree) { cJSON *j_groups, *j_group; cJSON *j_clientlist, *j_client; cJSON *j_roles, *j_role; struct dynsec__group *group; struct dynsec__role *role; char *groupname; int priority; j_groups = cJSON_GetObjectItem(tree, "groups"); if(j_groups == NULL){ return 0; } if(cJSON_IsArray(j_groups) == false){ return 1; } cJSON_ArrayForEach(j_group, j_groups){ if(cJSON_IsObject(j_group) == true){ /* Group name */ if(json_get_string(j_group, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ continue; } group = dynsec_groups__find(groupname); if(group){ continue; } group = mosquitto_calloc(1, sizeof(struct dynsec__group)); if(group == NULL){ return MOSQ_ERR_NOMEM; } group->groupname = strdup(groupname); if(group->groupname == NULL){ mosquitto_free(group); continue; } /* Text name */ char *textname; if(json_get_string(j_group, "textname", &textname, false) == MOSQ_ERR_SUCCESS){ if(textname){ group->text_name = strdup(textname); if(group->text_name == NULL){ mosquitto_free(group->groupname); mosquitto_free(group); continue; } } } /* Text description */ char *textdescription; if(json_get_string(j_group, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){ if(textdescription){ group->text_description = strdup(textdescription); if(group->text_description == NULL){ mosquitto_free(group->text_name); mosquitto_free(group->groupname); mosquitto_free(group); continue; } } } /* Roles */ j_roles = cJSON_GetObjectItem(j_group, "roles"); if(j_roles && cJSON_IsArray(j_roles)){ cJSON_ArrayForEach(j_role, j_roles){ if(cJSON_IsObject(j_role)){ char *rolename; json_get_string(j_role, "rolename", &rolename, false); if(rolename){ json_get_int(j_role, "priority", &priority, true, -1); role = dynsec_roles__find(rolename); dynsec_rolelist__group_add(group, role, priority); } } } } /* This must go before clients are loaded, otherwise the group won't be found */ HASH_ADD_KEYPTR(hh, local_groups, group->groupname, strlen(group->groupname), group); /* Clients */ j_clientlist = cJSON_GetObjectItem(j_group, "clients"); if(j_clientlist && cJSON_IsArray(j_clientlist)){ cJSON_ArrayForEach(j_client, j_clientlist){ if(cJSON_IsObject(j_client)){ char *username; json_get_string(j_client, "username", &username, false); if(username){ json_get_int(j_client, "priority", &priority, true, -1); dynsec_groups__add_client(username, group->groupname, priority, false); } } } } } } HASH_SORT(local_groups, group_cmp); json_get_string(tree, "anonymousGroup", &groupname, false); if(groupname){ dynsec_anonymous_group = dynsec_groups__find(groupname); } return 0; } /* ################################################################ * # * # Config load and save * # * ################################################################ */ static int dynsec__config_add_groups(cJSON *j_groups) { struct dynsec__group *group, *group_tmp = NULL; cJSON *j_group, *j_clients, *j_roles; HASH_ITER(hh, local_groups, group, group_tmp){ j_group = cJSON_CreateObject(); if(j_group == NULL) return 1; cJSON_AddItemToArray(j_groups, j_group); if(cJSON_AddStringToObject(j_group, "groupname", group->groupname) == NULL || (group->text_name && cJSON_AddStringToObject(j_group, "textname", group->text_name) == NULL) || (group->text_description && cJSON_AddStringToObject(j_group, "textdescription", group->text_description) == NULL) ){ return 1; } j_roles = dynsec_rolelist__all_to_json(group->rolelist); if(j_roles == NULL){ return 1; } cJSON_AddItemToObject(j_group, "roles", j_roles); j_clients = dynsec_clientlist__all_to_json(group->clientlist); if(j_clients == NULL){ return 1; } cJSON_AddItemToObject(j_group, "clients", j_clients); } return 0; } int dynsec_groups__config_save(cJSON *tree) { cJSON *j_groups; j_groups = cJSON_CreateArray(); if(j_groups == NULL){ return 1; } cJSON_AddItemToObject(tree, "groups", j_groups); if(dynsec__config_add_groups(j_groups)){ return 1; } if(dynsec_anonymous_group && cJSON_AddStringToObject(tree, "anonymousGroup", dynsec_anonymous_group->groupname) == NULL){ return 1; } return 0; } int dynsec_groups__process_create(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *groupname, *text_name, *text_description; struct dynsec__group *group = NULL; int rc = MOSQ_ERR_SUCCESS; const char *admin_clientid, *admin_username; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createGroup", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createGroup", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textname", &text_name, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createGroup", "Invalid/missing textname", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textdescription", &text_description, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createGroup", "Invalid/missing textdescription", correlation_data); return MOSQ_ERR_INVAL; } group = dynsec_groups__find(groupname); if(group){ dynsec__command_reply(j_responses, context, "createGroup", "Group already exists", correlation_data); return MOSQ_ERR_SUCCESS; } group = mosquitto_calloc(1, sizeof(struct dynsec__group)); if(group == NULL){ dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } group->groupname = strdup(groupname); if(group->groupname == NULL){ dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data); group__free_item(group); return MOSQ_ERR_NOMEM; } if(text_name){ group->text_name = strdup(text_name); if(group->text_name == NULL){ dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data); group__free_item(group); return MOSQ_ERR_NOMEM; } } if(text_description){ group->text_description = strdup(text_description); if(group->text_description == NULL){ dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data); group__free_item(group); return MOSQ_ERR_NOMEM; } } rc = dynsec_rolelist__load_from_json(command, &group->rolelist); if(rc == MOSQ_ERR_SUCCESS || rc == ERR_LIST_NOT_FOUND){ }else if(rc == MOSQ_ERR_NOT_FOUND){ dynsec__command_reply(j_responses, context, "createGroup", "Role not found", correlation_data); group__free_item(group); return MOSQ_ERR_INVAL; }else{ dynsec__command_reply(j_responses, context, "createGroup", "Internal error", correlation_data); group__free_item(group); return MOSQ_ERR_INVAL; } HASH_ADD_KEYPTR_INORDER(hh, local_groups, group->groupname, strlen(group->groupname), group, group_cmp); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | createGroup | groupname=%s", admin_clientid, admin_username, groupname); dynsec__config_save(); dynsec__command_reply(j_responses, context, "createGroup", NULL, correlation_data); return MOSQ_ERR_SUCCESS; } int dynsec_groups__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *groupname; struct dynsec__group *group; const char *admin_clientid, *admin_username; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "deleteGroup", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "deleteGroup", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } group = dynsec_groups__find(groupname); if(group){ if(group == dynsec_anonymous_group){ dynsec__command_reply(j_responses, context, "deleteGroup", "Deleting the anonymous group is forbidden", correlation_data); return MOSQ_ERR_INVAL; } /* Enforce any changes */ group__kick_all(group); dynsec__remove_all_roles_from_group(group); group__free_item(group); dynsec__config_save(); dynsec__command_reply(j_responses, context, "deleteGroup", NULL, correlation_data); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | deleteGroup | groupname=%s", admin_clientid, admin_username, groupname); return MOSQ_ERR_SUCCESS; }else{ dynsec__command_reply(j_responses, context, "deleteGroup", "Group not found", correlation_data); return MOSQ_ERR_SUCCESS; } } int dynsec_groups__add_client(const char *username, const char *groupname, int priority, bool update_config) { struct dynsec__client *client; struct dynsec__clientlist *clientlist; struct dynsec__group *group; int rc; client = dynsec_clients__find(username); if(client == NULL){ return ERR_USER_NOT_FOUND; } group = dynsec_groups__find(groupname); if(group == NULL){ return ERR_GROUP_NOT_FOUND; } HASH_FIND(hh, group->clientlist, username, strlen(username), clientlist); if(clientlist != NULL){ /* Client is already in the group */ return MOSQ_ERR_ALREADY_EXISTS; } rc = dynsec_clientlist__add(&group->clientlist, client, priority); if(rc){ return rc; } rc = dynsec_grouplist__add(&client->grouplist, group, priority); if(rc){ dynsec_clientlist__remove(&group->clientlist, client); return rc; } if(update_config){ dynsec__config_save(); } return MOSQ_ERR_SUCCESS; } int dynsec_groups__process_add_client(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username, *groupname; int rc; int priority; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupClient", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupClient", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addGroupClient", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } json_get_int(command, "priority", &priority, true, -1); rc = dynsec_groups__add_client(username, groupname, priority, true); if(rc == MOSQ_ERR_SUCCESS){ admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addGroupClient | groupname=%s | username=%s | priority=%d", admin_clientid, admin_username, groupname, username, priority); dynsec__command_reply(j_responses, context, "addGroupClient", NULL, correlation_data); }else if(rc == ERR_USER_NOT_FOUND){ dynsec__command_reply(j_responses, context, "addGroupClient", "Client not found", correlation_data); }else if(rc == ERR_GROUP_NOT_FOUND){ dynsec__command_reply(j_responses, context, "addGroupClient", "Group not found", correlation_data); }else if(rc == MOSQ_ERR_ALREADY_EXISTS){ dynsec__command_reply(j_responses, context, "addGroupClient", "Client is already in this group", correlation_data); }else{ dynsec__command_reply(j_responses, context, "addGroupClient", "Internal error", correlation_data); } /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); return rc; } static int dynsec__remove_all_clients_from_group(struct dynsec__group *group) { struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL; HASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){ /* Remove client stored group reference */ dynsec_grouplist__remove(&clientlist->client->grouplist, group); HASH_DELETE(hh, group->clientlist, clientlist); mosquitto_free(clientlist); } return MOSQ_ERR_SUCCESS; } static int dynsec__remove_all_roles_from_group(struct dynsec__group *group) { struct dynsec__rolelist *rolelist, *rolelist_tmp = NULL; HASH_ITER(hh, group->rolelist, rolelist, rolelist_tmp){ dynsec_rolelist__group_remove(group, rolelist->role); } return MOSQ_ERR_SUCCESS; } int dynsec_groups__remove_client(const char *username, const char *groupname, bool update_config) { struct dynsec__client *client; struct dynsec__group *group; client = dynsec_clients__find(username); if(client == NULL){ return ERR_USER_NOT_FOUND; } group = dynsec_groups__find(groupname); if(group == NULL){ return ERR_GROUP_NOT_FOUND; } dynsec_clientlist__remove(&group->clientlist, client); dynsec_grouplist__remove(&client->grouplist, group); if(update_config){ dynsec__config_save(); } return MOSQ_ERR_SUCCESS; } int dynsec_groups__process_remove_client(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *username, *groupname; int rc; const char *admin_clientid, *admin_username; if(json_get_string(command, "username", &username, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupClient", "Invalid/missing username", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(username, (int)strlen(username)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupClient", "Username not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupClient", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupClient", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } rc = dynsec_groups__remove_client(username, groupname, true); if(rc == MOSQ_ERR_SUCCESS){ admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeGroupClient | groupname=%s | username=%s", admin_clientid, admin_username, groupname, username); dynsec__command_reply(j_responses, context, "removeGroupClient", NULL, correlation_data); }else if(rc == ERR_USER_NOT_FOUND){ dynsec__command_reply(j_responses, context, "removeGroupClient", "Client not found", correlation_data); }else if(rc == ERR_GROUP_NOT_FOUND){ dynsec__command_reply(j_responses, context, "removeGroupClient", "Group not found", correlation_data); }else{ dynsec__command_reply(j_responses, context, "removeGroupClient", "Internal error", correlation_data); } /* Enforce any changes */ mosquitto_kick_client_by_username(username, false); return rc; } static cJSON *add_group_to_json(struct dynsec__group *group) { cJSON *j_group, *jtmp, *j_clientlist, *j_client, *j_rolelist; struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL; j_group = cJSON_CreateObject(); if(j_group == NULL){ return NULL; } if(cJSON_AddStringToObject(j_group, "groupname", group->groupname) == NULL || (group->text_name && cJSON_AddStringToObject(j_group, "textname", group->text_name) == NULL) || (group->text_description && cJSON_AddStringToObject(j_group, "textdescription", group->text_description) == NULL) || (j_clientlist = cJSON_AddArrayToObject(j_group, "clients")) == NULL ){ cJSON_Delete(j_group); return NULL; } HASH_ITER(hh, group->clientlist, clientlist, clientlist_tmp){ j_client = cJSON_CreateObject(); if(j_client == NULL){ cJSON_Delete(j_group); return NULL; } cJSON_AddItemToArray(j_clientlist, j_client); jtmp = cJSON_CreateStringReference(clientlist->client->username); if(jtmp == NULL){ cJSON_Delete(j_group); return NULL; } cJSON_AddItemToObject(j_client, "username", jtmp); } j_rolelist = dynsec_rolelist__all_to_json(group->rolelist); if(j_rolelist == NULL){ cJSON_Delete(j_group); return NULL; } cJSON_AddItemToObject(j_group, "roles", j_rolelist); return j_group; } int dynsec_groups__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { bool verbose; cJSON *tree, *j_groups, *j_group, *j_data; struct dynsec__group *group, *group_tmp = NULL; int i, count, offset; const char *admin_clientid, *admin_username; json_get_bool(command, "verbose", &verbose, true, false); json_get_int(command, "count", &count, true, -1); json_get_int(command, "offset", &offset, true, 0); tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } if(cJSON_AddStringToObject(tree, "command", "listGroups") == NULL || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL || cJSON_AddIntToObject(j_data, "totalCount", (int)HASH_CNT(hh, local_groups)) == NULL || (j_groups = cJSON_AddArrayToObject(j_data, "groups")) == NULL || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } i = 0; HASH_ITER(hh, local_groups, group, group_tmp){ if(i>=offset){ if(verbose){ j_group = add_group_to_json(group); if(j_group == NULL){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToArray(j_groups, j_group); }else{ j_group = cJSON_CreateString(group->groupname); if(j_group){ cJSON_AddItemToArray(j_groups, j_group); }else{ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "listGroups", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } } if(count >= 0){ count--; if(count <= 0){ break; } } } i++; } cJSON_AddItemToArray(j_responses, tree); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | listGroups | verbose=%s | count=%d | offset=%d", admin_clientid, admin_username, verbose?"true":"false", count, offset); return MOSQ_ERR_SUCCESS; } int dynsec_groups__process_get(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *groupname; cJSON *tree, *j_group, *j_data; struct dynsec__group *group; const char *admin_clientid, *admin_username; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "getGroup", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "getGroup", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "getGroup", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } if(cJSON_AddStringToObject(tree, "command", "getGroup") == NULL || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getGroup", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } group = dynsec_groups__find(groupname); if(group){ j_group = add_group_to_json(group); if(j_group == NULL){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getGroup", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(j_data, "group", j_group); }else{ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getGroup", "Group not found", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToArray(j_responses, tree); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getGroup | groupname=%s", admin_clientid, admin_username, groupname); return MOSQ_ERR_SUCCESS; } int dynsec_groups__process_remove_role(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *groupname, *rolename; struct dynsec__group *group; struct dynsec__role *role; const char *admin_clientid, *admin_username; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupRole", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupRole", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeGroupRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } group = dynsec_groups__find(groupname); if(group == NULL){ dynsec__command_reply(j_responses, context, "removeGroupRole", "Group not found", correlation_data); return MOSQ_ERR_SUCCESS; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "removeGroupRole", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } dynsec_rolelist__group_remove(group, role); dynsec__config_save(); dynsec__command_reply(j_responses, context, "removeGroupRole", NULL, correlation_data); /* Enforce any changes */ group__kick_all(group); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeGroupRole | groupname=%s | rolename=%s", admin_clientid, admin_username, groupname, rolename); return MOSQ_ERR_SUCCESS; } int dynsec_groups__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *groupname = NULL; char *text_name = NULL, *text_description = NULL; struct dynsec__client *client = NULL; struct dynsec__group *group = NULL; struct dynsec__rolelist *rolelist = NULL; bool have_text_name = false, have_text_description = false, have_rolelist = false; int rc; int priority; cJSON *j_client, *j_clients; char *username; char *textname; char *textdescription; const char *admin_clientid, *admin_username; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "modifyGroup", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "modifyGroup", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } group = dynsec_groups__find(groupname); if(group == NULL){ dynsec__command_reply(j_responses, context, "modifyGroup", "Group not found", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textname", &textname, false) == MOSQ_ERR_SUCCESS){ have_text_name = true; text_name = mosquitto_strdup(textname); if(text_name == NULL){ dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } } if(json_get_string(command, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){ have_text_description = true; text_description = mosquitto_strdup(textdescription); if(text_description == NULL){ dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } } rc = dynsec_rolelist__load_from_json(command, &rolelist); if(rc == MOSQ_ERR_SUCCESS){ /* Apply changes below */ have_rolelist = true; }else if(rc == ERR_LIST_NOT_FOUND){ /* There was no list in the JSON, so no modification */ rolelist = NULL; }else if(rc == MOSQ_ERR_NOT_FOUND){ dynsec__command_reply(j_responses, context, "modifyGroup", "Role not found", correlation_data); rc = MOSQ_ERR_INVAL; goto error; }else{ if(rc == MOSQ_ERR_INVAL){ dynsec__command_reply(j_responses, context, "modifyGroup", "'roles' not an array or missing/invalid rolename", correlation_data); }else{ dynsec__command_reply(j_responses, context, "modifyGroup", "Internal error", correlation_data); } rc = MOSQ_ERR_INVAL; goto error; } j_clients = cJSON_GetObjectItem(command, "clients"); if(j_clients && cJSON_IsArray(j_clients)){ /* Iterate over array to check clients are valid before proceeding */ cJSON_ArrayForEach(j_client, j_clients){ if(cJSON_IsObject(j_client)){ json_get_string(j_client, "username", &username, false); if(username){ client = dynsec_clients__find(username); if(client == NULL){ dynsec__command_reply(j_responses, context, "modifyGroup", "'clients' contains an object with a 'username' that does not exist", correlation_data); rc = MOSQ_ERR_INVAL; goto error; } }else{ dynsec__command_reply(j_responses, context, "modifyGroup", "'clients' contains an object with an invalid 'username'", correlation_data); rc = MOSQ_ERR_INVAL; goto error; } } } /* Kick all clients in the *current* group */ group__kick_all(group); dynsec__remove_all_clients_from_group(group); /* Now we can add the new clients to the group */ cJSON_ArrayForEach(j_client, j_clients){ if(cJSON_IsObject(j_client)){ json_get_string(j_client, "username", &username, false); if(username){ json_get_int(j_client, "priority", &priority, true, -1); dynsec_groups__add_client(username, groupname, priority, false); } } } } /* Apply remaining changes to group, note that user changes are already applied */ if(have_text_name){ mosquitto_free(group->text_name); group->text_name = text_name; } if(have_text_description){ mosquitto_free(group->text_description); group->text_description = text_description; } if(have_rolelist){ dynsec_rolelist__cleanup(&group->rolelist); group->rolelist = rolelist; } /* And save */ dynsec__config_save(); dynsec__command_reply(j_responses, context, "modifyGroup", NULL, correlation_data); /* Enforce any changes - kick any clients in the *new* group */ group__kick_all(group); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyGroup | groupname=%s", admin_clientid, admin_username, groupname); return MOSQ_ERR_SUCCESS; error: mosquitto_free(text_name); mosquitto_free(text_description); dynsec_rolelist__cleanup(&rolelist); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyGroup | groupname=%s", admin_clientid, admin_username, groupname); return rc; } int dynsec_groups__process_set_anonymous_group(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *groupname; struct dynsec__group *group = NULL; const char *admin_clientid, *admin_username; if(json_get_string(command, "groupname", &groupname, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setAnonymousGroup", "Invalid/missing groupname", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(groupname, (int)strlen(groupname)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "setAnonymousGroup", "Group name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } group = dynsec_groups__find(groupname); if(group == NULL){ dynsec__command_reply(j_responses, context, "setAnonymousGroup", "Group not found", correlation_data); return MOSQ_ERR_SUCCESS; } dynsec_anonymous_group = group; dynsec__config_save(); dynsec__command_reply(j_responses, context, "setAnonymousGroup", NULL, correlation_data); /* Enforce any changes */ mosquitto_kick_client_by_username(NULL, false); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | setAnonymousGroup | groupname=%s", admin_clientid, admin_username, groupname); return MOSQ_ERR_SUCCESS; } int dynsec_groups__process_get_anonymous_group(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { cJSON *tree, *j_data, *j_group; const char *groupname; const char *admin_clientid, *admin_username; UNUSED(command); tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "getAnonymousGroup", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } if(dynsec_anonymous_group){ groupname = dynsec_anonymous_group->groupname; }else{ groupname = ""; } if(cJSON_AddStringToObject(tree, "command", "getAnonymousGroup") == NULL || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL || (j_group = cJSON_AddObjectToObject(j_data, "group")) == NULL || cJSON_AddStringToObject(j_group, "groupname", groupname) == NULL || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getAnonymousGroup", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToArray(j_responses, tree); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getAnonymousGroup", admin_clientid, admin_username); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/plugins/dynamic-security/json_help.c000066400000000000000000000041001450213760600227440ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include "json_help.h" #include "mosquitto.h" int json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value) { cJSON *jtmp; if(optional == true){ *value = default_value; } jtmp = cJSON_GetObjectItem(json, name); if(jtmp){ if(cJSON_IsBool(jtmp) == false){ return MOSQ_ERR_INVAL; } *value = cJSON_IsTrue(jtmp); }else{ if(optional == false){ return MOSQ_ERR_INVAL; } } return MOSQ_ERR_SUCCESS; } int json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value) { cJSON *jtmp; if(optional == true){ *value = default_value; } jtmp = cJSON_GetObjectItem(json, name); if(jtmp){ if(cJSON_IsNumber(jtmp) == false){ return MOSQ_ERR_INVAL; } *value = jtmp->valueint; }else{ if(optional == false){ return MOSQ_ERR_INVAL; } } return MOSQ_ERR_SUCCESS; } int json_get_string(cJSON *json, const char *name, char **value, bool optional) { cJSON *jtmp; *value = NULL; jtmp = cJSON_GetObjectItem(json, name); if(jtmp){ if(cJSON_IsString(jtmp) == false){ return MOSQ_ERR_INVAL; } *value = jtmp->valuestring; }else{ if(optional == false){ return MOSQ_ERR_INVAL; } } return MOSQ_ERR_SUCCESS; } cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number) { char buf[30]; snprintf(buf, sizeof(buf), "%d", number); return cJSON_AddRawToObject(object, name, buf); } mosquitto-2.0.18/plugins/dynamic-security/json_help.h000066400000000000000000000022251450213760600227570ustar00rootroot00000000000000#ifndef JSON_HELP_H #define JSON_HELP_H /* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include #include /* "optional==false" can also be taken to mean "only return success if the key exists and is valid" */ int json_get_bool(cJSON *json, const char *name, bool *value, bool optional, bool default_value); int json_get_int(cJSON *json, const char *name, int *value, bool optional, int default_value); int json_get_string(cJSON *json, const char *name, char **value, bool optional); cJSON *cJSON_AddIntToObject(cJSON * const object, const char * const name, int number); cJSON *cJSON_CreateInt(int num); #endif mosquitto-2.0.18/plugins/dynamic-security/plugin.c000066400000000000000000000611511450213760600222720ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #ifndef WIN32 # include #endif #include "json_help.h" #include "mosquitto.h" #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mqtt_protocol.h" #include "dynamic_security.h" static mosquitto_plugin_id_t *plg_id = NULL; static char *config_file = NULL; struct dynsec__acl_default_access default_access = {false, false, false, false}; #ifdef WIN32 # include # include # include # include # include # define PATH_MAX MAX_PATH #else # include # include # include # include #endif /* Temporary - remove in 2.1 */ FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read) { #ifdef WIN32 char buf[4096]; int rc; int flags = 0; rc = ExpandEnvironmentStringsA(path, buf, 4096); if(rc == 0 || rc > 4096){ return NULL; }else{ if (restrict_read) { HANDLE hfile; SECURITY_ATTRIBUTES sec; EXPLICIT_ACCESS_A ea; PACL pacl = NULL; char username[UNLEN + 1]; DWORD ulen = UNLEN; SECURITY_DESCRIPTOR sd; DWORD dwCreationDisposition; int fd; FILE *fptr; switch(mode[0]){ case 'a': dwCreationDisposition = OPEN_ALWAYS; flags = _O_APPEND; break; case 'r': dwCreationDisposition = OPEN_EXISTING; flags = _O_RDONLY; break; case 'w': dwCreationDisposition = CREATE_ALWAYS; break; default: return NULL; } GetUserNameA(username, &ulen); if (!InitializeSecurityDescriptor(&sd, SECURITY_DESCRIPTOR_REVISION)) { return NULL; } BuildExplicitAccessWithNameA(&ea, username, GENERIC_ALL, SET_ACCESS, NO_INHERITANCE); if (SetEntriesInAclA(1, &ea, NULL, &pacl) != ERROR_SUCCESS) { return NULL; } if (!SetSecurityDescriptorDacl(&sd, TRUE, pacl, FALSE)) { LocalFree(pacl); return NULL; } memset(&sec, 0, sizeof(sec)); sec.nLength = sizeof(SECURITY_ATTRIBUTES); sec.bInheritHandle = FALSE; sec.lpSecurityDescriptor = &sd; hfile = CreateFileA(buf, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, &sec, dwCreationDisposition, FILE_ATTRIBUTE_NORMAL, NULL); LocalFree(pacl); fd = _open_osfhandle((intptr_t)hfile, flags); if (fd < 0) { return NULL; } fptr = _fdopen(fd, mode); if (!fptr) { _close(fd); return NULL; } if(mode[0] == 'a'){ fseek(fptr, 0, SEEK_END); } return fptr; }else { return fopen(buf, mode); } } #else FILE *fptr; struct stat statbuf; if (restrict_read) { mode_t old_mask; old_mask = umask(0077); fptr = fopen(path, mode); umask(old_mask); }else{ fptr = fopen(path, mode); } if(!fptr) return NULL; if(fstat(fileno(fptr), &statbuf) < 0){ fclose(fptr); return NULL; } if(restrict_read){ if(statbuf.st_mode & S_IRWXO){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_WARNING, #else fprintf(stderr, #endif "Warning: File %s has world readable permissions. Future versions will refuse to load this file." "To fix this, use `chmod 0700 %s`.", path, path); #if 0 return NULL; #endif } if(statbuf.st_uid != getuid()){ char buf[4096]; struct passwd pw, *result; getpwuid_r(getuid(), &pw, buf, sizeof(buf), &result); if(result){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_WARNING, #else fprintf(stderr, #endif "Warning: File %s owner is not %s. Future versions will refuse to load this file." "To fix this, use `chown %s %s`.", path, result->pw_name, result->pw_name, path); } #if 0 // Future version return NULL; #endif } if(statbuf.st_gid != getgid()){ char buf[4096]; struct group grp, *result; getgrgid_r(getgid(), &grp, buf, sizeof(buf), &result); if(result){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_WARNING, #else fprintf(stderr, #endif "Warning: File %s group is not %s. Future versions will refuse to load this file.", path, result->gr_name); } #if 0 // Future version return NULL #endif } } if(!S_ISREG(statbuf.st_mode) && !S_ISLNK(statbuf.st_mode)){ #ifdef WITH_BROKER log__printf(NULL, MOSQ_LOG_ERR, "Error: %s is not a file.", path); #endif fclose(fptr); return NULL; } return fptr; #endif } void dynsec__command_reply(cJSON *j_responses, struct mosquitto *context, const char *command, const char *error, const char *correlation_data) { cJSON *j_response; UNUSED(context); j_response = cJSON_CreateObject(); if(j_response == NULL) return; if(cJSON_AddStringToObject(j_response, "command", command) == NULL || (error && cJSON_AddStringToObject(j_response, "error", error) == NULL) || (correlation_data && cJSON_AddStringToObject(j_response, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(j_response); return; } cJSON_AddItemToArray(j_responses, j_response); } static void send_response(cJSON *tree) { char *payload; size_t payload_len; payload = cJSON_PrintUnformatted(tree); cJSON_Delete(tree); if(payload == NULL) return; payload_len = strlen(payload); if(payload_len > MQTT_MAX_PAYLOAD){ free(payload); return; } mosquitto_broker_publish(NULL, "$CONTROL/dynamic-security/v1/response", (int)payload_len, payload, 0, 0, NULL); } static int dynsec_control_callback(int event, void *event_data, void *userdata) { struct mosquitto_evt_control *ed = event_data; cJSON *tree, *commands; cJSON *j_response_tree, *j_responses; UNUSED(event); UNUSED(userdata); /* Create object for responses */ j_response_tree = cJSON_CreateObject(); if(j_response_tree == NULL){ return MOSQ_ERR_NOMEM; } j_responses = cJSON_CreateArray(); if(j_responses == NULL){ cJSON_Delete(j_response_tree); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(j_response_tree, "responses", j_responses); /* Parse cJSON tree. * Using cJSON_ParseWithLength() is the best choice here, but Mosquitto * always adds an extra 0 to the end of the payload memory, so using * cJSON_Parse() on its own will still not overrun. */ #if CJSON_VERSION_FULL < 1007013 tree = cJSON_Parse(ed->payload); #else tree = cJSON_ParseWithLength(ed->payload, ed->payloadlen); #endif if(tree == NULL){ dynsec__command_reply(j_responses, ed->client, "Unknown command", "Payload not valid JSON", NULL); send_response(j_response_tree); return MOSQ_ERR_SUCCESS; } commands = cJSON_GetObjectItem(tree, "commands"); if(commands == NULL || !cJSON_IsArray(commands)){ cJSON_Delete(tree); dynsec__command_reply(j_responses, ed->client, "Unknown command", "Invalid/missing commands", NULL); send_response(j_response_tree); return MOSQ_ERR_SUCCESS; } /* Handle commands */ dynsec__handle_control(j_responses, ed->client, commands); cJSON_Delete(tree); send_response(j_response_tree); return MOSQ_ERR_SUCCESS; } static int dynsec__process_set_default_acl_access(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { cJSON *j_actions, *j_action; const char *admin_clientid, *admin_username; j_actions = cJSON_GetObjectItem(command, "acls"); if(j_actions == NULL || !cJSON_IsArray(j_actions)){ dynsec__command_reply(j_responses, context, "setDefaultACLAccess", "Missing/invalid actions array", correlation_data); return MOSQ_ERR_INVAL; } admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); cJSON_ArrayForEach(j_action, j_actions){ char *acltype; bool allow; if(json_get_string(j_action, "acltype", &acltype, false) == MOSQ_ERR_SUCCESS && json_get_bool(j_action, "allow", &allow, false, false) == MOSQ_ERR_SUCCESS){ if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ default_access.publish_c_send = allow; }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ default_access.publish_c_recv = allow; }else if(!strcasecmp(acltype, ACL_TYPE_SUB_GENERIC)){ default_access.subscribe = allow; }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_GENERIC)){ default_access.unsubscribe = allow; } mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | setDefaultACLAccess | acltype=%s | allow=%s", admin_clientid, admin_username, acltype, allow?"true":"false"); } } dynsec__config_save(); dynsec__command_reply(j_responses, context, "setDefaultACLAccess", NULL, correlation_data); return MOSQ_ERR_SUCCESS; } static int dynsec__process_get_default_acl_access(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { cJSON *tree, *jtmp, *j_data, *j_acls, *j_acl; const char *admin_clientid, *admin_username; UNUSED(command); tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "getDefaultACLAccess", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | getDefaultACLAccess", admin_clientid, admin_username); if(cJSON_AddStringToObject(tree, "command", "getDefaultACLAccess") == NULL || ((j_data = cJSON_AddObjectToObject(tree, "data")) == NULL) ){ goto internal_error; } j_acls = cJSON_AddArrayToObject(j_data, "acls"); if(j_acls == NULL){ goto internal_error; } /* publishClientSend */ j_acl = cJSON_CreateObject(); if(j_acl == NULL){ goto internal_error; } cJSON_AddItemToArray(j_acls, j_acl); if(cJSON_AddStringToObject(j_acl, "acltype", ACL_TYPE_PUB_C_SEND) == NULL || cJSON_AddBoolToObject(j_acl, "allow", default_access.publish_c_send) == NULL ){ goto internal_error; } /* publishClientReceive */ j_acl = cJSON_CreateObject(); if(j_acl == NULL){ goto internal_error; } cJSON_AddItemToArray(j_acls, j_acl); if(cJSON_AddStringToObject(j_acl, "acltype", ACL_TYPE_PUB_C_RECV) == NULL || cJSON_AddBoolToObject(j_acl, "allow", default_access.publish_c_recv) == NULL ){ goto internal_error; } /* subscribe */ j_acl = cJSON_CreateObject(); if(j_acl == NULL){ goto internal_error; } cJSON_AddItemToArray(j_acls, j_acl); if(cJSON_AddStringToObject(j_acl, "acltype", ACL_TYPE_SUB_GENERIC) == NULL || cJSON_AddBoolToObject(j_acl, "allow", default_access.subscribe) == NULL ){ goto internal_error; } /* unsubscribe */ j_acl = cJSON_CreateObject(); if(j_acl == NULL){ goto internal_error; } cJSON_AddItemToArray(j_acls, j_acl); if(cJSON_AddStringToObject(j_acl, "acltype", ACL_TYPE_UNSUB_GENERIC) == NULL || cJSON_AddBoolToObject(j_acl, "allow", default_access.unsubscribe) == NULL ){ goto internal_error; } cJSON_AddItemToArray(j_responses, tree); if(correlation_data){ jtmp = cJSON_AddStringToObject(tree, "correlationData", correlation_data); if(jtmp == NULL){ goto internal_error; } } return MOSQ_ERR_SUCCESS; internal_error: cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getDefaultACLAccess", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { int i; for(i=0; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include "dynamic_security.h" #include "json_help.h" #include "mosquitto.h" #include "mosquitto_broker.h" /* ################################################################ * # * # Utility functions * # * ################################################################ */ static int rolelist_cmp(void *a, void *b) { int prio; struct dynsec__rolelist *rolelist_a = a; struct dynsec__rolelist *rolelist_b = b; prio = rolelist_b->priority - rolelist_a->priority; if(prio == 0){ return strcmp(rolelist_a->rolename, rolelist_b->rolename); }else{ return prio; } } static void dynsec_rolelist__free_item(struct dynsec__rolelist **base_rolelist, struct dynsec__rolelist *rolelist) { HASH_DELETE(hh, *base_rolelist, rolelist); mosquitto_free(rolelist->rolename); mosquitto_free(rolelist); } void dynsec_rolelist__cleanup(struct dynsec__rolelist **base_rolelist) { struct dynsec__rolelist *rolelist, *rolelist_tmp; HASH_ITER(hh, *base_rolelist, rolelist, rolelist_tmp){ dynsec_rolelist__free_item(base_rolelist, rolelist); } } static int dynsec_rolelist__remove_role(struct dynsec__rolelist **base_rolelist, const struct dynsec__role *role) { struct dynsec__rolelist *found_rolelist; HASH_FIND(hh, *base_rolelist, role->rolename, strlen(role->rolename), found_rolelist); if(found_rolelist){ dynsec_rolelist__free_item(base_rolelist, found_rolelist); return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOT_FOUND; } } int dynsec_rolelist__client_remove(struct dynsec__client *client, struct dynsec__role *role) { int rc; struct dynsec__clientlist *found_clientlist; rc = dynsec_rolelist__remove_role(&client->rolelist, role); if(rc) return rc; HASH_FIND(hh, role->clientlist, client->username, strlen(client->username), found_clientlist); if(found_clientlist){ HASH_DELETE(hh, role->clientlist, found_clientlist); mosquitto_free(found_clientlist); return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOT_FOUND; } } void dynsec_rolelist__group_remove(struct dynsec__group *group, struct dynsec__role *role) { dynsec_rolelist__remove_role(&group->rolelist, role); dynsec_grouplist__remove(&role->grouplist, group); } static int dynsec_rolelist__add(struct dynsec__rolelist **base_rolelist, struct dynsec__role *role, int priority) { struct dynsec__rolelist *rolelist; if(role == NULL) return MOSQ_ERR_INVAL; HASH_FIND(hh, *base_rolelist, role->rolename, strlen(role->rolename), rolelist); if(rolelist){ return MOSQ_ERR_ALREADY_EXISTS; }else{ rolelist = mosquitto_calloc(1, sizeof(struct dynsec__rolelist)); if(rolelist == NULL) return MOSQ_ERR_NOMEM; rolelist->role = role; rolelist->priority = priority; rolelist->rolename = mosquitto_strdup(role->rolename); if(rolelist->rolename == NULL){ mosquitto_free(rolelist); return MOSQ_ERR_NOMEM; } HASH_ADD_KEYPTR_INORDER(hh, *base_rolelist, role->rolename, strlen(role->rolename), rolelist, rolelist_cmp); return MOSQ_ERR_SUCCESS; } } int dynsec_rolelist__client_add(struct dynsec__client *client, struct dynsec__role *role, int priority) { struct dynsec__rolelist *rolelist; int rc; rc = dynsec_rolelist__add(&client->rolelist, role, priority); if(rc) return rc; HASH_FIND(hh, client->rolelist, role->rolename, strlen(role->rolename), rolelist); if(rolelist == NULL){ /* This should never happen because the above add_role succeeded. */ return MOSQ_ERR_UNKNOWN; } return dynsec_clientlist__add(&role->clientlist, client, priority); } int dynsec_rolelist__group_add(struct dynsec__group *group, struct dynsec__role *role, int priority) { int rc; rc = dynsec_rolelist__add(&group->rolelist, role, priority); if(rc) return rc; return dynsec_grouplist__add(&role->grouplist, group, priority); } int dynsec_rolelist__load_from_json(cJSON *command, struct dynsec__rolelist **rolelist) { cJSON *j_roles, *j_role; int priority; struct dynsec__role *role; j_roles = cJSON_GetObjectItem(command, "roles"); if(j_roles){ if(cJSON_IsArray(j_roles)){ cJSON_ArrayForEach(j_role, j_roles){ char *rolename; json_get_string(j_role, "rolename", &rolename, false); if(rolename){ json_get_int(j_role, "priority", &priority, true, -1); role = dynsec_roles__find(rolename); if(role){ dynsec_rolelist__add(rolelist, role, priority); }else{ dynsec_rolelist__cleanup(rolelist); return MOSQ_ERR_NOT_FOUND; } }else{ return MOSQ_ERR_INVAL; } } return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_INVAL; } }else{ return ERR_LIST_NOT_FOUND; } } cJSON *dynsec_rolelist__all_to_json(struct dynsec__rolelist *base_rolelist) { struct dynsec__rolelist *rolelist, *rolelist_tmp; cJSON *j_roles, *j_role; j_roles = cJSON_CreateArray(); if(j_roles == NULL) return NULL; HASH_ITER(hh, base_rolelist, rolelist, rolelist_tmp){ j_role = cJSON_CreateObject(); if(j_role == NULL){ cJSON_Delete(j_roles); return NULL; } cJSON_AddItemToArray(j_roles, j_role); if(cJSON_AddStringToObject(j_role, "rolename", rolelist->role->rolename) == NULL || (rolelist->priority != -1 && cJSON_AddIntToObject(j_role, "priority", rolelist->priority) == NULL) ){ cJSON_Delete(j_roles); return NULL; } } return j_roles; } mosquitto-2.0.18/plugins/dynamic-security/roles.c000066400000000000000000000722521450213760600221240ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #ifndef WIN32 # include #endif #include "dynamic_security.h" #include "json_help.h" #include "mosquitto.h" #include "mosquitto_broker.h" static cJSON *add_role_to_json(struct dynsec__role *role, bool verbose); static void role__remove_all_clients(struct dynsec__role *role); /* ################################################################ * # * # Local variables * # * ################################################################ */ static struct dynsec__role *local_roles = NULL; /* ################################################################ * # * # Utility functions * # * ################################################################ */ static int role_cmp(void *a, void *b) { struct dynsec__role *role_a = a; struct dynsec__role *role_b = b; return strcmp(role_a->rolename, role_b->rolename); } static void role__free_acl(struct dynsec__acl **acl, struct dynsec__acl *item) { HASH_DELETE(hh, *acl, item); mosquitto_free(item->topic); mosquitto_free(item); } static void role__free_all_acls(struct dynsec__acl **acl) { struct dynsec__acl *iter, *tmp = NULL; HASH_ITER(hh, *acl, iter, tmp){ role__free_acl(acl, iter); } } static void role__free_item(struct dynsec__role *role, bool remove_from_hash) { if(remove_from_hash){ HASH_DEL(local_roles, role); } dynsec_clientlist__cleanup(&role->clientlist); dynsec_grouplist__cleanup(&role->grouplist); mosquitto_free(role->text_name); mosquitto_free(role->text_description); mosquitto_free(role->rolename); role__free_all_acls(&role->acls.publish_c_send); role__free_all_acls(&role->acls.publish_c_recv); role__free_all_acls(&role->acls.subscribe_literal); role__free_all_acls(&role->acls.subscribe_pattern); role__free_all_acls(&role->acls.unsubscribe_literal); role__free_all_acls(&role->acls.unsubscribe_pattern); mosquitto_free(role); } struct dynsec__role *dynsec_roles__find(const char *rolename) { struct dynsec__role *role = NULL; if(rolename){ HASH_FIND(hh, local_roles, rolename, strlen(rolename), role); } return role; } void dynsec_roles__cleanup(void) { struct dynsec__role *role, *role_tmp = NULL; HASH_ITER(hh, local_roles, role, role_tmp){ role__free_item(role, true); } } static void role__kick_all(struct dynsec__role *role) { struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL; dynsec_clientlist__kick_all(role->clientlist); HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){ if(grouplist->group == dynsec_anonymous_group){ mosquitto_kick_client_by_username(NULL, false); } dynsec_clientlist__kick_all(grouplist->group->clientlist); } } /* ################################################################ * # * # Config file load and save * # * ################################################################ */ static int add_single_acl_to_json(cJSON *j_array, const char *acl_type, struct dynsec__acl *acl) { struct dynsec__acl *iter, *tmp = NULL; cJSON *j_acl; HASH_ITER(hh, acl, iter, tmp){ j_acl = cJSON_CreateObject(); if(j_acl == NULL){ return 1; } cJSON_AddItemToArray(j_array, j_acl); if(cJSON_AddStringToObject(j_acl, "acltype", acl_type) == NULL || cJSON_AddStringToObject(j_acl, "topic", iter->topic) == NULL || cJSON_AddIntToObject(j_acl, "priority", iter->priority) == NULL || cJSON_AddBoolToObject(j_acl, "allow", iter->allow) == NULL ){ return 1; } } return 0; } static int add_acls_to_json(cJSON *j_role, struct dynsec__role *role) { cJSON *j_acls; if((j_acls = cJSON_AddArrayToObject(j_role, "acls")) == NULL){ return 1; } if(add_single_acl_to_json(j_acls, ACL_TYPE_PUB_C_SEND, role->acls.publish_c_send) != MOSQ_ERR_SUCCESS || add_single_acl_to_json(j_acls, ACL_TYPE_PUB_C_RECV, role->acls.publish_c_recv) != MOSQ_ERR_SUCCESS || add_single_acl_to_json(j_acls, ACL_TYPE_SUB_LITERAL, role->acls.subscribe_literal) != MOSQ_ERR_SUCCESS || add_single_acl_to_json(j_acls, ACL_TYPE_SUB_PATTERN, role->acls.subscribe_pattern) != MOSQ_ERR_SUCCESS || add_single_acl_to_json(j_acls, ACL_TYPE_UNSUB_LITERAL, role->acls.unsubscribe_literal) != MOSQ_ERR_SUCCESS || add_single_acl_to_json(j_acls, ACL_TYPE_UNSUB_PATTERN, role->acls.unsubscribe_pattern) != MOSQ_ERR_SUCCESS ){ return 1; } return 0; } int dynsec_roles__config_save(cJSON *tree) { cJSON *j_roles, *j_role; struct dynsec__role *role, *role_tmp = NULL; if((j_roles = cJSON_AddArrayToObject(tree, "roles")) == NULL){ return 1; } HASH_ITER(hh, local_roles, role, role_tmp){ j_role = add_role_to_json(role, true); if(j_role == NULL){ return 1; } cJSON_AddItemToArray(j_roles, j_role); } return 0; } static int insert_acl_cmp(struct dynsec__acl *a, struct dynsec__acl *b) { return b->priority - a->priority; } static int dynsec_roles__acl_load(cJSON *j_acls, const char *key, struct dynsec__acl **acllist) { cJSON *j_acl; struct dynsec__acl *acl; cJSON_ArrayForEach(j_acl, j_acls){ char *acltype; char *topic; json_get_string(j_acl, "acltype", &acltype, false); json_get_string(j_acl, "topic", &topic, false); if(!acltype || strcasecmp(acltype, key) != 0 || !topic){ continue; } HASH_FIND(hh, *acllist, topic, strlen(topic), acl); if(acl){ continue; } acl = mosquitto_calloc(1, sizeof(struct dynsec__acl)); if(acl == NULL){ return 1; } json_get_int(j_acl, "priority", &acl->priority, true, 0); json_get_bool(j_acl, "allow", &acl->allow, true, false); bool allow; if(json_get_bool(j_acl, "allow", &allow, false, false) == MOSQ_ERR_SUCCESS){ acl->allow = allow; } acl->topic = mosquitto_strdup(topic); if(acl->topic == NULL){ mosquitto_free(acl); continue; } HASH_ADD_KEYPTR_INORDER(hh, *acllist, acl->topic, strlen(acl->topic), acl, insert_acl_cmp); } return 0; } int dynsec_roles__config_load(cJSON *tree) { cJSON *j_roles, *j_role, *j_acls; struct dynsec__role *role; j_roles = cJSON_GetObjectItem(tree, "roles"); if(j_roles == NULL){ return 0; } if(cJSON_IsArray(j_roles) == false){ return 1; } cJSON_ArrayForEach(j_role, j_roles){ if(cJSON_IsObject(j_role) == true){ /* Role name */ char *rolename; if(json_get_string(j_role, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ continue; } role = dynsec_roles__find(rolename); if(role){ continue; } role = mosquitto_calloc(1, sizeof(struct dynsec__role)); if(role == NULL){ return MOSQ_ERR_NOMEM; } role->rolename = mosquitto_strdup(rolename); if(role->rolename == NULL){ mosquitto_free(role); continue; } /* Text name */ char *textname; if(json_get_string(j_role, "textname", &textname, false) == MOSQ_ERR_SUCCESS){ role->text_name = mosquitto_strdup(textname); if(role->text_name == NULL){ mosquitto_free(role->rolename); mosquitto_free(role); continue; } } /* Text description */ char *textdescription; if(json_get_string(j_role, "textdescription", &textdescription, false) == MOSQ_ERR_SUCCESS){ role->text_description = mosquitto_strdup(textdescription); if(role->text_description == NULL){ mosquitto_free(role->text_name); mosquitto_free(role->rolename); mosquitto_free(role); continue; } } /* ACLs */ j_acls = cJSON_GetObjectItem(j_role, "acls"); if(j_acls && cJSON_IsArray(j_acls)){ if(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &role->acls.publish_c_send) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &role->acls.publish_c_recv) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &role->acls.subscribe_literal) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &role->acls.subscribe_pattern) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &role->acls.unsubscribe_literal) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &role->acls.unsubscribe_pattern) != 0 ){ mosquitto_free(role->rolename); mosquitto_free(role); continue; } } HASH_ADD_KEYPTR(hh, local_roles, role->rolename, strlen(role->rolename), role); } } HASH_SORT(local_roles, role_cmp); return 0; } int dynsec_roles__process_create(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *rolename; char *text_name, *text_description; struct dynsec__role *role; int rc = MOSQ_ERR_SUCCESS; cJSON *j_acls; const char *admin_clientid, *admin_username; if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textname", &text_name, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createRole", "Invalid/missing textname", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textdescription", &text_description, true) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "createRole", "Invalid/missing textdescription", correlation_data); return MOSQ_ERR_INVAL; } role = dynsec_roles__find(rolename); if(role){ dynsec__command_reply(j_responses, context, "createRole", "Role already exists", correlation_data); return MOSQ_ERR_SUCCESS; } role = mosquitto_calloc(1, sizeof(struct dynsec__role)); if(role == NULL){ dynsec__command_reply(j_responses, context, "createRole", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } role->rolename = mosquitto_strdup(rolename); if(role->rolename == NULL){ dynsec__command_reply(j_responses, context, "createRole", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } if(text_name){ role->text_name = mosquitto_strdup(text_name); if(role->text_name == NULL){ dynsec__command_reply(j_responses, context, "createRole", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } } if(text_description){ role->text_description = mosquitto_strdup(text_description); if(role->text_description == NULL){ dynsec__command_reply(j_responses, context, "createRole", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } } /* ACLs */ j_acls = cJSON_GetObjectItem(command, "acls"); if(j_acls && cJSON_IsArray(j_acls)){ if(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &role->acls.publish_c_send) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &role->acls.publish_c_recv) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &role->acls.subscribe_literal) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &role->acls.subscribe_pattern) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &role->acls.unsubscribe_literal) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &role->acls.unsubscribe_pattern) != 0 ){ dynsec__command_reply(j_responses, context, "createRole", "Internal error", correlation_data); rc = MOSQ_ERR_NOMEM; goto error; } } HASH_ADD_KEYPTR_INORDER(hh, local_roles, role->rolename, strlen(role->rolename), role, role_cmp); dynsec__config_save(); dynsec__command_reply(j_responses, context, "createRole", NULL, correlation_data); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | createRole | rolename=%s", admin_clientid, admin_username, rolename); return MOSQ_ERR_SUCCESS; error: if(role){ role__free_item(role, false); } return rc; } static void role__remove_all_clients(struct dynsec__role *role) { struct dynsec__clientlist *clientlist, *clientlist_tmp = NULL; HASH_ITER(hh, role->clientlist, clientlist, clientlist_tmp){ mosquitto_kick_client_by_username(clientlist->client->username, false); dynsec_rolelist__client_remove(clientlist->client, role); } } static void role__remove_all_groups(struct dynsec__role *role) { struct dynsec__grouplist *grouplist, *grouplist_tmp = NULL; HASH_ITER(hh, role->grouplist, grouplist, grouplist_tmp){ if(grouplist->group == dynsec_anonymous_group){ mosquitto_kick_client_by_username(NULL, false); } dynsec_clientlist__kick_all(grouplist->group->clientlist); dynsec_rolelist__group_remove(grouplist->group, role); } } int dynsec_roles__process_delete(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *rolename; struct dynsec__role *role; const char *admin_clientid, *admin_username; if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "deleteRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "deleteRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } role = dynsec_roles__find(rolename); if(role){ role__remove_all_clients(role); role__remove_all_groups(role); role__free_item(role, true); dynsec__config_save(); dynsec__command_reply(j_responses, context, "deleteRole", NULL, correlation_data); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | deleteRole | rolename=%s", admin_clientid, admin_username, rolename); return MOSQ_ERR_SUCCESS; }else{ dynsec__command_reply(j_responses, context, "deleteRole", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } } static cJSON *add_role_to_json(struct dynsec__role *role, bool verbose) { cJSON *j_role = NULL; if(verbose){ j_role = cJSON_CreateObject(); if(j_role == NULL){ return NULL; } if(cJSON_AddStringToObject(j_role, "rolename", role->rolename) == NULL || (role->text_name && cJSON_AddStringToObject(j_role, "textname", role->text_name) == NULL) || (role->text_description && cJSON_AddStringToObject(j_role, "textdescription", role->text_description) == NULL) ){ cJSON_Delete(j_role); return NULL; } if(add_acls_to_json(j_role, role)){ cJSON_Delete(j_role); return NULL; } }else{ j_role = cJSON_CreateString(role->rolename); if(j_role == NULL){ return NULL; } } return j_role; } int dynsec_roles__process_list(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { bool verbose; struct dynsec__role *role, *role_tmp = NULL; cJSON *tree, *j_roles, *j_role, *j_data; int i, count, offset; const char *admin_clientid, *admin_username; json_get_bool(command, "verbose", &verbose, true, false); json_get_int(command, "count", &count, true, -1); json_get_int(command, "offset", &offset, true, 0); tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "listRoles", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } if(cJSON_AddStringToObject(tree, "command", "listRoles") == NULL || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL || cJSON_AddIntToObject(j_data, "totalCount", (int)HASH_CNT(hh, local_roles)) == NULL || (j_roles = cJSON_AddArrayToObject(j_data, "roles")) == NULL || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "listRoles", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } i = 0; HASH_ITER(hh, local_roles, role, role_tmp){ if(i>=offset){ j_role = add_role_to_json(role, verbose); if(j_role == NULL){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "listRoles", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToArray(j_roles, j_role); if(count >= 0){ count--; if(count <= 0){ break; } } } i++; } cJSON_AddItemToArray(j_responses, tree); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | listRoles | verbose=%s | count=%d | offset=%d", admin_clientid, admin_username, verbose?"true":"false", count, offset); return MOSQ_ERR_SUCCESS; } int dynsec_roles__process_add_acl(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *rolename; char *topic; struct dynsec__role *role; struct dynsec__acl **acllist, *acl; int rc; char *acltype; const char *admin_clientid, *admin_username; if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "addRoleACL", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } if(json_get_string(command, "acltype", &acltype, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Invalid/missing acltype", correlation_data); return MOSQ_ERR_SUCCESS; } if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ acllist = &role->acls.publish_c_send; }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ acllist = &role->acls.publish_c_recv; }else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){ acllist = &role->acls.subscribe_literal; }else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){ acllist = &role->acls.subscribe_pattern; }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){ acllist = &role->acls.unsubscribe_literal; }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){ acllist = &role->acls.unsubscribe_pattern; }else{ dynsec__command_reply(j_responses, context, "addRoleACL", "Unknown acltype", correlation_data); return MOSQ_ERR_SUCCESS; } if(json_get_string(command, "topic", &topic, false) == MOSQ_ERR_SUCCESS){ if(mosquitto_validate_utf8(topic, (int)strlen(topic)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Topic not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } rc = mosquitto_sub_topic_check(topic); if(rc != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "addRoleACL", "Invalid ACL topic", correlation_data); return MOSQ_ERR_INVAL; } }else{ dynsec__command_reply(j_responses, context, "addRoleACL", "Invalid/missing topic", correlation_data); return MOSQ_ERR_SUCCESS; } HASH_FIND(hh, *acllist, topic, strlen(topic), acl); if(acl){ dynsec__command_reply(j_responses, context, "addRoleACL", "ACL with this topic already exists", correlation_data); return MOSQ_ERR_SUCCESS; } acl = mosquitto_calloc(1, sizeof(struct dynsec__acl)); if(acl == NULL){ dynsec__command_reply(j_responses, context, "addRoleACL", "Internal error", correlation_data); return MOSQ_ERR_SUCCESS; } acl->topic = mosquitto_strdup(topic); if(acl->topic == NULL){ mosquitto_free(acl); dynsec__command_reply(j_responses, context, "addRoleACL", "Internal error", correlation_data); return MOSQ_ERR_SUCCESS; } json_get_int(command, "priority", &acl->priority, true, 0); json_get_bool(command, "allow", &acl->allow, true, false); HASH_ADD_KEYPTR_INORDER(hh, *acllist, acl->topic, strlen(acl->topic), acl, insert_acl_cmp); dynsec__config_save(); dynsec__command_reply(j_responses, context, "addRoleACL", NULL, correlation_data); role__kick_all(role); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | addRoleACL | rolename=%s | acltype=%s | topic=%s | priority=%d | allow=%s", admin_clientid, admin_username, rolename, acltype, topic, acl->priority, acl->allow?"true":"false"); return MOSQ_ERR_SUCCESS; } int dynsec_roles__process_remove_acl(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *rolename; struct dynsec__role *role; struct dynsec__acl **acllist, *acl; char *topic; char *acltype; int rc; const char *admin_clientid, *admin_username; if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } if(json_get_string(command, "acltype", &acltype, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Invalid/missing acltype", correlation_data); return MOSQ_ERR_SUCCESS; } if(!strcasecmp(acltype, ACL_TYPE_PUB_C_SEND)){ acllist = &role->acls.publish_c_send; }else if(!strcasecmp(acltype, ACL_TYPE_PUB_C_RECV)){ acllist = &role->acls.publish_c_recv; }else if(!strcasecmp(acltype, ACL_TYPE_SUB_LITERAL)){ acllist = &role->acls.subscribe_literal; }else if(!strcasecmp(acltype, ACL_TYPE_SUB_PATTERN)){ acllist = &role->acls.subscribe_pattern; }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_LITERAL)){ acllist = &role->acls.unsubscribe_literal; }else if(!strcasecmp(acltype, ACL_TYPE_UNSUB_PATTERN)){ acllist = &role->acls.unsubscribe_pattern; }else{ dynsec__command_reply(j_responses, context, "removeRoleACL", "Unknown acltype", correlation_data); return MOSQ_ERR_SUCCESS; } if(json_get_string(command, "topic", &topic, false)){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Invalid/missing topic", correlation_data); return MOSQ_ERR_SUCCESS; } if(mosquitto_validate_utf8(topic, (int)strlen(topic)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Topic not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } rc = mosquitto_sub_topic_check(topic); if(rc != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "removeRoleACL", "Invalid ACL topic", correlation_data); return MOSQ_ERR_INVAL; } HASH_FIND(hh, *acllist, topic, strlen(topic), acl); if(acl){ role__free_acl(acllist, acl); dynsec__config_save(); dynsec__command_reply(j_responses, context, "removeRoleACL", NULL, correlation_data); role__kick_all(role); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | removeRoleACL | rolename=%s | acltype=%s | topic=%s", admin_clientid, admin_username, rolename, acltype, topic); }else{ dynsec__command_reply(j_responses, context, "removeRoleACL", "ACL not found", correlation_data); } return MOSQ_ERR_SUCCESS; } int dynsec_roles__process_get(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *rolename; struct dynsec__role *role; cJSON *tree, *j_role, *j_data; if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "getRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "getRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "getRole", "Role not found", correlation_data); return MOSQ_ERR_SUCCESS; } tree = cJSON_CreateObject(); if(tree == NULL){ dynsec__command_reply(j_responses, context, "getRole", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } if(cJSON_AddStringToObject(tree, "command", "getRole") == NULL || (j_data = cJSON_AddObjectToObject(tree, "data")) == NULL || (correlation_data && cJSON_AddStringToObject(tree, "correlationData", correlation_data) == NULL) ){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getRole", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } j_role = add_role_to_json(role, true); if(j_role == NULL){ cJSON_Delete(tree); dynsec__command_reply(j_responses, context, "getRole", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } cJSON_AddItemToObject(j_data, "role", j_role); cJSON_AddItemToArray(j_responses, tree); return MOSQ_ERR_SUCCESS; } int dynsec_roles__process_modify(cJSON *j_responses, struct mosquitto *context, cJSON *command, char *correlation_data) { char *rolename; char *text_name, *text_description; struct dynsec__role *role; char *str; cJSON *j_acls; struct dynsec__acl *tmp_publish_c_send = NULL, *tmp_publish_c_recv = NULL; struct dynsec__acl *tmp_subscribe_literal = NULL, *tmp_subscribe_pattern = NULL; struct dynsec__acl *tmp_unsubscribe_literal = NULL, *tmp_unsubscribe_pattern = NULL; const char *admin_clientid, *admin_username; if(json_get_string(command, "rolename", &rolename, false) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "modifyRole", "Invalid/missing rolename", correlation_data); return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(rolename, (int)strlen(rolename)) != MOSQ_ERR_SUCCESS){ dynsec__command_reply(j_responses, context, "modifyRole", "Role name not valid UTF-8", correlation_data); return MOSQ_ERR_INVAL; } role = dynsec_roles__find(rolename); if(role == NULL){ dynsec__command_reply(j_responses, context, "modifyRole", "Role does not exist", correlation_data); return MOSQ_ERR_INVAL; } if(json_get_string(command, "textname", &text_name, false) == MOSQ_ERR_SUCCESS){ str = mosquitto_strdup(text_name); if(str == NULL){ dynsec__command_reply(j_responses, context, "modifyRole", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } mosquitto_free(role->text_name); role->text_name = str; } if(json_get_string(command, "textdescription", &text_description, false) == MOSQ_ERR_SUCCESS){ str = mosquitto_strdup(text_description); if(str == NULL){ dynsec__command_reply(j_responses, context, "modifyRole", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } mosquitto_free(role->text_description); role->text_description = str; } j_acls = cJSON_GetObjectItem(command, "acls"); if(j_acls && cJSON_IsArray(j_acls)){ if(dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_SEND, &tmp_publish_c_send) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_PUB_C_RECV, &tmp_publish_c_recv) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_LITERAL, &tmp_subscribe_literal) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_SUB_PATTERN, &tmp_subscribe_pattern) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_LITERAL, &tmp_unsubscribe_literal) != 0 || dynsec_roles__acl_load(j_acls, ACL_TYPE_UNSUB_PATTERN, &tmp_unsubscribe_pattern) != 0 ){ /* Free any that were successful */ role__free_all_acls(&tmp_publish_c_send); role__free_all_acls(&tmp_publish_c_recv); role__free_all_acls(&tmp_subscribe_literal); role__free_all_acls(&tmp_subscribe_pattern); role__free_all_acls(&tmp_unsubscribe_literal); role__free_all_acls(&tmp_unsubscribe_pattern); dynsec__command_reply(j_responses, context, "modifyRole", "Internal error", correlation_data); return MOSQ_ERR_NOMEM; } role__free_all_acls(&role->acls.publish_c_send); role__free_all_acls(&role->acls.publish_c_recv); role__free_all_acls(&role->acls.subscribe_literal); role__free_all_acls(&role->acls.subscribe_pattern); role__free_all_acls(&role->acls.unsubscribe_literal); role__free_all_acls(&role->acls.unsubscribe_pattern); role->acls.publish_c_send = tmp_publish_c_send; role->acls.publish_c_recv = tmp_publish_c_recv; role->acls.subscribe_literal = tmp_subscribe_literal; role->acls.subscribe_pattern = tmp_subscribe_pattern; role->acls.unsubscribe_literal = tmp_unsubscribe_literal; role->acls.unsubscribe_pattern = tmp_unsubscribe_pattern; } dynsec__config_save(); dynsec__command_reply(j_responses, context, "modifyRole", NULL, correlation_data); admin_clientid = mosquitto_client_id(context); admin_username = mosquitto_client_username(context); mosquitto_log_printf(MOSQ_LOG_INFO, "dynsec: %s/%s | modifyRole | rolename=%s", admin_clientid, admin_username, rolename); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/plugins/dynamic-security/sub_matches_sub.c000066400000000000000000000120431450213760600241360ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include #include #include #include #include "dynamic_security.h" static char *strtok_hier(char *str, char **saveptr) { char *c; if(str != NULL){ *saveptr = str; } if(*saveptr == NULL){ return NULL; } c = strchr(*saveptr, '/'); if(c){ str = *saveptr; *saveptr = c+1; c[0] = '\0'; }else if(*saveptr){ /* No match, but surplus string */ str = *saveptr; *saveptr = NULL; } return str; } static int count_hier_levels(const char *s) { int count = 1; const char *c = s; while((c = strchr(c, '/')) && c[0]){ c++; count++; } return count; } static bool hash_check(char *s, size_t *len) { if((*len) == 1 && s[0] == '#'){ s[0] = '\0'; (*len)--; return true; }else if((*len) > 1 && s[(*len)-2] == '/' && s[(*len)-1] == '#'){ s[(*len)-2] = '\0'; s[(*len)-1] = '\0'; (*len) -= 2; return true; } return false; } bool sub_acl_check(const char *acl, const char *sub) { char *acl_local; char *sub_local; size_t acl_len, sub_len; bool acl_hash = false, sub_hash = false; int acl_levels, sub_levels; int i; char *acl_token, *sub_token; char *acl_saveptr, *sub_saveptr; acl_len = strlen(acl); if(acl_len == 1 && acl[0] == '#'){ return true; } sub_len = strlen(sub); /* mosquitto_validate_utf8(acl, acl_len); */ acl_local = strdup(acl); sub_local = strdup(sub); if(acl_local == NULL || sub_local == NULL){ free(acl_local); free(sub_local); return false; } acl_hash = hash_check(acl_local, &acl_len); sub_hash = hash_check(sub_local, &sub_len); if(sub_hash == true && acl_hash == false){ free(acl_local); free(sub_local); return false; } acl_levels = count_hier_levels(acl_local); sub_levels = count_hier_levels(sub_local); if(acl_levels > sub_levels){ free(acl_local); free(sub_local); return false; }else if(sub_levels > acl_levels){ if(acl_hash == false){ free(acl_local); free(sub_local); return false; } } acl_saveptr = acl_local; sub_saveptr = sub_local; for(i=0; i=acl_levels && acl_hash == true){ /* The sub has more levels of hierarchy than the acl, but the acl * ends in a multi level wildcard so the match is fine. */ }else{ free(acl_local); free(sub_local); return false; } } free(acl_local); free(sub_local); return true; } #ifdef TEST #define BLK "\e[0;30m" #define RED "\e[0;31m" #define GRN "\e[0;32m" #define YEL "\e[0;33m" #define BLU "\e[0;34m" #define MAG "\e[0;35m" #define CYN "\e[0;36m" #define WHT "\e[0;37m" #define RST "\e[0m" void hier_test(const char *s, int expected) { int levels; levels = count_hier_levels(s); printf("HIER %s %d:%d ", s, expected, levels); if(levels == expected){ printf(GRN "passed" RST "\n"); }else{ printf(RED "failed" RST "\n"); } } void test(const char *sub1, const char *sub2, bool expected) { bool result; printf("ACL %s : %s ", sub1, sub2); result = sub_acl_check(sub1, sub2); if(result == expected){ printf(GRN "passed\n" RST); }else{ printf(RED "failed\n" RST); } } int main(int argc, char *argv[]) { hier_test("foo/+/bar", 3); hier_test("foo/#", 2); hier_test("foo/+/ba℞/#", 4); hier_test("foo/baz/ba℞", 3); hier_test("foo/+/ba℞/#", 4); hier_test("foo/baz/ba℞/+", 4); hier_test("foo/+/ba℞/#", 4); hier_test("foo/baz/ba℞/#", 4); hier_test("foo/+/ba℞/#", 4); hier_test("foo/baz/+/#", 4); hier_test("/+//#", 4); hier_test("/foo///#", 5); hier_test("#", 1); hier_test("+", 1); hier_test("/", 2); hier_test("////////////////////////////////////////////////////////////////////////////////////////////////////", 101); test("foo/+/bar", "foo/#", false); test("foo/+/ba℞/#", "foo/baz/ba℞", true); test("foo/+/ba℞/#", "foo/baz/ba℞/+", true); test("foo/+/ba℞/#", "foo/baz/ba℞/#", true); test("foo/+/ba℞/#", "foo/baz/+/#", false); test("/+//#", "/foo///#", true); test("#", "#", true); test("#", "+", true); test("/#", "+", false); test("/#", "/+", true); test("/+", "#", false); test("/+", "+", false); test("+/+", "topic/topic", true); test("+/+", "topic/topic/", false); test("+", "#", false); test("+", "+", true); test("a/b/c/d/e", "a/b/c/d/e", true); test("a/b/ /d/e", "a/b/c/d/e", false); return 0; } #endif mosquitto-2.0.18/plugins/message-timestamp/000077500000000000000000000000001450213760600207605ustar00rootroot00000000000000mosquitto-2.0.18/plugins/message-timestamp/CMakeLists.txt000066400000000000000000000010711450213760600235170ustar00rootroot00000000000000include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/include ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH}) add_library(mosquitto_message_timestamp MODULE mosquitto_message_timestamp.c) set_target_properties(mosquitto_message_timestamp PROPERTIES POSITION_INDEPENDENT_CODE 1 ) set_target_properties(mosquitto_message_timestamp PROPERTIES PREFIX "") # Don't install, these are example plugins only. #install(TARGETS mosquitto_message_timestamp RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}") mosquitto-2.0.18/plugins/message-timestamp/Makefile000066400000000000000000000012231450213760600224160ustar00rootroot00000000000000include ../../config.mk .PHONY : all binary check clean reallyclean test install uninstall PLUGIN_NAME=mosquitto_message_timestamp all : binary binary : ${PLUGIN_NAME}.so ${PLUGIN_NAME}.so : ${PLUGIN_NAME}.c $(CROSS_COMPILE)$(CC) $(PLUGIN_CPPFLAGS) $(PLUGIN_CFLAGS) $(PLUGIN_LDFLAGS) -fPIC -shared $< -o $@ reallyclean : clean clean: -rm -f *.o ${PLUGIN_NAME}.so *.gcda *.gcno check: test test: install: ${PLUGIN_NAME}.so # Don't install, these are examples only. #$(INSTALL) -d "${DESTDIR}$(libdir)" #$(INSTALL) ${STRIP_OPTS} ${PLUGIN_NAME}.so "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" uninstall : -rm -f "${DESTDIR}${libdir}/${PLUGIN_NAME}.so" mosquitto-2.0.18/plugins/message-timestamp/mosquitto_message_timestamp.c000066400000000000000000000045231450213760600267630ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ /* * Add an MQTT v5 user-property with key "timestamp" and value of timestamp in ISO-8601 format to all messages. * * Compile with: * gcc -I -fPIC -shared mosquitto_timestamp.c -o mosquitto_timestamp.so * * Use in config with: * * plugin /path/to/mosquitto_timestamp.so * * Note that this only works on Mosquitto 2.0 or later. */ #include "config.h" #include #include #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mosquitto.h" #include "mqtt_protocol.h" static mosquitto_plugin_id_t *mosq_pid = NULL; static int callback_message(int event, void *event_data, void *userdata) { struct mosquitto_evt_message *ed = event_data; struct timespec ts; struct tm *ti; char time_buf[25]; UNUSED(event); UNUSED(userdata); clock_gettime(CLOCK_REALTIME, &ts); ti = gmtime(&ts.tv_sec); strftime(time_buf, sizeof(time_buf), "%Y-%m-%dT%H:%M:%SZ", ti); return mosquitto_property_add_string_pair(&ed->properties, MQTT_PROP_USER_PROPERTY, "timestamp", time_buf); } int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { int i; for(i=0; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ /* * This is an *example* plugin which demonstrates how to modify the payload of * a message after it is received by the broker and before it is sent on to * other clients. * * You should be very sure of what you are doing before making use of this feature. * * Compile with: * gcc -I -fPIC -shared mosquitto_payload_modification.c -o mosquitto_payload_modification.so * * Use in config with: * * plugin /path/to/mosquitto_payload_modification.so * * Note that this only works on Mosquitto 2.0 or later. */ #include #include #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mosquitto.h" #include "mqtt_protocol.h" #define UNUSED(A) (void)(A) static mosquitto_plugin_id_t *mosq_pid = NULL; static int callback_message(int event, void *event_data, void *userdata) { struct mosquitto_evt_message *ed = event_data; char *new_payload; uint32_t new_payloadlen; UNUSED(event); UNUSED(userdata); /* This simply adds "hello " to the front of every payload. You can of * course do much more complicated message processing if needed. */ /* Calculate the length of our new payload */ new_payloadlen = ed->payloadlen + (uint32_t)strlen("hello ")+1; /* Allocate some memory - use * mosquitto_calloc/mosquitto_malloc/mosquitto_strdup when allocating, to * allow the broker to track memory usage */ new_payload = mosquitto_calloc(1, new_payloadlen); if(new_payload == NULL){ return MOSQ_ERR_NOMEM; } /* Print "hello " to the payload */ snprintf(new_payload, new_payloadlen, "hello "); memcpy(new_payload+(uint32_t)strlen("hello "), ed->payload, ed->payloadlen); /* Assign the new payload and payloadlen to the event data structure. You * must *not* free the original payload, it will be handled by the * broker. */ ed->payload = new_payload; ed->payloadlen = new_payloadlen; return MOSQ_ERR_SUCCESS; } int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { int i; for(i=0; i #include /usr/sbin/mosquitto r, /etc/mosquitto/mosquitto.conf r, /etc/mosquitto/ca_certificates/* r, /etc/mosquitto/certs/* r, /etc/mosquitto/conf.d/* r, /var/lib/mosquitto/ r, /var/lib/mosquitto/mosquitto.db rwk, /var/lib/mosquitto/mosquitto.db.new rwk, /var/run/mosquitto.pid rw, network inet stream, network inet6 stream, network inet dgram, network inet6 dgram, # For drop privileges capability setgid, capability setuid, # For tcp-wrappers /lib{,32,64}/libwrap.so* rm, /etc/hosts.allow r, /etc/hosts.deny r, } mosquitto-2.0.18/service/000077500000000000000000000000001450213760600153125ustar00rootroot00000000000000mosquitto-2.0.18/service/monit/000077500000000000000000000000001450213760600164405ustar00rootroot00000000000000mosquitto-2.0.18/service/monit/mosquitto.monit000066400000000000000000000002051450213760600215510ustar00rootroot00000000000000check process mosquitto with pidfile /run/mosquitto.pid start = "/etc/init.d/mosquitto start" stop = "/etc/init.d/mosquitto stop" mosquitto-2.0.18/service/svscan/000077500000000000000000000000001450213760600166075ustar00rootroot00000000000000mosquitto-2.0.18/service/svscan/run000077500000000000000000000001001450213760600173300ustar00rootroot00000000000000#!/bin/sh /usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf mosquitto-2.0.18/service/systemd/000077500000000000000000000000001450213760600170025ustar00rootroot00000000000000mosquitto-2.0.18/service/systemd/README000066400000000000000000000010101450213760600176520ustar00rootroot00000000000000Select appropriate systemd service based on your compile settings. If you enabled WITH_SYSTEMD, use mosquitto.service.notify, otherwise use mosquitto.service.simple. The service must be renamed to mosquitto.service before usage. Don't forget to change default paths in service file if you changed the default build settings. With WITH_SYSTEMD mosquitto will notify a complete startup after initialization. This means that follow-up units can be started after full initialization of mosquitto (i.e. sockets are opened). mosquitto-2.0.18/service/systemd/mosquitto.service.notify000066400000000000000000000010541450213760600237370ustar00rootroot00000000000000[Unit] Description=Mosquitto MQTT Broker Documentation=man:mosquitto.conf(5) man:mosquitto(8) After=network.target Wants=network.target [Service] Type=notify NotifyAccess=main ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto [Install] WantedBy=multi-user.target mosquitto-2.0.18/service/systemd/mosquitto.service.simple000066400000000000000000000010161450213760600237160ustar00rootroot00000000000000[Unit] Description=Mosquitto MQTT Broker Documentation=man:mosquitto.conf(5) man:mosquitto(8) After=network.target Wants=network.target [Service] ExecStart=/usr/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf ExecReload=/bin/kill -HUP $MAINPID Restart=on-failure ExecStartPre=/bin/mkdir -m 740 -p /var/log/mosquitto ExecStartPre=/bin/chown mosquitto:mosquitto /var/log/mosquitto ExecStartPre=/bin/mkdir -m 740 -p /run/mosquitto ExecStartPre=/bin/chown mosquitto:mosquitto /run/mosquitto [Install] WantedBy=multi-user.target mosquitto-2.0.18/service/upstart/000077500000000000000000000000001450213760600170145ustar00rootroot00000000000000mosquitto-2.0.18/service/upstart/mosquitto.conf000066400000000000000000000002621450213760600217270ustar00rootroot00000000000000description "Mosquitto MQTTv3.1 broker" author "Roger Light " start on net-device-up respawn exec /usr/local/sbin/mosquitto -c /etc/mosquitto/mosquitto.conf mosquitto-2.0.18/set-version.sh000077500000000000000000000012611450213760600164670ustar00rootroot00000000000000#!/bin/sh MAJOR=2 MINOR=0 REVISION=18 sed -i "s/^VERSION=.*/VERSION=${MAJOR}.${MINOR}.${REVISION}/" config.mk sed -i "s/^#define LIBMOSQUITTO_MAJOR .*/#define LIBMOSQUITTO_MAJOR ${MAJOR}/" include/mosquitto.h sed -i "s/^#define LIBMOSQUITTO_MINOR .*/#define LIBMOSQUITTO_MINOR ${MINOR}/" include/mosquitto.h sed -i "s/^#define LIBMOSQUITTO_REVISION .*/#define LIBMOSQUITTO_REVISION ${REVISION}/" include/mosquitto.h sed -i "s/^set (VERSION .*)/set (VERSION ${MAJOR}.${MINOR}.${REVISION})/" CMakeLists.txt sed -i "s/^!define VERSION .*/!define VERSION ${MAJOR}.${MINOR}.${REVISION}/" installer/*.nsi sed -i "s/^version: .*/version: ${MAJOR}.${MINOR}.${REVISION}/" snap/snapcraft.yaml mosquitto-2.0.18/snap/000077500000000000000000000000001450213760600146135ustar00rootroot00000000000000mosquitto-2.0.18/snap/local/000077500000000000000000000000001450213760600157055ustar00rootroot00000000000000mosquitto-2.0.18/snap/local/default_config.conf000066400000000000000000000000341450213760600215220ustar00rootroot00000000000000persistence false user root mosquitto-2.0.18/snap/local/launcher.sh000077500000000000000000000026511450213760600200510ustar00rootroot00000000000000#!/bin/sh # Wrapper to check for custom config in $SNAP_USER_COMMON or $SNAP_COMMON and # use it otherwise fall back to the included basic config which will at least # allow mosquitto to run and do something. # This script will also copy the full example config in to SNAP_USER_COMMON or # SNAP_COMMON so that people can refer to it. # # The decision about whether to use SNAP_USER_COMMON or SNAP_COMMON is taken # based on the user that runs the command. If the user is root, it is assumed # that mosquitto is being run as a system daemon, and SNAP_COMMON will be used. # If a non-root user runs the command, then SNAP_USER_COMMON will be used. case "$SNAP_USER_COMMON" in */root/snap/mosquitto/common*) COMMON=$SNAP_COMMON ;; *) COMMON=$SNAP_USER_COMMON ;; esac CONFIG_FILE="$SNAP/default_config.conf" CUSTOM_CONFIG="$COMMON/mosquitto.conf" # Copy the example config if it doesn't exist if [ ! -e "$COMMON/mosquitto_example.conf" ] then echo "Copying example config to $COMMON/mosquitto_example.conf" echo "You can create a custom config by creating a file called $CUSTOM_CONFIG" cp $SNAP/mosquitto.conf $COMMON/mosquitto_example.conf fi # Does the custom config exist? If so use it. if [ -e "$CUSTOM_CONFIG" ] then echo "Found config in $CUSTOM_CONFIG" CONFIG_FILE=$CUSTOM_CONFIG else echo "Using default config from $CONFIG_FILE" fi # Launch the snap $SNAP/usr/sbin/mosquitto -c $CONFIG_FILE $@ mosquitto-2.0.18/snap/snapcraft.yaml000066400000000000000000000056301450213760600174640ustar00rootroot00000000000000name: mosquitto version: 2.0.18 summary: Eclipse Mosquitto MQTT broker description: This is a message broker that supports version 5.0, 3.1.1, and 3.1 of the MQTT protocol. MQTT provides a method of carrying out messaging using a publish/subscribe model. It is lightweight, both in terms of bandwidth usage and ease of implementation. This makes it particularly useful at the edge of the network where a sensor or other simple device may be implemented using an arduino for example. confinement: strict grade: stable base: core18 apps: mosquitto: command: launcher.sh daemon: simple restart-condition: always plugs: [home, network, network-bind] ctrl: command: usr/bin/mosquitto_ctrl plugs: [home, network] pub: command: usr/bin/mosquitto_pub plugs: [home, network] rr: command: usr/bin/mosquitto_rr plugs: [home, network] sub: command: usr/bin/mosquitto_sub plugs: [home, network] passwd: command: usr/bin/mosquitto_passwd plugs: [home] parts: script: plugin: dump source: snap/local/ prime: - default_config.conf - launcher.sh config: plugin: dump source: . prime: - mosquitto.conf mosquitto: after: - lws plugin: make make-parameters: ["prefix=/usr", "WITH_WEBSOCKETS=yes", "WITH_ADNS=yes", "CFLAGS=-Wall -ggdb -O2 -I$SNAPCRAFT_STAGE/include -D_GNU_SOURCE"] source: https://github.com/eclipse/mosquitto source-type: git build-packages: - libssl-dev - xsltproc - docbook-xsl - gcc - g++ stage-packages: - libssl1.0.0 - ca-certificates prime: - usr/sbin/mosquitto - usr/bin/mosquitto_ctrl - usr/bin/mosquitto_pub - usr/bin/mosquitto_rr - usr/bin/mosquitto_sub - usr/bin/mosquitto_passwd - usr/lib/libmosquitto.so* - usr/lib/mosquitto_dynamic_security.so* - lib/*-linux-gnu/libcrypto.so* - lib/*-linux-gnu/libssl.so* - usr/include/mosquitto.h - usr/include/mosquitto_broker.h - usr/include/mosquitto_plugin.h - usr/include/mqtt_protocol.h lws: after: - cjson plugin: cmake configflags: ["-DLWS_IPV6=ON", "-DLWS_WITHOUT_CLIENT=ON", "-DLWS_WITHOUT_EXTENSIONS=ON", "-DLWS_WITH_ZIP_FOPS=OFF", "-DLWS_WITH_ZLIB=OFF", "-DLWS_WITH_SHARED=OFF"] source: https://github.com/warmcat/libwebsockets/archive/v2.4.2.tar.gz source-type: tar stage: - include/libwebsockets.h - include/lws_config.h - lib/libwebsockets.a prime: [-*] cjson: plugin: cmake configflags: ["-DCMAKE_C_FLAGS=-fPIC", "-DBUILD_SHARED_AND_STATIC_LIBS=OFF", "-DBUILD_SHARED_LIBS=OFF", "-DCJSON_BUILD_SHARED_LIBS=OFF", "-DCJSON_OVERRIDE_BUILD_SHARED_LIBS=OFF"] source: https://github.com/DaveGamble/cJSON/archive/v1.7.14.tar.gz source-type: tar stage: - include/cjson/cJSON.h - lib/libcjson.a prime: [-*] mosquitto-2.0.18/src/000077500000000000000000000000001450213760600144415ustar00rootroot00000000000000mosquitto-2.0.18/src/CMakeLists.txt000066400000000000000000000134761450213760600172140ustar00rootroot00000000000000include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/src ${mosquitto_SOURCE_DIR}/include ${mosquitto_SOURCE_DIR}/lib ${OPENSSL_INCLUDE_DIR} ${STDBOOL_H_PATH} ${STDINT_H_PATH}) set (MOSQ_SRCS ../lib/alias_mosq.c ../lib/alias_mosq.h bridge.c bridge_topic.c conf.c conf_includedir.c context.c control.c database.c handle_auth.c handle_connack.c handle_connect.c handle_disconnect.c ../lib/handle_ping.c ../lib/handle_pubackcomp.c handle_publish.c ../lib/handle_pubrec.c ../lib/handle_pubrel.c ../lib/handle_suback.c handle_subscribe.c ../lib/handle_unsuback.c handle_unsubscribe.c keepalive.c lib_load.h logging.c loop.c ../lib/memory_mosq.c ../lib/memory_mosq.h memory_public.c mosquitto.c ../include/mosquitto_broker.h mosquitto_broker_internal.h ../lib/misc_mosq.c ../lib/misc_mosq.h mux.c mux.h mux_epoll.c mux_poll.c net.c ../lib/net_mosq_ocsp.c ../lib/net_mosq.c ../lib/net_mosq.h ../lib/packet_datatypes.c ../lib/packet_mosq.c ../lib/packet_mosq.h password_mosq.c password_mosq.h persist_read_v234.c persist_read_v5.c persist_read.c persist_write_v5.c persist_write.c persist.h plugin.c plugin_public.c property_broker.c ../lib/property_mosq.c ../lib/property_mosq.h read_handle.c ../lib/read_handle.h retain.c security.c security_default.c ../lib/send_mosq.c ../lib/send_mosq.h send_auth.c send_connack.c ../lib/send_connect.c ../lib/send_disconnect.c ../lib/send_publish.c send_suback.c signals.c ../lib/send_subscribe.c send_unsuback.c ../lib/send_unsubscribe.c session_expiry.c ../lib/strings_mosq.c subs.c sys_tree.c sys_tree.h ../lib/time_mosq.c ../lib/tls_mosq.c topic_tok.c ../lib/util_mosq.c ../lib/util_topic.c ../lib/util_mosq.h ../lib/utf8_mosq.c websockets.c will_delay.c ../lib/will_mosq.c ../lib/will_mosq.h) if (WITH_BUNDLED_DEPS) include_directories(${mosquitto_SOURCE_DIR} ${mosquitto_SOURCE_DIR}/deps) endif (WITH_BUNDLED_DEPS) find_path(HAVE_SYS_EPOLL_H sys/epoll.h) if (HAVE_SYS_EPOLL_H) add_definitions("-DWITH_EPOLL") endif() option(INC_BRIDGE_SUPPORT "Include bridge support for connecting to other brokers?" ON) if (INC_BRIDGE_SUPPORT) set (MOSQ_SRCS ${MOSQ_SRCS} bridge.c) add_definitions("-DWITH_BRIDGE") endif (INC_BRIDGE_SUPPORT) option(USE_LIBWRAP "Include tcp-wrappers support?" OFF) if (USE_LIBWRAP) set (MOSQ_LIBS ${MOSQ_LIBS} wrap) add_definitions("-DWITH_WRAP") endif (USE_LIBWRAP) option(INC_DB_UPGRADE "Include database upgrade support? (recommended)" ON) option(INC_MEMTRACK "Include memory tracking support?" ON) if (INC_MEMTRACK) add_definitions("-DWITH_MEMORY_TRACKING") endif (INC_MEMTRACK) option(WITH_PERSISTENCE "Include persistence support?" ON) if (WITH_PERSISTENCE) add_definitions("-DWITH_PERSISTENCE") endif (WITH_PERSISTENCE) option(WITH_SYS_TREE "Include $SYS tree support?" ON) if (WITH_SYS_TREE) add_definitions("-DWITH_SYS_TREE") endif (WITH_SYS_TREE) option(WITH_ADNS "Include ADNS support?" OFF) if (CMAKE_SYSTEM_NAME STREQUAL Linux) option(WITH_SYSTEMD "Include systemd support?" OFF) if (WITH_SYSTEMD) add_definitions("-DWITH_SYSTEMD") find_library(SYSTEMD_LIBRARY systemd) set (MOSQ_LIBS ${MOSQ_LIBS} ${SYSTEMD_LIBRARY}) endif (WITH_SYSTEMD) endif (CMAKE_SYSTEM_NAME STREQUAL Linux) option(WITH_WEBSOCKETS "Include websockets support?" OFF) option(STATIC_WEBSOCKETS "Use the static libwebsockets library?" OFF) if (WITH_WEBSOCKETS) find_package(libwebsockets) add_definitions("-DWITH_WEBSOCKETS") endif (WITH_WEBSOCKETS) option(WITH_CONTROL "Include $CONTROL topic support?" ON) if (WITH_CONTROL) add_definitions("-DWITH_CONTROL") endif (WITH_CONTROL) if (WIN32 OR CYGWIN) set (MOSQ_SRCS ${MOSQ_SRCS} service.c) endif (WIN32 OR CYGWIN) add_definitions (-DWITH_BROKER) if (WITH_DLT) message(STATUS "DLT_LIBDIR = ${DLT_LIBDIR}") link_directories(${DLT_LIBDIR}) set (MOSQ_LIBS ${MOSQ_LIBS} ${DLT_LIBRARIES}) endif (WITH_DLT) set (MOSQ_LIBS ${MOSQ_LIBS} ${OPENSSL_LIBRARIES}) # Check for getaddrinfo_a include(CheckLibraryExists) check_library_exists(anl getaddrinfo_a "" HAVE_GETADDRINFO_A) if (HAVE_GETADDRINFO_A AND WITH_ADNS) add_definitions("-DWITH_ADNS") add_definitions(-DHAVE_GETADDRINFO_A) set (MOSQ_LIBS ${MOSQ_LIBS} anl) endif (HAVE_GETADDRINFO_A AND WITH_ADNS) if (UNIX) if (APPLE) set (MOSQ_LIBS ${MOSQ_LIBS} dl m) elseif (${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD") set (MOSQ_LIBS ${MOSQ_LIBS} m) elseif (${CMAKE_SYSTEM_NAME} MATCHES "NetBSD") set (MOSQ_LIBS ${MOSQ_LIBS} m) elseif (${CMAKE_SYSTEM_NAME} MATCHES "Haiku") set (MOSQ_LIBS ${MOSQ_LIBS} m network) elseif(QNX) set(MOSQ_LIBS ${MOSQ_LIBS} m socket) else(APPLE) set (MOSQ_LIBS ${MOSQ_LIBS} dl m) find_library(LIBRT rt) if (LIBRT) set (MOSQ_LIBS ${MOSQ_LIBS} rt) endif (LIBRT) endif (APPLE) endif (UNIX) if (WIN32) set (MOSQ_LIBS ${MOSQ_LIBS} ws2_32) endif (WIN32) if (WITH_WEBSOCKETS) if (STATIC_WEBSOCKETS) set (MOSQ_LIBS ${MOSQ_LIBS} websockets_static) if (WIN32) set (MOSQ_LIBS ${MOSQ_LIBS} iphlpapi) link_directories(${mosquitto_SOURCE_DIR}) endif (WIN32) else (STATIC_WEBSOCKETS) set (MOSQ_LIBS ${MOSQ_LIBS} websockets) endif (STATIC_WEBSOCKETS) endif (WITH_WEBSOCKETS) add_executable(mosquitto ${MOSQ_SRCS}) target_link_libraries(mosquitto ${MOSQ_LIBS}) if (WIN32) set_target_properties(mosquitto PROPERTIES ENABLE_EXPORTS 1) endif (WIN32) if (UNIX) if (APPLE) set_target_properties(mosquitto PROPERTIES LINK_FLAGS "-Wl,-exported_symbols_list -Wl,${mosquitto_SOURCE_DIR}/src/linker-macosx.syms") else (APPLE) set_target_properties(mosquitto PROPERTIES LINK_FLAGS "-Wl,-dynamic-list=${mosquitto_SOURCE_DIR}/src/linker.syms") endif (APPLE) endif (UNIX) install(TARGETS mosquitto RUNTIME DESTINATION "${CMAKE_INSTALL_SBINDIR}") install(FILES ../include/mosquitto_broker.h ../include/mosquitto_plugin.h DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") mosquitto-2.0.18/src/Makefile000066400000000000000000000301771450213760600161110ustar00rootroot00000000000000include ../config.mk .PHONY: all install uninstall clean reallyclean all : mosquitto OBJS= mosquitto.o \ alias_mosq.o \ bridge.o \ bridge_topic.o \ conf.o \ conf_includedir.o \ context.o \ control.o \ database.o \ handle_auth.o \ handle_connack.o \ handle_connect.o \ handle_disconnect.o \ handle_ping.o \ handle_pubackcomp.o \ handle_publish.o \ handle_pubrec.o \ handle_pubrel.o \ handle_suback.o \ handle_subscribe.o \ handle_unsuback.o \ handle_unsubscribe.o \ keepalive.o \ logging.o \ loop.o \ memory_mosq.o \ memory_public.o \ misc_mosq.o \ mux.o \ mux_epoll.o \ mux_poll.o \ net.o \ net_mosq.o \ net_mosq_ocsp.o \ packet_datatypes.o \ packet_mosq.o \ password_mosq.o \ property_broker.o \ property_mosq.o \ persist_read.o \ persist_read_v234.o \ persist_read_v5.o \ persist_write.o \ persist_write_v5.o \ plugin.o \ plugin_public.o \ read_handle.o \ retain.o \ security.o \ security_default.o \ send_auth.o \ send_connack.o \ send_connect.o \ send_disconnect.o \ send_mosq.o \ send_publish.o \ send_suback.o \ send_subscribe.o \ send_unsuback.o \ send_unsubscribe.o \ service.o \ session_expiry.o \ signals.o \ strings_mosq.o \ subs.o \ sys_tree.o \ time_mosq.o \ topic_tok.o \ tls_mosq.o \ utf8_mosq.o \ util_mosq.o \ util_topic.o \ websockets.o \ will_delay.o \ will_mosq.o \ xtreport.o mosquitto : ${OBJS} ${CROSS_COMPILE}${CC} ${BROKER_LDFLAGS} $^ -o $@ $(BROKER_LDADD) mosquitto.o : mosquitto.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ alias_mosq.o : ../lib/alias_mosq.c ../lib/alias_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ bridge.o : bridge.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ bridge_topic.o : bridge_topic.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ conf.o : conf.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ conf_includedir.o : conf_includedir.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ context.o : context.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ control.o : control.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ database.o : database.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_auth.o : handle_auth.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_connack.o : handle_connack.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_connect.o : handle_connect.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_disconnect.o : handle_disconnect.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_ping.o : ../lib/handle_ping.c ../lib/read_handle.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_pubackcomp.o : ../lib/handle_pubackcomp.c ../lib/read_handle.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_publish.o : handle_publish.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_pubrec.o : ../lib/handle_pubrec.c ../lib/read_handle.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_pubrel.o : ../lib/handle_pubrel.c ../lib/read_handle.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_suback.o : ../lib/handle_suback.c ../lib/read_handle.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_subscribe.o : handle_subscribe.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_unsuback.o : ../lib/handle_unsuback.c ../lib/read_handle.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ handle_unsubscribe.o : handle_unsubscribe.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ keepalive.o : keepalive.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ logging.o : logging.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ loop.o : loop.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ memory_mosq.o : ../lib/memory_mosq.c ../lib/memory_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ memory_public.o : memory_public.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ misc_mosq.o : ../lib/misc_mosq.c ../lib/misc_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ mux.o : mux.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ mux_epoll.o : mux_epoll.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ mux_poll.o : mux_poll.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ net.o : net.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ net_mosq_ocsp.o : ../lib/net_mosq_ocsp.c ../lib/net_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ net_mosq.o : ../lib/net_mosq.c ../lib/net_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ password_mosq.o : password_mosq.c password_mosq.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ persist_read.o : persist_read.c persist.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ persist_read_v234.o : persist_read_v234.c persist.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ persist_read_v5.o : persist_read_v5.c persist.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ persist_write.o : persist_write.c persist.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ persist_write_v5.o : persist_write_v5.c persist.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ packet_datatypes.o : ../lib/packet_datatypes.c ../lib/packet_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ packet_mosq.o : ../lib/packet_mosq.c ../lib/packet_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ property_broker.o : property_broker.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ property_mosq.o : ../lib/property_mosq.c ../lib/property_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ plugin.o : plugin.c ../include/mosquitto_plugin.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ plugin_public.o : plugin_public.c ../include/mosquitto_plugin.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ read_handle.o : read_handle.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ retain.o : retain.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ security.o : security.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ security_default.o : security_default.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_auth.o : send_auth.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_connect.o : ../lib/send_connect.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_disconnect.o : ../lib/send_disconnect.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_connack.o : send_connack.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_mosq.o : ../lib/send_mosq.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_publish.o : ../lib/send_publish.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_suback.o : send_suback.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_subscribe.o : ../lib/send_subscribe.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_unsuback.o : send_unsuback.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ send_unsubscribe.o : ../lib/send_unsubscribe.c ../lib/send_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ service.o : service.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ session_expiry.o : session_expiry.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ signals.o : signals.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ strings_mosq.o : ../lib/strings_mosq.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ subs.o : subs.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ sys_tree.o : sys_tree.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ time_mosq.o : ../lib/time_mosq.c ../lib/time_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ tls_mosq.o : ../lib/tls_mosq.c ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ topic_tok.o : topic_tok.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ util_mosq.o : ../lib/util_mosq.c ../lib/util_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ util_topic.o : ../lib/util_topic.c ../lib/util_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ utf8_mosq.o : ../lib/utf8_mosq.c ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ websockets.o : websockets.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ will_delay.o : will_delay.c mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ will_mosq.o : ../lib/will_mosq.c ../lib/will_mosq.h ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ xtreport.o : xtreport.c ${CROSS_COMPILE}${CC} $(BROKER_CPPFLAGS) $(BROKER_CFLAGS) -c $< -o $@ plugin_defer.so : plugin_defer.c ../include/mosquitto_plugin.h ../include/mosquitto_broker.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} -I. -I../lib -fPIC -shared $< -o $@ plugin_debug.so : plugin_debug.c ../include/mosquitto_plugin.../include/h mosquitto_broker.h mosquitto_broker_internal.h ${CROSS_COMPILE}${CC} -I. -I../lib -fPIC -shared $< -o $@ install : all $(INSTALL) -d "${DESTDIR}$(prefix)/sbin" $(INSTALL) ${STRIP_OPTS} mosquitto "${DESTDIR}${prefix}/sbin/mosquitto" $(INSTALL) -d "${DESTDIR}$(prefix)/include" $(INSTALL) ../include/mosquitto_broker.h "${DESTDIR}${prefix}/include/mosquitto_broker.h" $(INSTALL) ../include/mosquitto_plugin.h "${DESTDIR}${prefix}/include/mosquitto_plugin.h" uninstall : -rm -f "${DESTDIR}${prefix}/sbin/mosquitto" -rm -f "${DESTDIR}${prefix}/include/mosquitto_broker.h" -rm -f "${DESTDIR}${prefix}/include/mosquitto_plugin.h" clean : -rm -f *.o mosquitto *.gcda *.gcno reallyclean : clean -rm -rf *.orig *.db mosquitto-2.0.18/src/bridge.c000066400000000000000000000646661450213760600160630ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #ifndef WIN32 #include #include #else #include #include #endif #ifndef WIN32 #include #else #include #include #include #endif #include "mqtt_protocol.h" #include "mosquitto.h" #include "mosquitto_broker_internal.h" #include "mosquitto_internal.h" #include "net_mosq.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "send_mosq.h" #include "time_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" #include "will_mosq.h" #ifdef WITH_BRIDGE static void bridge__backoff_step(struct mosquitto *context); static void bridge__backoff_reset(struct mosquitto *context); void bridge__start_all(void) { int i; for(i=0; ibridge_count; i++){ if(bridge__new(&(db.config->bridges[i])) > 0){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Unable to connect to bridge %s.", db.config->bridges[i].name); } } } int bridge__new(struct mosquitto__bridge *bridge) { struct mosquitto *new_context = NULL; struct mosquitto **bridges; char *local_id; assert(bridge); local_id = mosquitto__strdup(bridge->local_clientid); HASH_FIND(hh_id, db.contexts_by_id, local_id, strlen(local_id), new_context); if(new_context){ /* (possible from persistent db) */ mosquitto__free(local_id); }else{ /* id wasn't found, so generate a new context */ new_context = context__init(INVALID_SOCKET); if(!new_context){ mosquitto__free(local_id); return MOSQ_ERR_NOMEM; } new_context->id = local_id; context__add_to_by_id(new_context); } new_context->bridge = bridge; new_context->is_bridge = true; new_context->username = new_context->bridge->remote_username; new_context->password = new_context->bridge->remote_password; #ifdef WITH_TLS new_context->tls_cafile = new_context->bridge->tls_cafile; new_context->tls_capath = new_context->bridge->tls_capath; new_context->tls_certfile = new_context->bridge->tls_certfile; new_context->tls_keyfile = new_context->bridge->tls_keyfile; new_context->tls_cert_reqs = SSL_VERIFY_PEER; new_context->tls_ocsp_required = new_context->bridge->tls_ocsp_required; new_context->tls_version = new_context->bridge->tls_version; new_context->tls_insecure = new_context->bridge->tls_insecure; new_context->tls_alpn = new_context->bridge->tls_alpn; new_context->tls_engine = db.config->default_listener.tls_engine; new_context->tls_keyform = db.config->default_listener.tls_keyform; new_context->ssl_ctx_defaults = true; #ifdef FINAL_WITH_TLS_PSK new_context->tls_psk_identity = new_context->bridge->tls_psk_identity; new_context->tls_psk = new_context->bridge->tls_psk; #endif #endif bridge->try_private_accepted = true; if(bridge->clean_start_local == -1){ /* default to "regular" clean start setting */ bridge->clean_start_local = bridge->clean_start; } new_context->retain_available = bridge->outgoing_retain; new_context->protocol = bridge->protocol_version; if(!bridge->clean_start_local){ new_context->session_expiry_interval = UINT32_MAX; } bridges = mosquitto__realloc(db.bridges, (size_t)(db.bridge_count+1)*sizeof(struct mosquitto *)); if(bridges){ db.bridges = bridges; db.bridge_count++; db.bridges[db.bridge_count-1] = new_context; }else{ return MOSQ_ERR_NOMEM; } #if defined(__GLIBC__) && defined(WITH_ADNS) new_context->bridge->restart_t = 1; /* force quick restart of bridge */ return bridge__connect_step1(new_context); #else return bridge__connect(new_context); #endif } #if defined(__GLIBC__) && defined(WITH_ADNS) int bridge__connect_step1(struct mosquitto *context) { int rc; char *notification_topic; size_t notification_topic_len; uint8_t notification_payload; int i; uint8_t qos; if(!context || !context->bridge) return MOSQ_ERR_INVAL; mosquitto__set_state(context, mosq_cs_new); context->sock = INVALID_SOCKET; context->last_msg_in = db.now_s; context->next_msg_out = db.now_s + context->bridge->keepalive; context->keepalive = context->bridge->keepalive; context->clean_start = context->bridge->clean_start; context->in_packet.payload = NULL; context->ping_t = 0; context->bridge->lazy_reconnect = false; context->maximum_packet_size = context->bridge->maximum_packet_size; bridge__packet_cleanup(context); db__message_reconnect_reset(context); db__messages_delete(context, false); /* Delete all local subscriptions even for clean_start==false. We don't * remove any messages and the next loop carries out the resubscription * anyway. This means any unwanted subs will be removed. */ sub__clean_session(context); for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ log__printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic); if(context->bridge->topics[i].qos > context->max_qos){ qos = context->max_qos; }else{ qos = context->bridge->topics[i].qos; } if(sub__add(context, context->bridge->topics[i].local_topic, qos, 0, MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, &db.subs) > 0){ return 1; } retain__queue(context, context->bridge->topics[i].local_topic, qos, 0); } } /* prepare backoff for a possible failure. Restart timeout will be reset if connection gets established */ bridge__backoff_step(context); if(context->bridge->notifications){ if(context->max_qos == 0){ qos = 0; }else{ qos = 1; } if(context->bridge->notification_topic){ if(!context->bridge->initial_notification_done){ notification_payload = '0'; db__messages_easy_queue(context, context->bridge->notification_topic, qos, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } notification_payload = '0'; rc = will__set(context, context->bridge->notification_topic, 1, ¬ification_payload, qos, true, NULL); if(rc != MOSQ_ERR_SUCCESS){ return rc; } }else{ notification_topic_len = strlen(context->bridge->remote_clientid)+strlen("$SYS/broker/connection//state"); notification_topic = mosquitto__malloc(sizeof(char)*(notification_topic_len+1)); if(!notification_topic) return MOSQ_ERR_NOMEM; snprintf(notification_topic, notification_topic_len+1, "$SYS/broker/connection/%s/state", context->bridge->remote_clientid); if(!context->bridge->initial_notification_done){ notification_payload = '0'; db__messages_easy_queue(context, notification_topic, qos, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } notification_payload = '0'; rc = will__set(context, notification_topic, 1, ¬ification_payload, qos, true, NULL); mosquitto__free(notification_topic); if(rc != MOSQ_ERR_SUCCESS){ return rc; } } } log__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); rc = net__try_connect_step1(context, context->bridge->addresses[context->bridge->cur_address].address); if(rc > 0 ){ if(rc == MOSQ_ERR_TLS){ mux__delete(context); net__socket_close(context); return rc; /* Error already printed */ }else if(rc == MOSQ_ERR_ERRNO){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc == MOSQ_ERR_EAI){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } return rc; } return MOSQ_ERR_SUCCESS; } int bridge__connect_step2(struct mosquitto *context) { int rc; if(!context || !context->bridge) return MOSQ_ERR_INVAL; log__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); rc = net__try_connect_step2(context, context->bridge->addresses[context->bridge->cur_address].port, &context->sock); if(rc > 0){ if(rc == MOSQ_ERR_TLS){ mux__delete(context); net__socket_close(context); return rc; /* Error already printed */ }else if(rc == MOSQ_ERR_ERRNO){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc == MOSQ_ERR_EAI){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } return rc; } HASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context); if(rc == MOSQ_ERR_CONN_PENDING){ mosquitto__set_state(context, mosq_cs_connect_pending); mux__add_out(context); } return rc; } int bridge__connect_step3(struct mosquitto *context) { int rc; rc = net__socket_connect_step3(context, context->bridge->addresses[context->bridge->cur_address].address); if(rc > 0){ if(rc == MOSQ_ERR_TLS){ mux__delete(context); net__socket_close(context); return rc; /* Error already printed */ }else if(rc == MOSQ_ERR_ERRNO){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc == MOSQ_ERR_EAI){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } return rc; } if(context->bridge->round_robin == false && context->bridge->cur_address != 0){ context->bridge->primary_retry = db.now_s + 5; } rc = send__connect(context, context->keepalive, context->clean_start, NULL); if(rc == MOSQ_ERR_SUCCESS){ return MOSQ_ERR_SUCCESS; }else if(rc == MOSQ_ERR_ERRNO && errno == ENOTCONN){ return MOSQ_ERR_SUCCESS; }else{ if(rc == MOSQ_ERR_TLS){ return rc; /* Error already printed */ }else if(rc == MOSQ_ERR_ERRNO){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc == MOSQ_ERR_EAI){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } mux__delete(context); net__socket_close(context); return rc; } } #else int bridge__connect(struct mosquitto *context) { int rc, rc2; int i; char *notification_topic = NULL; size_t notification_topic_len; uint8_t notification_payload; uint8_t qos; if(!context || !context->bridge) return MOSQ_ERR_INVAL; mosquitto__set_state(context, mosq_cs_new); context->sock = INVALID_SOCKET; context->last_msg_in = db.now_s; context->next_msg_out = db.now_s + context->bridge->keepalive; context->keepalive = context->bridge->keepalive; context->clean_start = context->bridge->clean_start; context->in_packet.payload = NULL; context->ping_t = 0; context->bridge->lazy_reconnect = false; context->maximum_packet_size = context->bridge->maximum_packet_size; bridge__packet_cleanup(context); db__message_reconnect_reset(context); db__messages_delete(context, false); /* Delete all local subscriptions even for clean_start==false. We don't * remove any messages and the next loop carries out the resubscription * anyway. This means any unwanted subs will be removed. */ sub__clean_session(context); for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ log__printf(NULL, MOSQ_LOG_DEBUG, "Bridge %s doing local SUBSCRIBE on topic %s", context->id, context->bridge->topics[i].local_topic); if(context->bridge->topics[i].qos > context->max_qos){ qos = context->max_qos; }else{ qos = context->bridge->topics[i].qos; } if(sub__add(context, context->bridge->topics[i].local_topic, qos, 0, MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, &db.subs) > 0){ return 1; } } } /* prepare backoff for a possible failure. Restart timeout will be reset if connection gets established */ bridge__backoff_step(context); if(context->bridge->notifications){ if(context->max_qos == 0){ qos = 0; }else{ qos = 1; } if(context->bridge->notification_topic){ if(!context->bridge->initial_notification_done){ notification_payload = '0'; db__messages_easy_queue(context, context->bridge->notification_topic, qos, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } notification_payload = '0'; rc = will__set(context, context->bridge->notification_topic, 1, ¬ification_payload, qos, true, NULL); if(rc != MOSQ_ERR_SUCCESS){ return rc; } }else{ notification_topic_len = strlen(context->bridge->remote_clientid)+strlen("$SYS/broker/connection//state"); notification_topic = mosquitto__malloc(sizeof(char)*(notification_topic_len+1)); if(!notification_topic) return MOSQ_ERR_NOMEM; snprintf(notification_topic, notification_topic_len+1, "$SYS/broker/connection/%s/state", context->bridge->remote_clientid); if(!context->bridge->initial_notification_done){ notification_payload = '0'; db__messages_easy_queue(context, notification_topic, qos, 1, ¬ification_payload, 1, 0, NULL); context->bridge->initial_notification_done = true; } notification_payload = '0'; rc = will__set(context, notification_topic, 1, ¬ification_payload, qos, true, NULL); if(rc != MOSQ_ERR_SUCCESS){ mosquitto__free(notification_topic); return rc; } mosquitto__free(notification_topic); } } log__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); rc = net__socket_connect(context, context->bridge->addresses[context->bridge->cur_address].address, context->bridge->addresses[context->bridge->cur_address].port, context->bridge->bind_address, false); if(rc > 0){ if(rc == MOSQ_ERR_TLS){ mux__delete(context); net__socket_close(context); return rc; /* Error already printed */ }else if(rc == MOSQ_ERR_ERRNO){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc == MOSQ_ERR_EAI){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } return rc; }else if(rc == MOSQ_ERR_CONN_PENDING){ mosquitto__set_state(context, mosq_cs_connect_pending); mux__add_out(context); } HASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context); rc2 = send__connect(context, context->keepalive, context->clean_start, NULL); if(rc2 == MOSQ_ERR_SUCCESS){ return rc; }else if(rc2 == MOSQ_ERR_ERRNO && errno == ENOTCONN){ return MOSQ_ERR_SUCCESS; }else{ if(rc2 == MOSQ_ERR_TLS){ return rc2; /* Error already printed */ }else if(rc2 == MOSQ_ERR_ERRNO){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", strerror(errno)); }else if(rc2 == MOSQ_ERR_EAI){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating bridge: %s.", gai_strerror(errno)); } mux__delete(context); net__socket_close(context); return rc2; } } #endif int bridge__on_connect(struct mosquitto *context) { int i; char *notification_topic; size_t notification_topic_len; char notification_payload; int sub_opts; bool retain = true; uint8_t qos; if(context->bridge->notifications){ if(context->max_qos == 0){ qos = 0; }else{ qos = 1; } if(!context->retain_available){ retain = false; } notification_payload = '1'; if(context->bridge->notification_topic){ if(!context->bridge->notifications_local_only){ if(send__real_publish(context, mosquitto__mid_generate(context), context->bridge->notification_topic, 1, ¬ification_payload, qos, retain, 0, NULL, NULL, 0)){ return 1; } } db__messages_easy_queue(context, context->bridge->notification_topic, qos, 1, ¬ification_payload, 1, 0, NULL); }else{ notification_topic_len = strlen(context->bridge->remote_clientid)+strlen("$SYS/broker/connection//state"); notification_topic = mosquitto__malloc(sizeof(char)*(notification_topic_len+1)); if(!notification_topic) return MOSQ_ERR_NOMEM; snprintf(notification_topic, notification_topic_len+1, "$SYS/broker/connection/%s/state", context->bridge->remote_clientid); notification_payload = '1'; if(!context->bridge->notifications_local_only){ if(send__real_publish(context, mosquitto__mid_generate(context), notification_topic, 1, ¬ification_payload, qos, retain, 0, NULL, NULL, 0)){ mosquitto__free(notification_topic); return 1; } } db__messages_easy_queue(context, notification_topic, qos, 1, ¬ification_payload, 1, 0, NULL); mosquitto__free(notification_topic); } } for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_in || context->bridge->topics[i].direction == bd_both){ if(context->bridge->topics[i].qos > context->max_qos){ sub_opts = context->max_qos; }else{ sub_opts = context->bridge->topics[i].qos; } if(context->bridge->protocol_version == mosq_p_mqtt5){ sub_opts = sub_opts | MQTT_SUB_OPT_NO_LOCAL | MQTT_SUB_OPT_RETAIN_AS_PUBLISHED | MQTT_SUB_OPT_SEND_RETAIN_ALWAYS; } if(send__subscribe(context, NULL, 1, &context->bridge->topics[i].remote_topic, sub_opts, NULL)){ return 1; } }else{ if(context->bridge->attempt_unsubscribe){ if(send__unsubscribe(context, NULL, 1, &context->bridge->topics[i].remote_topic, NULL)){ /* direction = inwards only. This means we should not be subscribed * to the topic. It is possible that we used to be subscribed to * this topic so unsubscribe. */ return 1; } } } } for(i=0; ibridge->topic_count; i++){ if(context->bridge->topics[i].direction == bd_out || context->bridge->topics[i].direction == bd_both){ if(context->bridge->topics[i].qos > context->max_qos){ qos = context->max_qos; }else{ qos = context->bridge->topics[i].qos; } retain__queue(context, context->bridge->topics[i].local_topic, qos, 0); } } bridge__backoff_reset(context); return MOSQ_ERR_SUCCESS; } int bridge__register_local_connections(void) { struct mosquitto *context, *ctxt_tmp = NULL; HASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){ if(context->bridge){ if(mux__add_in(context)){ log__printf(NULL, MOSQ_LOG_ERR, "Error in initial bridge registration: %s", strerror(errno)); return MOSQ_ERR_UNKNOWN; } mux__add_out(context); } } return MOSQ_ERR_SUCCESS; } void bridge__cleanup(struct mosquitto *context) { int i; for(i=0; ibridge->local_clientid); context->bridge->local_clientid = NULL; mosquitto__free(context->bridge->local_username); context->bridge->local_username = NULL; mosquitto__free(context->bridge->local_password); context->bridge->local_password = NULL; if(context->bridge->remote_clientid != context->id){ mosquitto__free(context->bridge->remote_clientid); } context->bridge->remote_clientid = NULL; if(context->bridge->remote_username != context->username){ mosquitto__free(context->bridge->remote_username); } context->bridge->remote_username = NULL; if(context->bridge->remote_password != context->password){ mosquitto__free(context->bridge->remote_password); } context->bridge->remote_password = NULL; #ifdef WITH_TLS if(context->ssl_ctx){ SSL_CTX_free(context->ssl_ctx); context->ssl_ctx = NULL; } #endif } void bridge__packet_cleanup(struct mosquitto *context) { struct mosquitto__packet *packet; if(!context) return; if(context->current_out_packet){ packet__cleanup(context->current_out_packet); mosquitto__free(context->current_out_packet); context->current_out_packet = NULL; } while(context->out_packet){ packet__cleanup(context->out_packet); packet = context->out_packet; context->out_packet = context->out_packet->next; mosquitto__free(packet); } context->out_packet = NULL; context->out_packet_last = NULL; context->out_packet_count = 0; packet__cleanup(&(context->in_packet)); } static int rand_between(int low, int high) { int r; util__random_bytes(&r, sizeof(int)); return (abs(r) % (high - low)) + low; } static void bridge__backoff_step(struct mosquitto *context) { struct mosquitto__bridge *bridge; if(!context || !context->bridge) return; bridge = context->bridge; /* skip if not using backoff */ if(bridge->backoff_cap){ /* “Decorrelated Jitter” calculation, according to: * https://aws.amazon.com/blogs/architecture/exponential-backoff-and-jitter/ */ bridge->restart_timeout = rand_between(bridge->backoff_base, bridge->restart_timeout * 3); if(bridge->restart_timeout > bridge->backoff_cap){ bridge->restart_timeout = bridge->backoff_cap; } } } static void bridge__backoff_reset(struct mosquitto *context) { struct mosquitto__bridge *bridge; if(!context || !context->bridge) return; bridge = context->bridge; /* skip if not using backoff */ if(bridge->backoff_cap){ bridge->restart_timeout = bridge->backoff_base; } } static void bridge_check_pending(struct mosquitto *context) { int err; socklen_t len; if(context->state == mosq_cs_connect_pending){ len = sizeof(int); if(!getsockopt(context->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){ if(err == 0){ mosquitto__set_state(context, mosq_cs_new); #if defined(WITH_ADNS) && defined(WITH_BRIDGE) if(context->bridge){ bridge__connect_step3(context); } #endif }else if(err == ECONNREFUSED){ do_disconnect(context, MOSQ_ERR_CONN_LOST); return; } }else{ do_disconnect(context, MOSQ_ERR_CONN_LOST); return; } } } void bridge_check(void) { static time_t last_check = 0; struct mosquitto *context = NULL; socklen_t len; int i; int rc; int err; if(db.now_s <= last_check) return; for(i=0; isock != INVALID_SOCKET){ mosquitto__check_keepalive(context); bridge_check_pending(context); /* Check for bridges that are not round robin and not currently * connected to their primary broker. */ if(context->bridge->round_robin == false && context->bridge->cur_address != 0 && context->bridge->primary_retry && db.now_s > context->bridge->primary_retry){ if(context->bridge->primary_retry_sock == INVALID_SOCKET){ rc = net__try_connect(context->bridge->addresses[0].address, context->bridge->addresses[0].port, &context->bridge->primary_retry_sock, context->bridge->bind_address, false); if(rc == 0){ COMPAT_CLOSE(context->bridge->primary_retry_sock); context->bridge->primary_retry_sock = INVALID_SOCKET; context->bridge->primary_retry = 0; mux__delete(context); net__socket_close(context); context->bridge->cur_address = 0; } }else{ len = sizeof(int); if(!getsockopt(context->bridge->primary_retry_sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){ if(err == 0){ COMPAT_CLOSE(context->bridge->primary_retry_sock); context->bridge->primary_retry_sock = INVALID_SOCKET; context->bridge->primary_retry = 0; mux__delete(context); net__socket_close(context); context->bridge->cur_address = context->bridge->address_count-1; }else{ COMPAT_CLOSE(context->bridge->primary_retry_sock); context->bridge->primary_retry_sock = INVALID_SOCKET; context->bridge->primary_retry = db.now_s+5; } }else{ COMPAT_CLOSE(context->bridge->primary_retry_sock); context->bridge->primary_retry_sock = INVALID_SOCKET; context->bridge->primary_retry = db.now_s+5; } } } } if(context->sock == INVALID_SOCKET){ /* Want to try to restart the bridge connection */ if(!context->bridge->restart_t){ context->bridge->restart_t = db.now_s+context->bridge->restart_timeout; context->bridge->cur_address++; if(context->bridge->cur_address == context->bridge->address_count){ context->bridge->cur_address = 0; } }else{ if((context->bridge->start_type == bst_lazy && context->bridge->lazy_reconnect) || (context->bridge->start_type == bst_automatic && db.now_s > context->bridge->restart_t)){ #if defined(__GLIBC__) && defined(WITH_ADNS) if(context->adns){ /* Connection attempted, waiting on DNS lookup */ rc = gai_error(context->adns); if(rc == EAI_INPROGRESS){ /* Just keep on waiting */ }else if(rc == 0){ rc = bridge__connect_step2(context); if(rc == MOSQ_ERR_SUCCESS){ mux__add_in(context); if(context->current_out_packet){ mux__add_out(context); } }else if(rc == MOSQ_ERR_CONN_PENDING){ mux__add_in(context); mux__add_out(context); context->bridge->restart_t = 0; }else{ context->bridge->cur_address++; if(context->bridge->cur_address == context->bridge->address_count){ context->bridge->cur_address = 0; } context->bridge->restart_t = 0; } }else{ /* Need to retry */ if(context->adns->ar_result){ freeaddrinfo(context->adns->ar_result); } mosquitto__free(context->adns); context->adns = NULL; context->bridge->restart_t = 0; } }else{ rc = bridge__connect_step1(context); if(rc){ context->bridge->cur_address++; if(context->bridge->cur_address == context->bridge->address_count){ context->bridge->cur_address = 0; } }else{ /* Short wait for ADNS lookup */ context->bridge->restart_t = 1; } } #else { rc = bridge__connect(context); context->bridge->restart_t = 0; if(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_CONN_PENDING){ if(context->bridge->round_robin == false && context->bridge->cur_address != 0){ context->bridge->primary_retry = db.now_s + 5; } mux__add_in(context); if(context->current_out_packet){ mux__add_out(context); } }else{ context->bridge->cur_address++; if(context->bridge->cur_address == context->bridge->address_count){ context->bridge->cur_address = 0; } } } #endif } } } } } #endif mosquitto-2.0.18/src/bridge_topic.c000066400000000000000000000153741450213760600172510ustar00rootroot00000000000000/* Copyright (c) 2009-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto.h" #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #ifdef WITH_BRIDGE static int bridge__create_remap_topic(const char *prefix, const char *topic, char **remap_topic) { size_t len; if(prefix){ if(topic){ len = strlen(topic) + strlen(prefix)+1; *remap_topic = mosquitto__malloc(len+1); if(!(*remap_topic)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } snprintf(*remap_topic, len+1, "%s%s", prefix, topic); (*remap_topic)[len] = '\0'; }else{ *remap_topic = mosquitto__strdup(prefix); if(!(*remap_topic)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } } }else{ *remap_topic = mosquitto__strdup(topic); if(!(*remap_topic)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } } return MOSQ_ERR_SUCCESS; } static int bridge__create_prefix(char **full_prefix, const char *topic, const char *prefix, const char *direction) { size_t len; if(mosquitto_pub_topic_check(prefix) != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic local prefix '%s'.", prefix); return MOSQ_ERR_INVAL; } if(topic){ len = strlen(topic) + strlen(prefix) + 1; }else{ len = strlen(prefix) + 1; } *full_prefix = mosquitto__malloc(len); if(*full_prefix == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } if(topic){ /* Print full_prefix+pattern to check for validity */ snprintf(*full_prefix, len, "%s%s", prefix, topic); }else{ snprintf(*full_prefix, len, "%s", prefix); } if(mosquitto_sub_topic_check(*full_prefix) != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic %s prefix and pattern combination '%s'.", direction, *full_prefix); return MOSQ_ERR_INVAL; } /* Print just the prefix for storage */ snprintf(*full_prefix, len, "%s", prefix); return MOSQ_ERR_SUCCESS; } /* topic [[[out | in | both] qos-level] local-prefix remote-prefix] */ int 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) { struct mosquitto__bridge_topic *topics; struct mosquitto__bridge_topic *cur_topic; if(bridge == NULL) return MOSQ_ERR_INVAL; if(direction != bd_out && direction != bd_in && direction != bd_both){ return MOSQ_ERR_INVAL; } if(qos > 2){ return MOSQ_ERR_INVAL; } if(local_prefix && mosquitto_pub_topic_check(local_prefix)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic local prefix '%s'.", local_prefix); return MOSQ_ERR_INVAL; } if(remote_prefix && mosquitto_pub_topic_check(remote_prefix)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic remote prefix '%s'.", remote_prefix); return MOSQ_ERR_INVAL; } if((topic == NULL || !strcmp(topic, "\"\"")) && (local_prefix == NULL || remote_prefix == NULL)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge remapping."); return MOSQ_ERR_INVAL; } bridge->topic_count++; topics = mosquitto__realloc(bridge->topics, sizeof(struct mosquitto__bridge_topic)*(size_t)bridge->topic_count); if(topics == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } bridge->topics = topics; cur_topic = &bridge->topics[bridge->topic_count-1]; cur_topic->direction = direction; cur_topic->qos = qos; cur_topic->local_prefix = NULL; cur_topic->remote_prefix = NULL; if(topic == NULL || !strcmp(topic, "\"\"")){ cur_topic->topic = NULL; }else{ cur_topic->topic = mosquitto__strdup(topic); if(cur_topic->topic == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } } if(local_prefix || remote_prefix){ bridge->topic_remapping = true; if(local_prefix){ if(bridge__create_prefix(&cur_topic->local_prefix, cur_topic->topic, local_prefix, "local")){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } } if(remote_prefix){ if(bridge__create_prefix(&cur_topic->remote_prefix, cur_topic->topic, remote_prefix, "local")){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } } } if(bridge__create_remap_topic(cur_topic->local_prefix, cur_topic->topic, &cur_topic->local_topic)){ return MOSQ_ERR_INVAL; } if(bridge__create_remap_topic(cur_topic->remote_prefix, cur_topic->topic, &cur_topic->remote_topic)){ return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } int bridge__remap_topic_in(struct mosquitto *context, char **topic) { struct mosquitto__bridge_topic *cur_topic; char *topic_temp; int i; size_t len; int rc; bool match; if(context->bridge && context->bridge->topics && context->bridge->topic_remapping){ for(i=0; ibridge->topic_count; i++){ cur_topic = &context->bridge->topics[i]; if((cur_topic->direction == bd_both || cur_topic->direction == bd_in) && (cur_topic->remote_prefix || cur_topic->local_prefix)){ /* Topic mapping required on this topic if the message matches */ rc = mosquitto_topic_matches_sub(cur_topic->remote_topic, *topic, &match); if(rc){ mosquitto__free(*topic); return rc; } if(match){ if(cur_topic->remote_prefix){ /* This prefix needs removing. */ if(!strncmp(cur_topic->remote_prefix, *topic, strlen(cur_topic->remote_prefix))){ topic_temp = mosquitto__strdup((*topic)+strlen(cur_topic->remote_prefix)); if(!topic_temp){ mosquitto__free(*topic); return MOSQ_ERR_NOMEM; } mosquitto__free(*topic); *topic = topic_temp; } } if(cur_topic->local_prefix){ /* This prefix needs adding. */ len = strlen(*topic) + strlen(cur_topic->local_prefix)+1; topic_temp = mosquitto__malloc(len+1); if(!topic_temp){ mosquitto__free(*topic); return MOSQ_ERR_NOMEM; } snprintf(topic_temp, len, "%s%s", cur_topic->local_prefix, *topic); topic_temp[len] = '\0'; mosquitto__free(*topic); *topic = topic_temp; } break; } } } } return MOSQ_ERR_SUCCESS; } #endif mosquitto-2.0.18/src/conf.c000066400000000000000000002727251450213760600155510ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #ifdef WIN32 #else # include # include #endif #ifndef WIN32 # include # include #else # include # include #endif #if !defined(WIN32) && !defined(__CYGWIN__) # include #endif #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "misc_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" #include "mqtt_protocol.h" struct config_recurse { unsigned int log_dest; int log_dest_set; unsigned int log_type; int log_type_set; }; #if defined(WIN32) || defined(__CYGWIN__) #include extern SERVICE_STATUS_HANDLE service_handle; #endif static struct mosquitto__security_options *cur_security_options = NULL; static int conf__parse_bool(char **token, const char *name, bool *value, char *saveptr); static int conf__parse_int(char **token, const char *name, int *value, char *saveptr); static int conf__parse_ssize_t(char **token, const char *name, ssize_t *value, char *saveptr); static int conf__parse_string(char **token, const char *name, char **value, char *saveptr); static int config__read_file(struct mosquitto__config *config, bool reload, const char *file, struct config_recurse *config_tmp, int level, int *lineno); static int config__check(struct mosquitto__config *config); static void config__cleanup_plugins(struct mosquitto__config *config); static void conf__set_cur_security_options(struct mosquitto__config *config, struct mosquitto__listener *cur_listener, struct mosquitto__security_options **security_options) { if(config->per_listener_settings){ (*security_options) = &cur_listener->security_options; }else{ (*security_options) = &config->security_options; } } static int conf__attempt_resolve(const char *host, const char *text, unsigned int log, const char *msg) { struct addrinfo gai_hints; struct addrinfo *gai_res; int rc; memset(&gai_hints, 0, sizeof(struct addrinfo)); gai_hints.ai_family = AF_UNSPEC; gai_hints.ai_socktype = SOCK_STREAM; gai_res = NULL; rc = getaddrinfo(host, NULL, &gai_hints, &gai_res); if(gai_res){ freeaddrinfo(gai_res); } if(rc != 0){ #ifndef WIN32 if(rc == EAI_SYSTEM){ if(errno == ENOENT){ log__printf(NULL, log, "%s: Unable to resolve %s %s.", msg, text, host); }else{ log__printf(NULL, log, "%s: Error resolving %s: %s.", msg, text, strerror(errno)); } }else{ log__printf(NULL, log, "%s: Error resolving %s: %s.", msg, text, gai_strerror(rc)); } #else if(rc == WSAHOST_NOT_FOUND){ log__printf(NULL, log, "%s: Error resolving %s.", msg, text); } #endif return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } static void config__init_reload(struct mosquitto__config *config) { int i; /* Set defaults */ for(i=0; ilistener_count; i++){ mosquitto__free(config->listeners[i].security_options.acl_file); config->listeners[i].security_options.acl_file = NULL; mosquitto__free(config->listeners[i].security_options.password_file); config->listeners[i].security_options.password_file = NULL; mosquitto__free(config->listeners[i].security_options.psk_file); config->listeners[i].security_options.psk_file = NULL; config->listeners[i].security_options.allow_anonymous = -1; config->listeners[i].security_options.allow_zero_length_clientid = true; config->listeners[i].security_options.auto_id_prefix = NULL; config->listeners[i].security_options.auto_id_prefix_len = 0; } config->local_only = true; config->allow_duplicate_messages = false; mosquitto__free(config->security_options.acl_file); config->security_options.acl_file = NULL; config->security_options.allow_anonymous = -1; config->security_options.allow_zero_length_clientid = true; config->security_options.auto_id_prefix = NULL; config->security_options.auto_id_prefix_len = 0; mosquitto__free(config->security_options.password_file); config->security_options.password_file = NULL; mosquitto__free(config->security_options.psk_file); config->security_options.psk_file = NULL; config->autosave_interval = 1800; config->autosave_on_changes = false; mosquitto__free(config->clientid_prefixes); config->connection_messages = true; config->clientid_prefixes = NULL; config->per_listener_settings = false; if(config->log_fptr){ fclose(config->log_fptr); config->log_fptr = NULL; } mosquitto__free(config->log_file); config->log_file = NULL; #if defined(WIN32) || defined(__CYGWIN__) if(service_handle){ /* This is running as a Windows service. Default to no logging. Using * stdout/stderr is forbidden because the first clients to connect will * get log information sent to them for some reason. */ config->log_dest = MQTT3_LOG_NONE; }else{ config->log_dest = MQTT3_LOG_STDERR; } #else config->log_facility = LOG_DAEMON; config->log_dest = MQTT3_LOG_STDERR | MQTT3_LOG_DLT; if(db.verbose){ config->log_type = UINT_MAX; }else{ config->log_type = MOSQ_LOG_ERR | MOSQ_LOG_WARNING | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO; } #endif config->log_timestamp = true; mosquitto__free(config->log_timestamp_format); config->log_timestamp_format = NULL; config->max_keepalive = 0; config->max_packet_size = 0; config->max_inflight_messages = 20; config->max_queued_messages = 1000; config->max_inflight_bytes = 0; config->max_queued_bytes = 0; config->persistence = false; mosquitto__free(config->persistence_location); config->persistence_location = NULL; mosquitto__free(config->persistence_file); config->persistence_file = NULL; config->persistent_client_expiration = 0; config->queue_qos0_messages = false; config->retain_available = true; config->set_tcp_nodelay = false; config->sys_interval = 10; config->upgrade_outgoing_qos = false; config__cleanup_plugins(config); } static void config__cleanup_plugins(struct mosquitto__config *config) { int i, j; struct mosquitto__auth_plugin_config *plug; if(config->security_options.auth_plugin_configs){ for(i=0; isecurity_options.auth_plugin_config_count; i++){ plug = &config->security_options.auth_plugin_configs[i]; mosquitto__free(plug->path); plug->path = NULL; if(plug->options){ for(j=0; joption_count; j++){ mosquitto__free(plug->options[j].key); mosquitto__free(plug->options[j].value); } mosquitto__free(plug->options); plug->options = NULL; plug->option_count = 0; } } mosquitto__free(config->security_options.auth_plugin_configs); config->security_options.auth_plugin_configs = NULL; } } void config__init(struct mosquitto__config *config) { memset(config, 0, sizeof(struct mosquitto__config)); config__init_reload(config); config->daemon = false; memset(&config->default_listener, 0, sizeof(struct mosquitto__listener)); listener__set_defaults(&config->default_listener); } void config__cleanup(struct mosquitto__config *config) { int i; #ifdef WITH_BRIDGE int j; #endif mosquitto__free(config->clientid_prefixes); mosquitto__free(config->persistence_location); mosquitto__free(config->persistence_file); mosquitto__free(config->persistence_filepath); mosquitto__free(config->security_options.auto_id_prefix); mosquitto__free(config->security_options.acl_file); mosquitto__free(config->security_options.password_file); mosquitto__free(config->security_options.psk_file); mosquitto__free(config->pid_file); mosquitto__free(config->user); mosquitto__free(config->log_timestamp_format); if(config->listeners){ for(i=0; ilistener_count; i++){ mosquitto__free(config->listeners[i].host); mosquitto__free(config->listeners[i].bind_interface); mosquitto__free(config->listeners[i].mount_point); mosquitto__free(config->listeners[i].socks); mosquitto__free(config->listeners[i].security_options.auto_id_prefix); mosquitto__free(config->listeners[i].security_options.acl_file); mosquitto__free(config->listeners[i].security_options.password_file); mosquitto__free(config->listeners[i].security_options.psk_file); #ifdef WITH_TLS mosquitto__free(config->listeners[i].cafile); mosquitto__free(config->listeners[i].capath); mosquitto__free(config->listeners[i].certfile); mosquitto__free(config->listeners[i].keyfile); mosquitto__free(config->listeners[i].ciphers); mosquitto__free(config->listeners[i].ciphers_tls13); mosquitto__free(config->listeners[i].psk_hint); mosquitto__free(config->listeners[i].crlfile); mosquitto__free(config->listeners[i].dhparamfile); mosquitto__free(config->listeners[i].tls_version); mosquitto__free(config->listeners[i].tls_engine); mosquitto__free(config->listeners[i].tls_engine_kpass_sha1); #ifdef WITH_WEBSOCKETS if(!config->listeners[i].ws_context) /* libwebsockets frees its own SSL_CTX */ #endif { SSL_CTX_free(config->listeners[i].ssl_ctx); } #endif #ifdef WITH_WEBSOCKETS mosquitto__free(config->listeners[i].http_dir); #endif #ifdef WITH_UNIX_SOCKETS mosquitto__free(config->listeners[i].unix_socket_path); #endif } mosquitto__free(config->listeners); } #ifdef WITH_BRIDGE if(config->bridges){ for(i=0; ibridge_count; i++){ mosquitto__free(config->bridges[i].name); if(config->bridges[i].addresses){ for(j=0; jbridges[i].address_count; j++){ mosquitto__free(config->bridges[i].addresses[j].address); } mosquitto__free(config->bridges[i].addresses); } mosquitto__free(config->bridges[i].remote_clientid); mosquitto__free(config->bridges[i].remote_username); mosquitto__free(config->bridges[i].remote_password); mosquitto__free(config->bridges[i].local_clientid); mosquitto__free(config->bridges[i].local_username); mosquitto__free(config->bridges[i].local_password); if(config->bridges[i].topics){ for(j=0; jbridges[i].topic_count; j++){ mosquitto__free(config->bridges[i].topics[j].topic); mosquitto__free(config->bridges[i].topics[j].local_prefix); mosquitto__free(config->bridges[i].topics[j].remote_prefix); mosquitto__free(config->bridges[i].topics[j].local_topic); mosquitto__free(config->bridges[i].topics[j].remote_topic); } mosquitto__free(config->bridges[i].topics); } mosquitto__free(config->bridges[i].notification_topic); #ifdef WITH_TLS mosquitto__free(config->bridges[i].tls_version); mosquitto__free(config->bridges[i].tls_cafile); mosquitto__free(config->bridges[i].tls_alpn); #ifdef FINAL_WITH_TLS_PSK mosquitto__free(config->bridges[i].tls_psk_identity); mosquitto__free(config->bridges[i].tls_psk); #endif #endif } mosquitto__free(config->bridges); } #endif config__cleanup_plugins(config); if(config->log_fptr){ fclose(config->log_fptr); config->log_fptr = NULL; } if(config->log_file){ mosquitto__free(config->log_file); config->log_file = NULL; } } static void print_usage(void) { printf("mosquitto version %s\n\n", VERSION); printf("mosquitto is an MQTT v5.0/v3.1.1/v3.1 broker.\n\n"); printf("Usage: mosquitto [-c config_file] [-d] [-h] [-p port]\n\n"); printf(" -c : specify the broker config file.\n"); printf(" -d : put the broker into the background after starting.\n"); printf(" -h : display this help.\n"); printf(" -p : start the broker listening on the specified port.\n"); printf(" Not recommended in conjunction with the -c option.\n"); printf(" -v : verbose mode - enable all logging types. This overrides\n"); printf(" any logging options given in the config file.\n"); printf("\nSee https://mosquitto.org/ for more information.\n\n"); } int config__parse_args(struct mosquitto__config *config, int argc, char *argv[]) { int i; int port_tmp; for(i=1; idaemon = true; }else if(!strcmp(argv[i], "-h") || !strcmp(argv[i], "--help")){ print_usage(); return MOSQ_ERR_INVAL; }else if(!strcmp(argv[i], "-p") || !strcmp(argv[i], "--port")){ if(iUINT16_MAX){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid port specified (%d).", port_tmp); return MOSQ_ERR_INVAL; }else{ if(config->cmd_port_count == CMD_PORT_LIMIT){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Only %d ports can be specified on the command line.", CMD_PORT_LIMIT); return MOSQ_ERR_INVAL; } config->cmd_port[config->cmd_port_count] = (uint16_t)port_tmp; config->cmd_port_count++; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: -p argument given, but no port specified."); return MOSQ_ERR_INVAL; } i++; }else if(!strcmp(argv[i], "-v") || !strcmp(argv[i], "--verbose")){ db.verbose = true; }else{ fprintf(stderr, "Error: Unknown option '%s'.\n",argv[i]); print_usage(); return MOSQ_ERR_INVAL; } } if(config->default_listener.bind_interface #ifdef WITH_TLS || config->default_listener.cafile || config->default_listener.capath || config->default_listener.certfile || config->default_listener.keyfile || config->default_listener.tls_engine || config->default_listener.tls_keyform != mosq_k_pem || config->default_listener.tls_engine_kpass_sha1 || config->default_listener.ciphers || config->default_listener.ciphers_tls13 || config->default_listener.dhparamfile || config->default_listener.psk_hint || config->default_listener.require_certificate || config->default_listener.crlfile || config->default_listener.use_identity_as_username || config->default_listener.use_subject_as_username #endif || config->default_listener.use_username_as_clientid || config->default_listener.host || config->default_listener.port || config->default_listener.max_connections != -1 || config->default_listener.max_qos != 2 || config->default_listener.mount_point || config->default_listener.protocol != mp_mqtt || config->default_listener.socket_domain || config->default_listener.security_options.password_file || config->default_listener.security_options.psk_file || config->default_listener.security_options.auth_plugin_config_count || config->default_listener.security_options.allow_zero_length_clientid != true ){ config->listener_count++; config->listeners = mosquitto__realloc(config->listeners, sizeof(struct mosquitto__listener)*(size_t)config->listener_count); if(!config->listeners){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } memset(&config->listeners[config->listener_count-1], 0, sizeof(struct mosquitto__listener)); if(config->default_listener.port){ config->listeners[config->listener_count-1].port = config->default_listener.port; }else{ config->listeners[config->listener_count-1].port = 1883; } if(config->default_listener.host){ config->listeners[config->listener_count-1].host = config->default_listener.host; }else{ config->listeners[config->listener_count-1].host = NULL; } if(config->default_listener.mount_point){ config->listeners[config->listener_count-1].mount_point = config->default_listener.mount_point; }else{ config->listeners[config->listener_count-1].mount_point = NULL; } config->listeners[config->listener_count-1].bind_interface = config->default_listener.bind_interface; config->listeners[config->listener_count-1].max_connections = config->default_listener.max_connections; config->listeners[config->listener_count-1].protocol = config->default_listener.protocol; config->listeners[config->listener_count-1].socket_domain = config->default_listener.socket_domain; config->listeners[config->listener_count-1].socks = NULL; config->listeners[config->listener_count-1].sock_count = 0; config->listeners[config->listener_count-1].client_count = 0; config->listeners[config->listener_count-1].use_username_as_clientid = config->default_listener.use_username_as_clientid; config->listeners[config->listener_count-1].max_qos = config->default_listener.max_qos; config->listeners[config->listener_count-1].max_topic_alias = config->default_listener.max_topic_alias; #ifdef WITH_TLS config->listeners[config->listener_count-1].tls_version = config->default_listener.tls_version; config->listeners[config->listener_count-1].tls_engine = config->default_listener.tls_engine; config->listeners[config->listener_count-1].tls_keyform = config->default_listener.tls_keyform; config->listeners[config->listener_count-1].tls_engine_kpass_sha1 = config->default_listener.tls_engine_kpass_sha1; config->listeners[config->listener_count-1].cafile = config->default_listener.cafile; config->listeners[config->listener_count-1].capath = config->default_listener.capath; config->listeners[config->listener_count-1].certfile = config->default_listener.certfile; config->listeners[config->listener_count-1].keyfile = config->default_listener.keyfile; config->listeners[config->listener_count-1].ciphers = config->default_listener.ciphers; config->listeners[config->listener_count-1].ciphers_tls13 = config->default_listener.ciphers_tls13; config->listeners[config->listener_count-1].dhparamfile = config->default_listener.dhparamfile; config->listeners[config->listener_count-1].psk_hint = config->default_listener.psk_hint; config->listeners[config->listener_count-1].require_certificate = config->default_listener.require_certificate; config->listeners[config->listener_count-1].ssl_ctx = NULL; config->listeners[config->listener_count-1].crlfile = config->default_listener.crlfile; config->listeners[config->listener_count-1].use_identity_as_username = config->default_listener.use_identity_as_username; config->listeners[config->listener_count-1].use_subject_as_username = config->default_listener.use_subject_as_username; #endif config->listeners[config->listener_count-1].security_options.acl_file = config->default_listener.security_options.acl_file; config->listeners[config->listener_count-1].security_options.password_file = config->default_listener.security_options.password_file; config->listeners[config->listener_count-1].security_options.psk_file = config->default_listener.security_options.psk_file; config->listeners[config->listener_count-1].security_options.auth_plugin_configs = config->default_listener.security_options.auth_plugin_configs; config->listeners[config->listener_count-1].security_options.auth_plugin_config_count = config->default_listener.security_options.auth_plugin_config_count; config->listeners[config->listener_count-1].security_options.allow_anonymous = config->default_listener.security_options.allow_anonymous; config->listeners[config->listener_count-1].security_options.allow_zero_length_clientid = config->default_listener.security_options.allow_zero_length_clientid; } /* Default to drop to mosquitto user if we are privileged and no user specified. */ if(!config->user){ config->user = mosquitto__strdup("mosquitto"); if(config->user == NULL){ return MOSQ_ERR_NOMEM; } } if(db.verbose){ config->log_type = UINT_MAX; } return config__check(config); } static void config__copy(struct mosquitto__config *src, struct mosquitto__config *dest) { mosquitto__free(dest->security_options.acl_file); dest->security_options.acl_file = src->security_options.acl_file; dest->security_options.allow_anonymous = src->security_options.allow_anonymous; dest->security_options.allow_zero_length_clientid = src->security_options.allow_zero_length_clientid; mosquitto__free(dest->security_options.auto_id_prefix); dest->security_options.auto_id_prefix = src->security_options.auto_id_prefix; dest->security_options.auto_id_prefix_len = src->security_options.auto_id_prefix_len; mosquitto__free(dest->security_options.password_file); dest->security_options.password_file = src->security_options.password_file; mosquitto__free(dest->security_options.psk_file); dest->security_options.psk_file = src->security_options.psk_file; dest->allow_duplicate_messages = src->allow_duplicate_messages; dest->autosave_interval = src->autosave_interval; dest->autosave_on_changes = src->autosave_on_changes; mosquitto__free(dest->clientid_prefixes); dest->clientid_prefixes = src->clientid_prefixes; dest->connection_messages = src->connection_messages; dest->log_dest = src->log_dest; dest->log_facility = src->log_facility; dest->log_type = src->log_type; dest->log_timestamp = src->log_timestamp; mosquitto__free(dest->log_timestamp_format); dest->log_timestamp_format = src->log_timestamp_format; mosquitto__free(dest->log_file); dest->log_file = src->log_file; dest->message_size_limit = src->message_size_limit; dest->persistence = src->persistence; mosquitto__free(dest->persistence_location); dest->persistence_location = src->persistence_location; mosquitto__free(dest->persistence_file); dest->persistence_file = src->persistence_file; mosquitto__free(dest->persistence_filepath); dest->persistence_filepath = src->persistence_filepath; dest->persistent_client_expiration = src->persistent_client_expiration; dest->queue_qos0_messages = src->queue_qos0_messages; dest->sys_interval = src->sys_interval; dest->upgrade_outgoing_qos = src->upgrade_outgoing_qos; #ifdef WITH_WEBSOCKETS dest->websockets_log_level = src->websockets_log_level; #endif } int config__read(struct mosquitto__config *config, bool reload) { int rc = MOSQ_ERR_SUCCESS; struct config_recurse cr; int lineno = 0; #ifdef WITH_PERSISTENCE size_t len; #endif struct mosquitto__config config_reload; int i; if(reload){ memset(&config_reload, 0, sizeof(struct mosquitto__config)); } cr.log_dest = MQTT3_LOG_NONE; cr.log_dest_set = 0; cr.log_type = MOSQ_LOG_NONE; cr.log_type_set = 0; if(!db.config_file) return 0; if(reload){ /* Re-initialise appropriate config vars to default for reload. */ config__init_reload(&config_reload); config_reload.listeners = config->listeners; config_reload.listener_count = config->listener_count; cur_security_options = NULL; rc = config__read_file(&config_reload, reload, db.config_file, &cr, 0, &lineno); }else{ rc = config__read_file(config, reload, db.config_file, &cr, 0, &lineno); } if(rc){ if(lineno > 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", db.config_file, lineno); } return rc; } if(reload){ config__copy(&config_reload, config); } /* If auth/access options are set and allow_anonymous not explicitly set, disallow anon. */ if(config->local_only == true){ config->security_options.allow_anonymous = true; }else{ if(config->per_listener_settings){ for(i=0; ilistener_count; i++){ /* Default option if no security options set */ if(config->listeners[i].security_options.allow_anonymous == -1){ config->listeners[i].security_options.allow_anonymous = false; } } }else{ if(config->security_options.allow_anonymous == -1){ config->security_options.allow_anonymous = false; } } } #ifdef WITH_PERSISTENCE if(config->persistence){ if(!config->persistence_file){ config->persistence_file = mosquitto__strdup("mosquitto.db"); if(!config->persistence_file) return MOSQ_ERR_NOMEM; } mosquitto__free(config->persistence_filepath); if(config->persistence_location && strlen(config->persistence_location)){ len = strlen(config->persistence_location) + strlen(config->persistence_file) + 2; config->persistence_filepath = mosquitto__malloc(len); if(!config->persistence_filepath) return MOSQ_ERR_NOMEM; #ifdef WIN32 snprintf(config->persistence_filepath, len, "%s\\%s", config->persistence_location, config->persistence_file); #else snprintf(config->persistence_filepath, len, "%s/%s", config->persistence_location, config->persistence_file); #endif }else{ config->persistence_filepath = mosquitto__strdup(config->persistence_file); if(!config->persistence_filepath) return MOSQ_ERR_NOMEM; } } #endif /* Default to drop to mosquitto user if no other user specified. This must * remain here even though it is covered in config__parse_args() because this * function may be called on its own. */ if(!config->user){ config->user = mosquitto__strdup("mosquitto"); } #ifdef WITH_BRIDGE for(i=0; ibridge_count; i++){ if(!config->bridges[i].name){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration: bridge name not defined."); return MOSQ_ERR_INVAL; } if(config->bridges[i].addresses == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration: no remote addresses defined."); return MOSQ_ERR_INVAL; } if(config->bridges[i].topic_count == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration: no topics defined."); return MOSQ_ERR_INVAL; } #ifdef FINAL_WITH_TLS_PSK if(config->bridges[i].tls_psk && !config->bridges[i].tls_psk_identity){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration: missing bridge_identity."); return MOSQ_ERR_INVAL; } if(config->bridges[i].tls_psk_identity && !config->bridges[i].tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration: missing bridge_psk."); return MOSQ_ERR_INVAL; } #endif } #endif if(cr.log_dest_set){ config->log_dest = cr.log_dest; } if(db.verbose){ config->log_type = UINT_MAX; }else if(cr.log_type_set){ config->log_type = cr.log_type; } return MOSQ_ERR_SUCCESS; } static 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) { int rc; char *token; int tmp_int; char *saveptr = NULL; #ifdef WITH_BRIDGE char *tmp_char; struct mosquitto__bridge *cur_bridge = NULL; #endif struct mosquitto__auth_plugin_config *cur_auth_plugin_config = NULL; time_t expiration_mult; char *key; struct mosquitto__listener *cur_listener = &config->default_listener; int i; int lineno_ext = 0; size_t prefix_len; char **files; int file_count; size_t slen; #ifdef WITH_TLS char *kpass_sha = NULL, *kpass_sha_bin = NULL; char *keyform ; #endif *lineno = 0; while(fgets_extending(buf, buflen, fptr)){ (*lineno)++; if((*buf)[0] != '#' && (*buf)[0] != 10 && (*buf)[0] != 13){ slen = strlen(*buf); if(slen == 0){ continue; } while((*buf)[slen-1] == 10 || (*buf)[slen-1] == 13){ (*buf)[slen-1] = 0; slen = strlen(*buf); if(slen == 0){ continue; } } token = strtok_r((*buf), " ", &saveptr); if(token){ if(!strcmp(token, "acl_file")){ conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(reload){ mosquitto__free(cur_security_options->acl_file); cur_security_options->acl_file = NULL; } if(conf__parse_string(&token, "acl_file", &cur_security_options->acl_file, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "address") || !strcmp(token, "addresses")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge || cur_bridge->addresses){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } while((token = strtok_r(NULL, " ", &saveptr))){ if (token[0] == '#'){ break; } cur_bridge->address_count++; cur_bridge->addresses = mosquitto__realloc(cur_bridge->addresses, sizeof(struct bridge_address)*(size_t)cur_bridge->address_count); if(!cur_bridge->addresses){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } cur_bridge->addresses[cur_bridge->address_count-1].address = token; } for(i=0; iaddress_count; i++){ /* cur_bridge->addresses[i].address is now * "address[:port]". If address is an IPv6 address, * then port is required. We must check for the : * backwards. */ tmp_char = strrchr(cur_bridge->addresses[i].address, ':'); if(tmp_char){ /* Remove ':', so cur_bridge->addresses[i].address * now just looks like the address. */ tmp_char[0] = '\0'; /* The remainder of the string */ tmp_int = atoi(&tmp_char[1]); if(tmp_int < 1 || tmp_int > UINT16_MAX){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid port value (%d).", tmp_int); return MOSQ_ERR_INVAL; } cur_bridge->addresses[i].port = (uint16_t)tmp_int; }else{ cur_bridge->addresses[i].port = 1883; } /* This looks a bit weird, but isn't. Before this * call, cur_bridge->addresses[i].address points * to the tokenised part of the line, it will be * reused in a future parse of a config line so we * must duplicate it. */ cur_bridge->addresses[i].address = mosquitto__strdup(cur_bridge->addresses[i].address); conf__attempt_resolve(cur_bridge->addresses[i].address, "bridge address", MOSQ_LOG_WARNING, "Warning"); } if(cur_bridge->address_count == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty address value in configuration."); return MOSQ_ERR_INVAL; } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "allow_anonymous")){ conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(conf__parse_bool(&token, "allow_anonymous", (bool *)&cur_security_options->allow_anonymous, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "allow_duplicate_messages")){ log__printf(NULL, MOSQ_LOG_NOTICE, "The 'allow_duplicate_messages' option is now deprecated and will be removed in a future version. The behaviour will default to true."); if(conf__parse_bool(&token, "allow_duplicate_messages", &config->allow_duplicate_messages, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "allow_zero_length_clientid")){ conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(conf__parse_bool(&token, "allow_zero_length_clientid", &cur_security_options->allow_zero_length_clientid, saveptr)) return MOSQ_ERR_INVAL; }else if(!strncmp(token, "auth_opt_", strlen("auth_opt_")) || !strncmp(token, "plugin_opt_", strlen("plugin_opt_"))){ if(reload) continue; /* Auth plugin not currently valid for reloading. */ if(!cur_auth_plugin_config){ log__printf(NULL, MOSQ_LOG_ERR, "Error: An auth_opt_ option exists in the config file without an auth_plugin."); return MOSQ_ERR_INVAL; } if(!strncmp(token, "auth_opt_", strlen("auth_opt_"))){ prefix_len = strlen("auth_opt_"); }else{ prefix_len = strlen("plugin_opt_"); } if(strlen(token) < prefix_len + 3){ /* auth_opt_ == 9, + one digit key == 10, + one space == 11, + one value == 12 */ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid auth_opt_ config option."); return MOSQ_ERR_INVAL; } key = mosquitto__strdup(&token[prefix_len]); if(!key){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; }else if(STREMPTY(key)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid auth_opt_ config option."); mosquitto__free(key); return MOSQ_ERR_INVAL; } token += prefix_len+strlen(key)+1; while(token[0] == ' ' || token[0] == '\t'){ token++; } if(token[0]){ cur_auth_plugin_config->option_count++; cur_auth_plugin_config->options = mosquitto__realloc(cur_auth_plugin_config->options, (size_t)cur_auth_plugin_config->option_count*sizeof(struct mosquitto_auth_opt)); if(!cur_auth_plugin_config->options){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); mosquitto__free(key); return MOSQ_ERR_NOMEM; } cur_auth_plugin_config->options[cur_auth_plugin_config->option_count-1].key = key; cur_auth_plugin_config->options[cur_auth_plugin_config->option_count-1].value = mosquitto__strdup(token); if(!cur_auth_plugin_config->options[cur_auth_plugin_config->option_count-1].value){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", key); mosquitto__free(key); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "auth_plugin") || !strcmp(token, "plugin")){ if(reload) continue; /* Auth plugin not currently valid for reloading. */ conf__set_cur_security_options(config, cur_listener, &cur_security_options); cur_security_options->auth_plugin_configs = mosquitto__realloc(cur_security_options->auth_plugin_configs, (size_t)(cur_security_options->auth_plugin_config_count+1)*sizeof(struct mosquitto__auth_plugin_config)); if(!cur_security_options->auth_plugin_configs){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } cur_auth_plugin_config = &cur_security_options->auth_plugin_configs[cur_security_options->auth_plugin_config_count]; memset(cur_auth_plugin_config, 0, sizeof(struct mosquitto__auth_plugin_config)); cur_auth_plugin_config->path = NULL; cur_auth_plugin_config->options = NULL; cur_auth_plugin_config->option_count = 0; cur_auth_plugin_config->deny_special_chars = true; cur_security_options->auth_plugin_config_count++; if(conf__parse_string(&token, "auth_plugin", &cur_auth_plugin_config->path, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "auth_plugin_deny_special_chars")){ if(reload) continue; /* Auth plugin not currently valid for reloading. */ if(!cur_auth_plugin_config){ log__printf(NULL, MOSQ_LOG_ERR, "Error: An auth_plugin_deny_special_chars option exists in the config file without an auth_plugin."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "auth_plugin_deny_special_chars", &cur_auth_plugin_config->deny_special_chars, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "auto_id_prefix")){ conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(conf__parse_string(&token, "auto_id_prefix", &cur_security_options->auto_id_prefix, saveptr)) return MOSQ_ERR_INVAL; if(cur_security_options->auto_id_prefix){ cur_security_options->auto_id_prefix_len = (uint16_t)strlen(cur_security_options->auto_id_prefix); }else{ cur_security_options->auto_id_prefix_len = 0; } }else if(!strcmp(token, "autosave_interval")){ if(conf__parse_int(&token, "autosave_interval", &config->autosave_interval, saveptr)) return MOSQ_ERR_INVAL; if(config->autosave_interval < 0) config->autosave_interval = 0; }else if(!strcmp(token, "autosave_on_changes")){ if(conf__parse_bool(&token, "autosave_on_changes", &config->autosave_on_changes, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "bind_address")){ log__printf(NULL, MOSQ_LOG_NOTICE, "The 'bind_address' option is now deprecated and will be removed in a future version. The behaviour will default to true."); config->local_only = false; if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "default listener bind_address", &config->default_listener.host, saveptr)) return MOSQ_ERR_INVAL; if(conf__attempt_resolve(config->default_listener.host, "bind_address", MOSQ_LOG_ERR, "Error")){ return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "bind_interface")){ #ifdef SO_BINDTODEVICE if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "bind_interface", &cur_listener->bind_interface, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_ERR, "Error: bind_interface specified but socket option not available."); return MOSQ_ERR_INVAL; #endif }else if(!strcmp(token, "bridge_attempt_unsubscribe")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "bridge_attempt_unsubscribe", &cur_bridge->attempt_unsubscribe, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "bridge_cafile")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } #ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; } #endif if(conf__parse_string(&token, "bridge_cafile", &cur_bridge->tls_cafile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif }else if(!strcmp(token, "bridge_alpn")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge_alpn", &cur_bridge->tls_alpn, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif }else if(!strcmp(token, "bridge_bind_address")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge_bind_address", &cur_bridge->bind_address, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "bridge_capath")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } #ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; } #endif if(conf__parse_string(&token, "bridge_capath", &cur_bridge->tls_capath, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif }else if(!strcmp(token, "bridge_certfile")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } #ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; } #endif if(conf__parse_string(&token, "bridge_certfile", &cur_bridge->tls_certfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif }else if(!strcmp(token, "bridge_identity")){ #if defined(WITH_BRIDGE) && defined(FINAL_WITH_TLS_PSK) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(cur_bridge->tls_cafile || cur_bridge->tls_capath || cur_bridge->tls_certfile || cur_bridge->tls_keyfile){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and identity encryption in a single bridge."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge_identity", &cur_bridge->tls_psk_identity, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS-PSK support not available."); #endif }else if(!strcmp(token, "bridge_insecure")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "bridge_insecure", &cur_bridge->tls_insecure, saveptr)) return MOSQ_ERR_INVAL; if(cur_bridge->tls_insecure){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge %s using insecure mode.", cur_bridge->name); } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS-PSK support not available."); #endif }else if(!strcmp(token, "bridge_require_ocsp")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* Listeners not valid for reloading. */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "bridge_require_ocsp", &cur_bridge->tls_ocsp_required, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "bridge_max_packet_size")){ #if defined(WITH_BRIDGE) if(reload) continue; /* Bridges not valid for reloading. */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_int(&token, "bridge_max_packet_size", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0) tmp_int = 0; cur_bridge->maximum_packet_size = (uint32_t)tmp_int; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "bridge_outgoing_retain")){ #if defined(WITH_BRIDGE) if(reload) continue; /* Listeners not valid for reloading. */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "bridge_outgoing_retain", &cur_bridge->outgoing_retain, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "bridge_keyfile")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } #ifdef FINAL_WITH_TLS_PSK if(cur_bridge->tls_psk_identity || cur_bridge->tls_psk){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; } #endif if(conf__parse_string(&token, "bridge_keyfile", &cur_bridge->tls_keyfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif }else if(!strcmp(token, "bridge_protocol_version")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } token = strtok_r(NULL, "", &saveptr); if(token){ if(!strcmp(token, "mqttv31")){ cur_bridge->protocol_version = mosq_p_mqtt31; }else if(!strcmp(token, "mqttv311")){ cur_bridge->protocol_version = mosq_p_mqtt311; }else if(!strcmp(token, "mqttv50")){ cur_bridge->protocol_version = mosq_p_mqtt5; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge_protocol_version value (%s).", token); return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty bridge_protocol_version value in configuration."); return MOSQ_ERR_INVAL; } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "bridge_psk")){ #if defined(WITH_BRIDGE) && defined(FINAL_WITH_TLS_PSK) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(cur_bridge->tls_cafile || cur_bridge->tls_capath || cur_bridge->tls_certfile || cur_bridge->tls_keyfile){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single bridge."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge_psk", &cur_bridge->tls_psk, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS-PSK support not available."); #endif }else if(!strcmp(token, "bridge_tls_version")){ #if defined(WITH_BRIDGE) && defined(WITH_TLS) if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge_tls_version", &cur_bridge->tls_version, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge and/or TLS support not available."); #endif }else if(!strcmp(token, "cafile")){ #if defined(WITH_TLS) if(reload) continue; /* Listeners not valid for reloading. */ if(cur_listener->psk_hint){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single listener."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "cafile", &cur_listener->cafile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "capath")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "capath", &cur_listener->capath, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "certfile")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(cur_listener->psk_hint){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot use both certificate and psk encryption in a single listener."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "certfile", &cur_listener->certfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "check_retain_source")){ conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(conf__parse_bool(&token, "check_retain_source", &config->check_retain_source, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "ciphers")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "ciphers", &cur_listener->ciphers, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "ciphers_tls1.3")){ #if defined(WITH_TLS) && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER > 0x3040000FL) if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "ciphers_tls1.3", &cur_listener->ciphers_tls13, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: ciphers_tls1.3 support not available."); #endif }else if(!strcmp(token, "clientid") || !strcmp(token, "remote_clientid")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge remote clientid", &cur_bridge->remote_clientid, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "cleansession")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "cleansession", &cur_bridge->clean_start, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "local_cleansession")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "local_cleansession", (bool *) &cur_bridge->clean_start_local, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "clientid_prefixes")){ log__printf(NULL, MOSQ_LOG_NOTICE, "The 'clientid_prefixes' option is now deprecated and will be removed in a future version."); if(reload){ mosquitto__free(config->clientid_prefixes); config->clientid_prefixes = NULL; } if(conf__parse_string(&token, "clientid_prefixes", &config->clientid_prefixes, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "connection")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ token = strtok_r(NULL, " ", &saveptr); if(token){ /* Check for existing bridge name. */ for(i=0; ibridge_count; i++){ if(!strcmp(config->bridges[i].name, token)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate bridge name \"%s\".", token); return MOSQ_ERR_INVAL; } } config->bridge_count++; config->bridges = mosquitto__realloc(config->bridges, (size_t)config->bridge_count*sizeof(struct mosquitto__bridge)); if(!config->bridges){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } cur_bridge = &(config->bridges[config->bridge_count-1]); memset(cur_bridge, 0, sizeof(struct mosquitto__bridge)); cur_bridge->name = mosquitto__strdup(token); if(!cur_bridge->name){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } cur_bridge->keepalive = 60; cur_bridge->notifications = true; cur_bridge->notifications_local_only = false; cur_bridge->start_type = bst_automatic; cur_bridge->idle_timeout = 60; cur_bridge->restart_timeout = 0; cur_bridge->backoff_base = 5; cur_bridge->backoff_cap = 30; cur_bridge->threshold = 10; cur_bridge->try_private = true; cur_bridge->attempt_unsubscribe = true; cur_bridge->protocol_version = mosq_p_mqtt311; cur_bridge->primary_retry_sock = INVALID_SOCKET; cur_bridge->outgoing_retain = true; cur_bridge->clean_start_local = -1; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty connection value in configuration."); return MOSQ_ERR_INVAL; } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "connection_messages")){ if(conf__parse_bool(&token, token, &config->connection_messages, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "crlfile")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "crlfile", &cur_listener->crlfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "dhparamfile")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "dhparamfile", &cur_listener->dhparamfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "http_dir")){ #ifdef WITH_WEBSOCKETS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "http_dir", &cur_listener->http_dir, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Websockets support not available."); #endif }else if(!strcmp(token, "idle_timeout")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_int(&token, "idle_timeout", &cur_bridge->idle_timeout, saveptr)) return MOSQ_ERR_INVAL; if(cur_bridge->idle_timeout < 1){ log__printf(NULL, MOSQ_LOG_NOTICE, "idle_timeout interval too low, using 1 second."); cur_bridge->idle_timeout = 1; } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "include_dir")){ if(level == 0){ /* Only process include_dir from the main config file. */ token = strtok_r(NULL, "", &saveptr); if(!token){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty include_dir value in configuration."); return 1; } rc = config__get_dir_files(token, &files, &file_count); if(rc) return rc; for(i=0; i 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error found at %s:%d.", files[i], lineno_ext); } /* Free happens below */ break; } } for(i=0; i UINT16_MAX){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Bridge keepalive value too high."); return MOSQ_ERR_INVAL; } if(tmp_int < 5){ log__printf(NULL, MOSQ_LOG_NOTICE, "keepalive interval too low, using 5 seconds."); tmp_int = 5; } cur_bridge->keepalive = (uint16_t)tmp_int; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "keyfile")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "keyfile", &cur_listener->keyfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "listener")){ config->local_only = false; token = strtok_r(NULL, " ", &saveptr); if(token){ tmp_int = atoi(token); #ifdef WITH_UNIX_SOCKETS if(tmp_int < 0 || tmp_int > UINT16_MAX){ #else if(tmp_int < 1 || tmp_int > UINT16_MAX){ #endif log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid port value (%d).", tmp_int); return MOSQ_ERR_INVAL; } /* Look for bind address / unix socket path */ token = strtok_r(NULL, " ", &saveptr); if (token != NULL && token[0] == '#'){ token = NULL; } if(tmp_int == 0 && token == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: A listener with port 0 must provide a Unix socket path."); return MOSQ_ERR_INVAL; } if(reload){ /* We reload listeners settings based on port number/unix socket path. * If the port number/unix path doesn't already exist, exit with a complaint. */ cur_listener = NULL; #ifdef WITH_UNIX_SOCKETS if(tmp_int == 0){ for(i=0; ilistener_count; i++){ if(config->listeners[i].unix_socket_path != NULL && strcmp(config->listeners[i].unix_socket_path, token) == 0){ cur_listener = &config->listeners[i]; break; } } }else #endif { for(i=0; ilistener_count; i++){ if(config->listeners[i].port == tmp_int){ /* Now check we have a matching bind address, if defined */ if(config->listeners[i].host){ if(token && !strcmp(config->listeners[i].host, token)){ /* They both have a bind address, and they match */ cur_listener = &config->listeners[i]; break; } }else{ if(token == NULL){ /* Neither this config nor the new config have a bind address, * so they match. */ cur_listener = &config->listeners[i]; break; } } } } } if(!cur_listener){ log__printf(NULL, MOSQ_LOG_ERR, "Error: It is not currently possible to add/remove listeners when reloading the config file."); return MOSQ_ERR_INVAL; } }else{ config->listener_count++; config->listeners = mosquitto__realloc(config->listeners, sizeof(struct mosquitto__listener)*(size_t)config->listener_count); if(!config->listeners){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } cur_listener = &config->listeners[config->listener_count-1]; memset(cur_listener, 0, sizeof(struct mosquitto__listener)); } listener__set_defaults(cur_listener); cur_listener->port = (uint16_t)tmp_int; mosquitto__free(cur_listener->host); cur_listener->host = NULL; #ifdef WITH_UNIX_SOCKETS mosquitto__free(cur_listener->unix_socket_path); cur_listener->unix_socket_path = NULL; #endif if(token){ #ifdef WITH_UNIX_SOCKETS if(cur_listener->port == 0){ cur_listener->unix_socket_path = mosquitto__strdup(token); }else #endif { cur_listener->host = mosquitto__strdup(token); } } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty listener value in configuration."); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "local_clientid")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge local clientd", &cur_bridge->local_clientid, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "local_password")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge local_password", &cur_bridge->local_password, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "local_username")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge local_username", &cur_bridge->local_username, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "log_dest")){ token = strtok_r(NULL, " ", &saveptr); if(token){ cr->log_dest_set = 1; if(!strcmp(token, "none")){ cr->log_dest = MQTT3_LOG_NONE; }else if(!strcmp(token, "syslog")){ cr->log_dest |= MQTT3_LOG_SYSLOG; }else if(!strcmp(token, "stdout")){ cr->log_dest |= MQTT3_LOG_STDOUT; }else if(!strcmp(token, "stderr")){ cr->log_dest |= MQTT3_LOG_STDERR; }else if(!strcmp(token, "topic")){ cr->log_dest |= MQTT3_LOG_TOPIC; }else if(!strcmp(token, "dlt")){ cr->log_dest |= MQTT3_LOG_DLT; }else if(!strcmp(token, "file")){ if(config->log_fptr || config->log_file){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate \"log_dest file\" value."); return MOSQ_ERR_INVAL; } /* Get remaining string. */ token = saveptr; if(token && token[0]){ while(token[0] == ' ' || token[0] == '\t'){ token++; } } if(token[0]){ config->log_file = mosquitto__strdup(token); if(!config->log_file){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty \"log_dest file\" value in configuration."); return MOSQ_ERR_INVAL; } cr->log_dest |= MQTT3_LOG_FILE; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid log_dest value (%s).", token); return MOSQ_ERR_INVAL; } #if defined(WIN32) || defined(__CYGWIN__) if(service_handle){ if(cr->log_dest == MQTT3_LOG_STDOUT || cr->log_dest == MQTT3_LOG_STDERR){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Cannot log to stdout/stderr when running as a Windows service."); return MOSQ_ERR_INVAL; } } #endif }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty log_dest value in configuration."); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "log_facility")){ #if defined(WIN32) || defined(__CYGWIN__) log__printf(NULL, MOSQ_LOG_WARNING, "Warning: log_facility not supported on Windows."); #else if(conf__parse_int(&token, "log_facility", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; switch(tmp_int){ case 0: config->log_facility = LOG_LOCAL0; break; case 1: config->log_facility = LOG_LOCAL1; break; case 2: config->log_facility = LOG_LOCAL2; break; case 3: config->log_facility = LOG_LOCAL3; break; case 4: config->log_facility = LOG_LOCAL4; break; case 5: config->log_facility = LOG_LOCAL5; break; case 6: config->log_facility = LOG_LOCAL6; break; case 7: config->log_facility = LOG_LOCAL7; break; default: log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid log_facility value (%d).", tmp_int); return MOSQ_ERR_INVAL; } #endif }else if(!strcmp(token, "log_timestamp")){ if(conf__parse_bool(&token, token, &config->log_timestamp, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "log_timestamp_format")){ if(conf__parse_string(&token, token, &config->log_timestamp_format, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "log_type")){ token = strtok_r(NULL, " ", &saveptr); if(token){ cr->log_type_set = 1; if(!strcmp(token, "none")){ cr->log_type = MOSQ_LOG_NONE; }else if(!strcmp(token, "information")){ cr->log_type |= MOSQ_LOG_INFO; }else if(!strcmp(token, "notice")){ cr->log_type |= MOSQ_LOG_NOTICE; }else if(!strcmp(token, "warning")){ cr->log_type |= MOSQ_LOG_WARNING; }else if(!strcmp(token, "error")){ cr->log_type |= MOSQ_LOG_ERR; }else if(!strcmp(token, "debug")){ cr->log_type |= MOSQ_LOG_DEBUG; }else if(!strcmp(token, "subscribe")){ cr->log_type |= MOSQ_LOG_SUBSCRIBE; }else if(!strcmp(token, "unsubscribe")){ cr->log_type |= MOSQ_LOG_UNSUBSCRIBE; }else if(!strcmp(token, "internal")){ cr->log_type |= MOSQ_LOG_INTERNAL; #ifdef WITH_WEBSOCKETS }else if(!strcmp(token, "websockets")){ cr->log_type |= MOSQ_LOG_WEBSOCKETS; #endif }else if(!strcmp(token, "all")){ cr->log_type = MOSQ_LOG_ALL; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid log_type value (%s).", token); return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty log_type value in configuration."); } }else if(!strcmp(token, "max_connections")){ if(reload) continue; /* Listeners not valid for reloading. */ token = strtok_r(NULL, " ", &saveptr); if(token){ cur_listener->max_connections = atoi(token); if(cur_listener->max_connections < 0) cur_listener->max_connections = -1; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_connections value in configuration."); } }else if(!strcmp(token, "maximum_qos") || !strcmp(token, "max_qos")){ if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_int(&token, token, &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0 || tmp_int > 2){ log__printf(NULL, MOSQ_LOG_ERR, "Error: max_qos must be between 0 and 2 inclusive."); return MOSQ_ERR_INVAL; } cur_listener->max_qos = (uint8_t)tmp_int; }else if(!strcmp(token, "max_inflight_bytes")){ if(conf__parse_int(&token, "max_inflight_bytes", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0) tmp_int = 0; config->max_inflight_bytes = (size_t)tmp_int; }else if(!strcmp(token, "max_inflight_messages")){ if(conf__parse_int(&token, "max_inflight_messages", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0 || tmp_int == UINT16_MAX){ tmp_int = 0; }else if(tmp_int > UINT16_MAX){ log__printf(NULL, MOSQ_LOG_ERR, "Error: max_inflight_messages must be <= 65535."); return MOSQ_ERR_INVAL; } config->max_inflight_messages = (uint16_t)tmp_int; }else if(!strcmp(token, "max_keepalive")){ if(conf__parse_int(&token, "max_keepalive", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0 || tmp_int > UINT16_MAX){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid max_keepalive value (%d).", tmp_int); return MOSQ_ERR_INVAL; } config->max_keepalive = (uint16_t)tmp_int; }else if(!strcmp(token, "max_packet_size")){ if(conf__parse_int(&token, "max_packet_size", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 20){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid max_packet_size value (%d).", tmp_int); return MOSQ_ERR_INVAL; } config->max_packet_size = (uint32_t)tmp_int; }else if(!strcmp(token, "max_queued_bytes")){ if(conf__parse_int(&token, "max_queued_bytes", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0) tmp_int = 0; config->max_queued_bytes = (size_t)tmp_int; }else if(!strcmp(token, "max_queued_messages")){ if(conf__parse_int(&token, "max_queued_messages", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0) tmp_int = 0; config->max_queued_messages = tmp_int; }else if(!strcmp(token, "memory_limit")){ ssize_t lim; if(conf__parse_ssize_t(&token, "memory_limit", &lim, saveptr)) return MOSQ_ERR_INVAL; if(lim < 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid memory_limit value (%ld).", lim); return MOSQ_ERR_INVAL; } memory__set_limit((size_t)lim); }else if(!strcmp(token, "message_size_limit")){ log__printf(NULL, MOSQ_LOG_NOTICE, "Note: It is recommended to replace `message_size_limit` with `max_packet_size`."); if(conf__parse_int(&token, "message_size_limit", (int *)&config->message_size_limit, saveptr)) return MOSQ_ERR_INVAL; if(config->message_size_limit > MQTT_MAX_PAYLOAD){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid message_size_limit value (%u).", config->message_size_limit); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "mount_point")){ if(reload) continue; /* Listeners not valid for reloading. */ if(config->listener_count == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: You must use create a listener before using the mount_point option in the configuration file."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "mount_point", &cur_listener->mount_point, saveptr)) return MOSQ_ERR_INVAL; if(mosquitto_pub_topic_check(cur_listener->mount_point) != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid mount_point '%s'. Does it contain a wildcard character?", cur_listener->mount_point); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "notifications")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "notifications", &cur_bridge->notifications, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "notifications_local_only")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration"); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "notifications_local_only", &cur_bridge->notifications_local_only, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "notification_topic")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "notification_topic", &cur_bridge->notification_topic, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "password") || !strcmp(token, "remote_password")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge remote_password", &cur_bridge->remote_password, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "password_file")){ conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(reload){ mosquitto__free(cur_security_options->password_file); cur_security_options->password_file = NULL; } if(conf__parse_string(&token, "password_file", &cur_security_options->password_file, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "per_listener_settings")){ if(conf__parse_bool(&token, "per_listener_settings", &config->per_listener_settings, saveptr)) return MOSQ_ERR_INVAL; if(cur_security_options && config->per_listener_settings){ log__printf(NULL, MOSQ_LOG_ERR, "Error: per_listener_settings must be set before any other security settings."); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "persistence") || !strcmp(token, "retained_persistence")){ if(conf__parse_bool(&token, token, &config->persistence, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "persistence_file")){ if(conf__parse_string(&token, "persistence_file", &config->persistence_file, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "persistence_location")){ if(conf__parse_string(&token, "persistence_location", &config->persistence_location, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "persistent_client_expiration")){ token = strtok_r(NULL, " ", &saveptr); if(token){ switch(token[strlen(token)-1]){ case 'h': expiration_mult = 3600; break; case 'd': expiration_mult = 86400; break; case 'w': expiration_mult = 86400*7; break; case 'm': expiration_mult = 86400*30; break; case 'y': expiration_mult = 86400*365; break; default: log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid persistent_client_expiration duration in configuration."); return MOSQ_ERR_INVAL; } token[strlen(token)-1] = '\0'; config->persistent_client_expiration = atoi(token)*expiration_mult; if(config->persistent_client_expiration <= 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid persistent_client_expiration duration in configuration."); return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty persistent_client_expiration value in configuration."); } }else if(!strcmp(token, "pid_file")){ if(reload) continue; /* pid file not valid for reloading. */ if(conf__parse_string(&token, "pid_file", &config->pid_file, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "port")){ log__printf(NULL, MOSQ_LOG_NOTICE, "The 'port' option is now deprecated and will be removed in a future version. Please use 'listener' instead."); config->local_only = false; if(reload) continue; /* Listeners not valid for reloading. */ if(config->default_listener.port){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Default listener port specified multiple times. Only the latest will be used."); } if(conf__parse_int(&token, "port", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 1 || tmp_int > UINT16_MAX){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid port value (%d).", tmp_int); return MOSQ_ERR_INVAL; } config->default_listener.port = (uint16_t)tmp_int; }else if(!strcmp(token, "protocol")){ token = strtok_r(NULL, " ", &saveptr); if(token){ if(!strcmp(token, "mqtt")){ cur_listener->protocol = mp_mqtt; /* }else if(!strcmp(token, "mqttsn")){ cur_listener->protocol = mp_mqttsn; */ }else if(!strcmp(token, "websockets")){ #ifdef WITH_WEBSOCKETS cur_listener->protocol = mp_websockets; #else log__printf(NULL, MOSQ_LOG_ERR, "Error: Websockets support not available."); return MOSQ_ERR_INVAL; #endif }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid protocol value (%s).", token); return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty protocol value in configuration."); } }else if(!strcmp(token, "psk_file")){ #ifdef FINAL_WITH_TLS_PSK conf__set_cur_security_options(config, cur_listener, &cur_security_options); if(reload){ mosquitto__free(cur_security_options->psk_file); cur_security_options->psk_file = NULL; } if(conf__parse_string(&token, "psk_file", &cur_security_options->psk_file, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS/TLS-PSK support not available."); #endif }else if(!strcmp(token, "psk_hint")){ #ifdef FINAL_WITH_TLS_PSK if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "psk_hint", &cur_listener->psk_hint, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS/TLS-PSK support not available."); #endif }else if(!strcmp(token, "queue_qos0_messages")){ if(conf__parse_bool(&token, token, &config->queue_qos0_messages, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "require_certificate")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_bool(&token, "require_certificate", &cur_listener->require_certificate, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "restart_timeout")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } token = strtok_r(NULL, " ", &saveptr); if(!token){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty restart_timeout value in configuration."); return MOSQ_ERR_INVAL; } cur_bridge->restart_timeout = atoi(token); cur_bridge->backoff_base = 0; cur_bridge->backoff_cap = 0; if(cur_bridge->restart_timeout < 1){ log__printf(NULL, MOSQ_LOG_NOTICE, "restart_timeout interval too low, using 1 second."); cur_bridge->restart_timeout = 1; } token = strtok_r(NULL, " ", &saveptr); if(token){ cur_bridge->backoff_base = cur_bridge->restart_timeout; cur_bridge->backoff_cap = atoi(token); if(cur_bridge->backoff_cap < cur_bridge->backoff_base){ log__printf(NULL, MOSQ_LOG_ERR, "Error: backoff cap is lower than the base in restart_timeout."); return MOSQ_ERR_INVAL; } } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "retain_available")){ if(conf__parse_bool(&token, token, &config->retain_available, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "retry_interval")){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: The retry_interval option is no longer available."); }else if(!strcmp(token, "round_robin")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "round_robin", &cur_bridge->round_robin, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "set_tcp_nodelay")){ if(conf__parse_bool(&token, "set_tcp_nodelay", &config->set_tcp_nodelay, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "start_type")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } token = strtok_r(NULL, " ", &saveptr); if(token){ if(!strcmp(token, "automatic")){ cur_bridge->start_type = bst_automatic; }else if(!strcmp(token, "lazy")){ cur_bridge->start_type = bst_lazy; }else if(!strcmp(token, "manual")){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Manual start_type not supported."); return MOSQ_ERR_INVAL; }else if(!strcmp(token, "once")){ cur_bridge->start_type = bst_once; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid start_type value in configuration (%s).", token); return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty start_type value in configuration."); return MOSQ_ERR_INVAL; } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "socket_domain")){ if(reload) continue; /* Listeners not valid for reloading. */ token = strtok_r(NULL, " ", &saveptr); if(token){ if(!strcmp(token, "ipv4")){ cur_listener->socket_domain = AF_INET; }else if(!strcmp(token, "ipv6")){ cur_listener->socket_domain = AF_INET6; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid socket_domain value \"%s\" in configuration.", token); return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty socket_domain value in configuration."); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "sys_interval")){ if(conf__parse_int(&token, "sys_interval", &config->sys_interval, saveptr)) return MOSQ_ERR_INVAL; if(config->sys_interval < 0 || config->sys_interval > 65535){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid sys_interval value (%d).", config->sys_interval); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "threshold")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_int(&token, "threshold", &cur_bridge->threshold, saveptr)) return MOSQ_ERR_INVAL; if(cur_bridge->threshold < 1){ log__printf(NULL, MOSQ_LOG_NOTICE, "threshold too low, using 1 message."); cur_bridge->threshold = 1; } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "tls_engine")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "tls_engine", &cur_listener->tls_engine, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "tls_engine_kpass_sha1")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "tls_engine_kpass_sha1", &kpass_sha, saveptr)) return MOSQ_ERR_INVAL; if(mosquitto__hex2bin_sha1(kpass_sha, (unsigned char**)&kpass_sha_bin) != MOSQ_ERR_SUCCESS){ mosquitto__free(kpass_sha); return MOSQ_ERR_INVAL; } cur_listener->tls_engine_kpass_sha1 = kpass_sha_bin; mosquitto__free(kpass_sha); #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "tls_keyform")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ keyform = NULL; if(conf__parse_string(&token, "tls_keyform", &keyform, saveptr)) return MOSQ_ERR_INVAL; cur_listener->tls_keyform = mosq_k_pem; if(!strcmp(keyform, "engine")) cur_listener->tls_keyform = mosq_k_engine; mosquitto__free(keyform); #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "tls_version")){ #if defined(WITH_TLS) if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_string(&token, "tls_version", &cur_listener->tls_version, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "topic")){ #ifdef WITH_BRIDGE char *topic = NULL; enum mosquitto__bridge_direction direction = bd_out; uint8_t qos = 0; char *local_prefix = NULL, *remote_prefix = NULL; if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } token = strtok_r(NULL, " ", &saveptr); if(token){ topic = token; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty topic value in configuration."); return MOSQ_ERR_INVAL; } token = strtok_r(NULL, " ", &saveptr); if(token){ if(!strcasecmp(token, "out")){ direction = bd_out; }else if(!strcasecmp(token, "in")){ direction = bd_in; }else if(!strcasecmp(token, "both")){ direction = bd_both; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge topic direction '%s'.", token); return MOSQ_ERR_INVAL; } token = strtok_r(NULL, " ", &saveptr); if(token){ if (token[0] == '#'){ (void)strtok_r(NULL, "", &saveptr); } qos = (uint8_t)atoi(token); if(qos > 2){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge QoS level '%s'.", token); return MOSQ_ERR_INVAL; } token = strtok_r(NULL, " ", &saveptr); if(token){ if(!strcmp(token, "\"\"") || token[0] == '#'){ local_prefix = NULL; if (token[0] == '#'){ (void)strtok_r(NULL, "", &saveptr); } }else{ local_prefix = token; } token = strtok_r(NULL, " ", &saveptr); if(token){ if(!strcmp(token, "\"\"") || token[0] == '#'){ remote_prefix = NULL; }else{ remote_prefix = token; } } } } } if(bridge__add_topic(cur_bridge, topic, direction, qos, local_prefix, remote_prefix)){ return MOSQ_ERR_INVAL; } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "max_topic_alias")){ if(reload) continue; /* Listeners not valid for reloading. */ token = strtok_r(NULL, " ", &saveptr); if(token){ tmp_int = atoi(token); if(tmp_int < 0 || tmp_int > UINT16_MAX){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid max_topic_alias value in configuration."); return MOSQ_ERR_INVAL; } cur_listener->max_topic_alias = (uint16_t)tmp_int; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty max_topic_alias value in configuration."); return MOSQ_ERR_INVAL; } }else if(!strcmp(token, "try_private")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_bool(&token, "try_private", &cur_bridge->try_private, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "upgrade_outgoing_qos")){ if(conf__parse_bool(&token, token, &config->upgrade_outgoing_qos, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "use_identity_as_username")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_bool(&token, "use_identity_as_username", &cur_listener->use_identity_as_username, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "use_subject_as_username")){ #ifdef WITH_TLS if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_bool(&token, "use_subject_as_username", &cur_listener->use_subject_as_username, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "user")){ if(reload) continue; /* Drop privileges user not valid for reloading. */ mosquitto__free(config->user); if(conf__parse_string(&token, "user", &config->user, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "use_username_as_clientid")){ if(reload) continue; /* Listeners not valid for reloading. */ if(conf__parse_bool(&token, "use_username_as_clientid", &cur_listener->use_username_as_clientid, saveptr)) return MOSQ_ERR_INVAL; }else if(!strcmp(token, "username") || !strcmp(token, "remote_username")){ #ifdef WITH_BRIDGE if(reload) continue; /* FIXME */ if(!cur_bridge){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid bridge configuration."); return MOSQ_ERR_INVAL; } if(conf__parse_string(&token, "bridge remote_username", &cur_bridge->remote_username, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Bridge support not available."); #endif }else if(!strcmp(token, "websockets_log_level")){ #ifdef WITH_WEBSOCKETS if(conf__parse_int(&token, "websockets_log_level", &config->websockets_log_level, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Websockets support not available."); #endif }else if(!strcmp(token, "websockets_headers_size")){ #ifdef WITH_WEBSOCKETS if(conf__parse_int(&token, "websockets_headers_size", &tmp_int, saveptr)) return MOSQ_ERR_INVAL; if(tmp_int < 0 || tmp_int > UINT16_MAX){ log__printf(NULL, MOSQ_LOG_WARNING, "Error: Websockets headers size must be between 0 and 65535 inclusive."); return MOSQ_ERR_INVAL; } config->websockets_headers_size = (uint16_t)tmp_int; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Websockets support not available."); #endif }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unknown configuration variable \"%s\".", token); return MOSQ_ERR_INVAL; } } } } return MOSQ_ERR_SUCCESS; } int config__read_file(struct mosquitto__config *config, bool reload, const char *file, struct config_recurse *cr, int level, int *lineno) { int rc; FILE *fptr = NULL; char *buf; int buflen; #ifndef WIN32 DIR *dir; #endif #ifndef WIN32 dir = opendir(file); if(dir){ closedir(dir); log__printf(NULL, MOSQ_LOG_ERR, "Error: Config file %s is a directory.", file); return 1; } #endif fptr = mosquitto__fopen(file, "rt", false); if(!fptr){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open config file %s.", file); return 1; } buflen = 1000; buf = mosquitto__malloc((size_t)buflen); if(!buf){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); fclose(fptr); return MOSQ_ERR_NOMEM; } rc = config__read_file_core(config, reload, cr, level, lineno, fptr, &buf, &buflen); mosquitto__free(buf); fclose(fptr); return rc; } static int config__check(struct mosquitto__config *config) { /* Checks that are easy to make after the config has been loaded. */ int i; #ifdef WITH_BRIDGE int j; struct mosquitto__bridge *bridge1, *bridge2; char hostname[256]; size_t len; /* Check for bridge duplicate local_clientid, need to generate missing IDs * first. */ for(i=0; ibridge_count; i++){ bridge1 = &config->bridges[i]; if(!bridge1->remote_clientid){ if(!gethostname(hostname, 256)){ len = strlen(hostname) + strlen(bridge1->name) + 2; bridge1->remote_clientid = mosquitto__malloc(len); if(!bridge1->remote_clientid){ return MOSQ_ERR_NOMEM; } snprintf(bridge1->remote_clientid, len, "%s.%s", hostname, bridge1->name); }else{ return 1; } } if(!bridge1->local_clientid){ len = strlen(bridge1->remote_clientid) + strlen("local.") + 2; bridge1->local_clientid = mosquitto__malloc(len); if(!bridge1->local_clientid){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } snprintf(bridge1->local_clientid, len, "local.%s", bridge1->remote_clientid); } } for(i=0; ibridge_count; i++){ bridge1 = &config->bridges[i]; for(j=i+1; jbridge_count; j++){ bridge2 = &config->bridges[j]; if(!strcmp(bridge1->local_clientid, bridge2->local_clientid)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Bridge local_clientid " "'%s' is not unique. Try changing or setting the " "local_clientid value for one of the bridges.", bridge1->local_clientid); return MOSQ_ERR_INVAL; } } } #endif /* Default to auto_id_prefix = 'auto-' if none set. */ if(config->per_listener_settings){ for(i=0; ilistener_count; i++){ if(!config->listeners[i].security_options.auto_id_prefix){ config->listeners[i].security_options.auto_id_prefix = mosquitto__strdup("auto-"); if(!config->listeners[i].security_options.auto_id_prefix){ return MOSQ_ERR_NOMEM; } config->listeners[i].security_options.auto_id_prefix_len = (uint16_t)strlen("auto-"); } } }else{ if(!config->security_options.auto_id_prefix){ config->security_options.auto_id_prefix = mosquitto__strdup("auto-"); if(!config->security_options.auto_id_prefix){ return MOSQ_ERR_NOMEM; } config->security_options.auto_id_prefix_len = (uint16_t)strlen("auto-"); } } return MOSQ_ERR_SUCCESS; } static int conf__parse_bool(char **token, const char *name, bool *value, char *saveptr) { *token = strtok_r(NULL, " ", &saveptr); if(*token){ if(!strcmp(*token, "false") || !strcmp(*token, "0")){ *value = false; }else if(!strcmp(*token, "true") || !strcmp(*token, "1")){ *value = true; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid %s value (%s).", name, *token); return MOSQ_ERR_INVAL; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", name); return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } static int conf__parse_int(char **token, const char *name, int *value, char *saveptr) { *token = strtok_r(NULL, " ", &saveptr); if(*token){ *value = atoi(*token); }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", name); return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } static int conf__parse_ssize_t(char **token, const char *name, ssize_t *value, char *saveptr) { *token = strtok_r(NULL, " ", &saveptr); if(*token){ *value = atol(*token); }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", name); return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } static int conf__parse_string(char **token, const char *name, char **value, char *saveptr) { size_t tlen; *token = strtok_r(NULL, "", &saveptr); if(*token){ if(*value){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Duplicate %s value in configuration.", name); return MOSQ_ERR_INVAL; } /* Deal with multiple spaces at the beginning of the string. */ *token = misc__trimblanks(*token); if(strlen(*token) == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", name); return MOSQ_ERR_INVAL; } tlen = strlen(*token); if(tlen > UINT16_MAX){ return MOSQ_ERR_INVAL; } if(mosquitto_validate_utf8(*token, (uint16_t)tlen)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Malformed UTF-8 in configuration."); return MOSQ_ERR_INVAL; } *value = mosquitto__strdup(*token); if(!*value){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty %s value in configuration.", name); return MOSQ_ERR_INVAL; } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/src/conf_includedir.c000066400000000000000000000107051450213760600177370ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #include #include #ifdef WIN32 #else # include #endif #ifndef WIN32 # include # include # include #else # include # include #endif #if defined(__HAIKU__) # include #elif !defined(WIN32) && !defined(__CYGWIN__) && !defined(__QNX__) # include #endif #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" #include "mqtt_protocol.h" static int scmp_p(const void *p1, const void *p2) { const char *s1 = *(const char **)p1; const char *s2 = *(const char **)p2; int result; while(s1[0] && s2[0]){ /* Sort by case insensitive part first */ result = toupper(s1[0]) - toupper(s2[0]); if(result == 0){ /* Case insensitive part matched, now distinguish between case */ result = s1[0] - s2[0]; if(result != 0){ return result; } }else{ /* Return case insensitive match fail */ return result; } s1++; s2++; } return s1[0] - s2[0]; } #ifdef WIN32 int config__get_dir_files(const char *include_dir, char ***files, int *file_count) { size_t len; int i; char **l_files = NULL; int l_file_count = 0; char **files_tmp; HANDLE fh; char dirpath[MAX_PATH]; WIN32_FIND_DATA find_data; snprintf(dirpath, MAX_PATH, "%s\\*.conf", include_dir); fh = FindFirstFile(dirpath, &find_data); if(fh == INVALID_HANDLE_VALUE){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open include_dir '%s'.", include_dir); return 1; } do{ len = strlen(include_dir)+1+strlen(find_data.cFileName)+1; l_file_count++; files_tmp = mosquitto__realloc(l_files, l_file_count*sizeof(char *)); if(!files_tmp){ for(i=0; id_name) > 5){ if(!strcmp(&de->d_name[strlen(de->d_name)-5], ".conf")){ len = strlen(include_dir)+1+strlen(de->d_name)+1; l_file_count++; files_tmp = mosquitto__realloc(l_files, (size_t)l_file_count*sizeof(char *)); if(!files_tmp){ for(i=0; id_name); l_files[l_file_count-1][len] = '\0'; } } } closedir(dh); if(l_files){ qsort(l_files, (size_t)l_file_count, sizeof(char *), scmp_p); } *files = l_files; *file_count = l_file_count; return 0; } #endif mosquitto-2.0.18/src/context.c000066400000000000000000000172151450213760600162770ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto_broker_internal.h" #include "alias_mosq.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "time_mosq.h" #include "util_mosq.h" #include "will_mosq.h" #include "uthash.h" struct mosquitto *context__init(mosq_sock_t sock) { struct mosquitto *context; char address[1024]; context = mosquitto__calloc(1, sizeof(struct mosquitto)); if(!context) return NULL; #ifdef WITH_EPOLL context->ident = id_client; #else context->pollfd_index = -1; #endif mosquitto__set_state(context, mosq_cs_new); context->sock = sock; context->last_msg_in = db.now_s; context->next_msg_out = db.now_s + 60; context->keepalive = 60; /* Default to 60s */ context->clean_start = true; context->id = NULL; context->last_mid = 0; context->will = NULL; context->username = NULL; context->password = NULL; context->listener = NULL; context->acl_list = NULL; context->retain_available = true; /* is_bridge records whether this client is a bridge or not. This could be * done by looking at context->bridge for bridges that we create ourself, * but incoming bridges need some other way of being recorded. */ context->is_bridge = false; context->in_packet.payload = NULL; packet__cleanup(&context->in_packet); context->out_packet = NULL; context->current_out_packet = NULL; context->out_packet_count = 0; context->address = NULL; if((int)sock >= 0){ if(!net__socket_get_address(sock, address, 1024, &context->remote_port)){ context->address = mosquitto__strdup(address); } if(!context->address){ /* getpeername and inet_ntop failed and not a bridge */ mosquitto__free(context); return NULL; } } context->bridge = NULL; context->msgs_in.inflight_maximum = db.config->max_inflight_messages; context->msgs_in.inflight_quota = db.config->max_inflight_messages; context->msgs_out.inflight_maximum = db.config->max_inflight_messages; context->msgs_out.inflight_quota = db.config->max_inflight_messages; context->max_qos = 2; #ifdef WITH_TLS context->ssl = NULL; #endif if((int)context->sock >= 0){ HASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(context->sock), context); } return context; } static void context__cleanup_out_packets(struct mosquitto *context) { struct mosquitto__packet *packet; if(!context) return; if(context->current_out_packet){ packet__cleanup(context->current_out_packet); mosquitto__free(context->current_out_packet); context->current_out_packet = NULL; } while(context->out_packet){ packet__cleanup(context->out_packet); packet = context->out_packet; context->out_packet = context->out_packet->next; mosquitto__free(packet); } context->out_packet_count = 0; } /* * This will result in any outgoing packets going unsent. If we're disconnected * forcefully then it is usually an error condition and shouldn't be a problem, * but it will mean that CONNACK messages will never get sent for bad protocol * versions for example. */ void context__cleanup(struct mosquitto *context, bool force_free) { if(!context) return; if(force_free){ context->clean_start = true; } #ifdef WITH_BRIDGE if(context->bridge){ bridge__cleanup(context); } #endif alias__free_all(context); context__cleanup_out_packets(context); mosquitto__free(context->auth_method); context->auth_method = NULL; mosquitto__free(context->username); context->username = NULL; mosquitto__free(context->password); context->password = NULL; net__socket_close(context); if(force_free){ sub__clean_session(context); } db__messages_delete(context, force_free); mosquitto__free(context->address); context->address = NULL; context__send_will(context); if(context->id){ context__remove_from_by_id(context); mosquitto__free(context->id); context->id = NULL; } packet__cleanup(&(context->in_packet)); context__cleanup_out_packets(context); #if defined(WITH_BROKER) && defined(__GLIBC__) && defined(WITH_ADNS) if(context->adns){ gai_cancel(context->adns); mosquitto__free((struct addrinfo *)context->adns->ar_request); mosquitto__free(context->adns); } #endif if(force_free){ mosquitto__free(context); } } void context__send_will(struct mosquitto *ctxt) { if(ctxt->state != mosq_cs_disconnecting && ctxt->will){ if(ctxt->will_delay_interval > 0){ will_delay__add(ctxt); return; } if(mosquitto_acl_check(ctxt, ctxt->will->msg.topic, (uint32_t)ctxt->will->msg.payloadlen, ctxt->will->msg.payload, (uint8_t)ctxt->will->msg.qos, ctxt->will->msg.retain, MOSQ_ACL_WRITE) == MOSQ_ERR_SUCCESS){ /* Unexpected disconnect, queue the client will. */ db__messages_easy_queue(ctxt, ctxt->will->msg.topic, (uint8_t)ctxt->will->msg.qos, (uint32_t)ctxt->will->msg.payloadlen, ctxt->will->msg.payload, ctxt->will->msg.retain, ctxt->will->expiry_interval, &ctxt->will->properties); } } will__clear(ctxt); } void context__disconnect(struct mosquitto *context) { if(mosquitto__get_state(context) == mosq_cs_disconnected){ return; } plugin__handle_disconnect(context, -1); context__send_will(context); net__socket_close(context); #ifdef WITH_BRIDGE if(context->bridge == NULL) /* Outgoing bridge connection never expire */ #endif { if(context->session_expiry_interval == 0){ /* Client session is due to be expired now */ if(context->will_delay_interval == 0){ /* This will be done later, after the will is published for delay>0. */ context__add_to_disused(context); } }else{ session_expiry__add(context); } } keepalive__remove(context); mosquitto__set_state(context, mosq_cs_disconnected); } void context__add_to_disused(struct mosquitto *context) { if(context->state == mosq_cs_disused) return; mosquitto__set_state(context, mosq_cs_disused); if(context->id){ context__remove_from_by_id(context); mosquitto__free(context->id); context->id = NULL; } context->for_free_next = db.ll_for_free; db.ll_for_free = context; } void context__free_disused(void) { struct mosquitto *context, *next; #ifdef WITH_WEBSOCKETS struct mosquitto *last = NULL; #endif context = db.ll_for_free; db.ll_for_free = NULL; while(context){ #ifdef WITH_WEBSOCKETS if(context->wsi){ /* Don't delete yet, lws hasn't finished with it */ if(last){ last->for_free_next = context; }else{ db.ll_for_free = context; } next = context->for_free_next; context->for_free_next = NULL; last = context; context = next; }else #endif { next = context->for_free_next; context__cleanup(context, true); context = next; } } } void context__add_to_by_id(struct mosquitto *context) { if(context->in_by_id == false){ context->in_by_id = true; HASH_ADD_KEYPTR(hh_id, db.contexts_by_id, context->id, strlen(context->id), context); } } void context__remove_from_by_id(struct mosquitto *context) { struct mosquitto *context_found; if(context->in_by_id == true && context->id){ HASH_FIND(hh_id, db.contexts_by_id, context->id, strlen(context->id), context_found); if(context_found){ HASH_DELETE(hh_id, db.contexts_by_id, context_found); } context->in_by_id = false; } } mosquitto-2.0.18/src/control.c000066400000000000000000000102711450213760600162660ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include "mqtt_protocol.h" #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "send_mosq.h" #ifdef WITH_CONTROL /* Process messages coming in on $CONTROL/. These messages aren't * passed on to other clients. */ int control__process(struct mosquitto *context, struct mosquitto_msg_store *stored) { struct mosquitto__callback *cb_found; struct mosquitto_evt_control event_data; struct mosquitto__security_options *opts; mosquitto_property *properties = NULL; int rc = MOSQ_ERR_SUCCESS; if(db.config->per_listener_settings){ opts = &context->listener->security_options; }else{ opts = &db.config->security_options; } HASH_FIND(hh, opts->plugin_callbacks.control, stored->topic, strlen(stored->topic), cb_found); if(cb_found){ memset(&event_data, 0, sizeof(event_data)); event_data.client = context; event_data.topic = stored->topic; event_data.payload = stored->payload; event_data.payloadlen = stored->payloadlen; event_data.qos = stored->qos; event_data.retain = stored->retain; event_data.properties = stored->properties; event_data.reason_code = MQTT_RC_SUCCESS; event_data.reason_string = NULL; rc = cb_found->cb(MOSQ_EVT_CONTROL, &event_data, cb_found->userdata); if(rc){ if(context->protocol == mosq_p_mqtt5 && event_data.reason_string){ mosquitto_property_add_string(&properties, MQTT_PROP_REASON_STRING, event_data.reason_string); } } free(event_data.reason_string); event_data.reason_string = NULL; } if(stored->qos == 1){ rc = send__puback(context, stored->source_mid, MQTT_RC_SUCCESS, properties); }else if(stored->qos == 2){ rc = send__pubrec(context, stored->source_mid, MQTT_RC_SUCCESS, properties); } mosquitto_property_free_all(&properties); return rc; } #endif int control__register_callback(struct mosquitto__security_options *opts, MOSQ_FUNC_generic_callback cb_func, const char *topic, void *userdata) { #ifdef WITH_CONTROL struct mosquitto__callback *cb_found, *cb_new; size_t topic_len; if(topic == NULL || cb_func == NULL) return MOSQ_ERR_INVAL; topic_len = strlen(topic); if(topic_len == 0 || topic_len > 65535) return MOSQ_ERR_INVAL; if(strncmp(topic, "$CONTROL/", strlen("$CONTROL/")) || strlen(topic) < strlen("$CONTROL/A/v1")){ return MOSQ_ERR_INVAL; } HASH_FIND(hh, opts->plugin_callbacks.control, topic, topic_len, cb_found); if(cb_found){ return MOSQ_ERR_ALREADY_EXISTS; } cb_new = mosquitto__calloc(1, sizeof(struct mosquitto__callback)); if(cb_new == NULL){ return MOSQ_ERR_NOMEM; } cb_new->data = mosquitto__strdup(topic); if(cb_new->data == NULL){ mosquitto__free(cb_new); return MOSQ_ERR_NOMEM; } cb_new->cb = cb_func; cb_new->userdata = userdata; HASH_ADD_KEYPTR(hh, opts->plugin_callbacks.control, cb_new->data, strlen(cb_new->data), cb_new); return MOSQ_ERR_SUCCESS; #else return MOSQ_ERR_NOT_SUPPORTED; #endif } int control__unregister_callback(struct mosquitto__security_options *opts, MOSQ_FUNC_generic_callback cb_func, const char *topic) { #ifdef WITH_CONTROL struct mosquitto__callback *cb_found; size_t topic_len; if(topic == NULL) return MOSQ_ERR_INVAL; topic_len = strlen(topic); if(topic_len == 0 || topic_len > 65535) return MOSQ_ERR_INVAL; if(strncmp(topic, "$CONTROL/", strlen("$CONTROL/"))) return MOSQ_ERR_INVAL; HASH_FIND(hh, opts->plugin_callbacks.control, topic, topic_len, cb_found); if(cb_found && cb_found->cb == cb_func){ HASH_DELETE(hh, opts->plugin_callbacks.control, cb_found); mosquitto__free(cb_found->data); mosquitto__free(cb_found); return MOSQ_ERR_SUCCESS;; } return MOSQ_ERR_NOT_FOUND; #else return MOSQ_ERR_NOT_SUPPORTED; #endif } mosquitto-2.0.18/src/database.c000066400000000000000000001025501450213760600163540ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "send_mosq.h" #include "sys_tree.h" #include "time_mosq.h" #include "util_mosq.h" /** * Is this context ready to take more in flight messages right now? * @param context the client context of interest * @param qos qos for the packet of interest * @return true if more in flight are allowed. */ bool db__ready_for_flight(struct mosquitto *context, enum mosquitto_msg_direction dir, int qos) { struct mosquitto_msg_data *msgs; bool valid_bytes; bool valid_count; if(dir == mosq_md_out){ msgs = &context->msgs_out; }else{ msgs = &context->msgs_in; } if(msgs->inflight_maximum == 0 && db.config->max_inflight_bytes == 0){ return true; } if(qos == 0){ /* Deliver QoS 0 messages unless the queue is already full. * For QoS 0 messages the choice is either "inflight" or dropped. * There is no queueing option, unless the client is offline and * queue_qos0_messages is enabled. */ if(db.config->max_queued_messages == 0 && db.config->max_inflight_bytes == 0){ return true; } valid_bytes = ((msgs->inflight_bytes - (ssize_t)db.config->max_inflight_bytes) < (ssize_t)db.config->max_queued_bytes); if(dir == mosq_md_out){ valid_count = context->out_packet_count < db.config->max_queued_messages; }else{ valid_count = msgs->inflight_count - msgs->inflight_maximum < db.config->max_queued_messages; } if(db.config->max_queued_messages == 0){ return valid_bytes; } if(db.config->max_queued_bytes == 0){ return valid_count; } }else{ valid_bytes = (ssize_t)msgs->inflight_bytes12 < (ssize_t)db.config->max_inflight_bytes; valid_count = msgs->inflight_quota > 0; if(msgs->inflight_maximum == 0){ return valid_bytes; } if(db.config->max_inflight_bytes == 0){ return valid_count; } } return valid_bytes && valid_count; } /** * For a given client context, are more messages allowed to be queued? * It is assumed that inflight checks and queue_qos0 checks have already * been made. * @param context client of interest * @param qos destination qos for the packet of interest * @return true if queuing is allowed, false if should be dropped */ bool db__ready_for_queue(struct mosquitto *context, int qos, struct mosquitto_msg_data *msg_data) { int source_count; int adjust_count; long source_bytes; ssize_t adjust_bytes = (ssize_t)db.config->max_inflight_bytes; bool valid_bytes; bool valid_count; if(db.config->max_queued_messages == 0 && db.config->max_queued_bytes == 0){ return true; } if(qos == 0 && db.config->queue_qos0_messages == false){ return false; /* This case is handled in db__ready_for_flight() */ }else{ source_bytes = (ssize_t)msg_data->queued_bytes12; source_count = msg_data->queued_count12; } adjust_count = msg_data->inflight_maximum; /* nothing in flight for offline clients */ if(context->sock == INVALID_SOCKET){ adjust_bytes = 0; adjust_count = 0; } valid_bytes = (source_bytes - (ssize_t)adjust_bytes) < (ssize_t)db.config->max_queued_bytes; valid_count = source_count - adjust_count < db.config->max_queued_messages; if(db.config->max_queued_bytes == 0){ return valid_count; } if(db.config->max_queued_messages == 0){ return valid_bytes; } return valid_bytes && valid_count; } void db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { msg_data->inflight_count++; msg_data->inflight_bytes += msg->store->payloadlen; if(msg->qos != 0){ msg_data->inflight_count12++; msg_data->inflight_bytes12 += msg->store->payloadlen; } } static void db__msg_remove_from_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { msg_data->inflight_count--; msg_data->inflight_bytes -= msg->store->payloadlen; if(msg->qos != 0){ msg_data->inflight_count12--; msg_data->inflight_bytes12 -= msg->store->payloadlen; } } void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { msg_data->queued_count++; msg_data->queued_bytes += msg->store->payloadlen; if(msg->qos != 0){ msg_data->queued_count12++; msg_data->queued_bytes12 += msg->store->payloadlen; } } static void db__msg_remove_from_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { msg_data->queued_count--; msg_data->queued_bytes -= msg->store->payloadlen; if(msg->qos != 0){ msg_data->queued_count12--; msg_data->queued_bytes12 -= msg->store->payloadlen; } } int db__open(struct mosquitto__config *config) { struct mosquitto__subhier *subhier; if(!config) return MOSQ_ERR_INVAL; db.last_db_id = 0; db.contexts_by_id = NULL; db.contexts_by_sock = NULL; db.contexts_for_free = NULL; #ifdef WITH_BRIDGE db.bridges = NULL; db.bridge_count = 0; #endif /* Initialize the hashtable */ db.clientid_index_hash = NULL; db.subs = NULL; subhier = sub__add_hier_entry(NULL, &db.subs, "", 0); if(!subhier) return MOSQ_ERR_NOMEM; subhier = sub__add_hier_entry(NULL, &db.subs, "$SYS", (uint16_t)strlen("$SYS")); if(!subhier) return MOSQ_ERR_NOMEM; retain__init(); db.config->security_options.unpwd = NULL; #ifdef WITH_PERSISTENCE if(persist__restore()) return 1; #endif return MOSQ_ERR_SUCCESS; } static void subhier_clean(struct mosquitto__subhier **subhier) { struct mosquitto__subhier *peer, *subhier_tmp; struct mosquitto__subleaf *leaf, *nextleaf; HASH_ITER(hh, *subhier, peer, subhier_tmp){ leaf = peer->subs; while(leaf){ nextleaf = leaf->next; mosquitto__free(leaf); leaf = nextleaf; } subhier_clean(&peer->children); mosquitto__free(peer->topic); HASH_DELETE(hh, *subhier, peer); mosquitto__free(peer); } } int db__close(void) { subhier_clean(&db.subs); retain__clean(&db.retains); db__msg_store_clean(); return MOSQ_ERR_SUCCESS; } void db__msg_store_add(struct mosquitto_msg_store *store) { store->next = db.msg_store; store->prev = NULL; if(db.msg_store){ db.msg_store->prev = store; } db.msg_store = store; } void db__msg_store_free(struct mosquitto_msg_store *store) { int i; mosquitto__free(store->source_id); mosquitto__free(store->source_username); if(store->dest_ids){ for(i=0; idest_id_count; i++){ mosquitto__free(store->dest_ids[i]); } mosquitto__free(store->dest_ids); } mosquitto__free(store->topic); mosquitto_property_free_all(&store->properties); mosquitto__free(store->payload); mosquitto__free(store); } void db__msg_store_remove(struct mosquitto_msg_store *store) { if(store->prev){ store->prev->next = store->next; if(store->next){ store->next->prev = store->prev; } }else{ db.msg_store = store->next; if(store->next){ store->next->prev = NULL; } } db.msg_store_count--; db.msg_store_bytes -= store->payloadlen; db__msg_store_free(store); } void db__msg_store_clean(void) { struct mosquitto_msg_store *store, *next;; store = db.msg_store; while(store){ next = store->next; db__msg_store_remove(store); store = next; } } void db__msg_store_ref_inc(struct mosquitto_msg_store *store) { store->ref_count++; } void db__msg_store_ref_dec(struct mosquitto_msg_store **store) { (*store)->ref_count--; if((*store)->ref_count == 0){ db__msg_store_remove(*store); *store = NULL; } } void db__msg_store_compact(void) { struct mosquitto_msg_store *store, *next; store = db.msg_store; while(store){ next = store->next; if(store->ref_count < 1){ db__msg_store_remove(store); } store = next; } } static void db__message_remove_from_inflight(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *item) { if(!msg_data || !item){ return; } DL_DELETE(msg_data->inflight, item); if(item->store){ db__msg_remove_from_inflight_stats(msg_data, item); db__msg_store_ref_dec(&item->store); } mosquitto_property_free_all(&item->properties); mosquitto__free(item); } static void db__message_remove_from_queued(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *item) { if(!msg_data || !item){ return; } DL_DELETE(msg_data->queued, item); if(item->store){ db__msg_store_ref_dec(&item->store); } mosquitto_property_free_all(&item->properties); mosquitto__free(item); } void db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_data *msg_data) { struct mosquitto_client_msg *msg; UNUSED(context); msg = msg_data->queued; DL_DELETE(msg_data->queued, msg); DL_APPEND(msg_data->inflight, msg); if(msg_data->inflight_quota > 0){ msg_data->inflight_quota--; } db__msg_remove_from_queued_stats(msg_data, msg); db__msg_add_to_inflight_stats(msg_data, msg); } int db__message_delete_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state expect_state, int qos) { struct mosquitto_client_msg *tail, *tmp; int msg_index = 0; if(!context) return MOSQ_ERR_INVAL; DL_FOREACH_SAFE(context->msgs_out.inflight, tail, tmp){ msg_index++; if(tail->mid == mid){ if(tail->qos != qos){ return MOSQ_ERR_PROTOCOL; }else if(qos == 2 && tail->state != expect_state){ return MOSQ_ERR_PROTOCOL; } msg_index--; db__message_remove_from_inflight(&context->msgs_out, tail); break; } } DL_FOREACH_SAFE(context->msgs_out.queued, tail, tmp){ if(!db__ready_for_flight(context, mosq_md_out, tail->qos)){ break; } msg_index++; tail->timestamp = db.now_s; switch(tail->qos){ case 0: tail->state = mosq_ms_publish_qos0; break; case 1: tail->state = mosq_ms_publish_qos1; break; case 2: tail->state = mosq_ms_publish_qos2; break; } db__message_dequeue_first(context, &context->msgs_out); } #ifdef WITH_PERSISTENCE db.persistence_changes++; #endif return db__message_write_inflight_out_latest(context); } int db__message_insert(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, uint8_t qos, bool retain, struct mosquitto_msg_store *stored, mosquitto_property *properties, bool update) { struct mosquitto_client_msg *msg; struct mosquitto_msg_data *msg_data; enum mosquitto_msg_state state = mosq_ms_invalid; int rc = 0; int i; char **dest_ids; assert(stored); if(!context) return MOSQ_ERR_INVAL; if(!context->id) return MOSQ_ERR_SUCCESS; /* Protect against unlikely "client is disconnected but not entirely freed" scenario */ if(dir == mosq_md_out){ msg_data = &context->msgs_out; }else{ msg_data = &context->msgs_in; } /* Check whether we've already sent this message to this client * for outgoing messages only. * If retain==true then this is a stale retained message and so should be * sent regardless. FIXME - this does mean retained messages will received * multiple times for overlapping subscriptions, although this is only the * case for SUBSCRIPTION with multiple subs in so is a minor concern. */ if(context->protocol != mosq_p_mqtt5 && db.config->allow_duplicate_messages == false && dir == mosq_md_out && retain == false && stored->dest_ids){ for(i=0; idest_id_count; i++){ if(stored->dest_ids[i] && !strcmp(stored->dest_ids[i], context->id)){ /* We have already sent this message to this client. */ mosquitto_property_free_all(&properties); return MOSQ_ERR_SUCCESS; } } } if(context->sock == INVALID_SOCKET){ /* Client is not connected only queue messages with QoS>0. */ if(qos == 0 && !db.config->queue_qos0_messages){ if(!context->bridge){ mosquitto_property_free_all(&properties); return 2; }else{ if(context->bridge->start_type != bst_lazy){ mosquitto_property_free_all(&properties); return 2; } } } if(context->bridge && context->bridge->clean_start_local == true){ mosquitto_property_free_all(&properties); return 2; } } if(context->sock != INVALID_SOCKET){ if(db__ready_for_flight(context, dir, qos)){ if(dir == mosq_md_out){ switch(qos){ case 0: state = mosq_ms_publish_qos0; break; case 1: state = mosq_ms_publish_qos1; break; case 2: state = mosq_ms_publish_qos2; break; } }else{ if(qos == 2){ state = mosq_ms_wait_for_pubrel; }else{ mosquitto_property_free_all(&properties); return 1; } } }else if(qos != 0 && db__ready_for_queue(context, qos, msg_data)){ state = mosq_ms_queued; rc = 2; }else{ /* Dropping message due to full queue. */ if(context->is_dropping == false){ context->is_dropping = true; log__printf(NULL, MOSQ_LOG_NOTICE, "Outgoing messages are being dropped for client %s.", context->id); } G_MSGS_DROPPED_INC(); mosquitto_property_free_all(&properties); return 2; } }else{ if (db__ready_for_queue(context, qos, msg_data)){ state = mosq_ms_queued; }else{ G_MSGS_DROPPED_INC(); if(context->is_dropping == false){ context->is_dropping = true; log__printf(NULL, MOSQ_LOG_NOTICE, "Outgoing messages are being dropped for client %s.", context->id); } mosquitto_property_free_all(&properties); return 2; } } assert(state != mosq_ms_invalid); #ifdef WITH_PERSISTENCE if(state == mosq_ms_queued){ db.persistence_changes++; } #endif msg = mosquitto__calloc(1, sizeof(struct mosquitto_client_msg)); if(!msg) return MOSQ_ERR_NOMEM; msg->prev = NULL; msg->next = NULL; msg->store = stored; db__msg_store_ref_inc(msg->store); msg->mid = mid; msg->timestamp = db.now_s; msg->direction = dir; msg->state = state; msg->dup = false; if(qos > context->max_qos){ msg->qos = context->max_qos; }else{ msg->qos = qos; } msg->retain = retain; msg->properties = properties; if(state == mosq_ms_queued){ DL_APPEND(msg_data->queued, msg); db__msg_add_to_queued_stats(msg_data, msg); }else{ DL_APPEND(msg_data->inflight, msg); db__msg_add_to_inflight_stats(msg_data, msg); } if(db.config->allow_duplicate_messages == false && dir == mosq_md_out && retain == false){ /* Record which client ids this message has been sent to so we can avoid duplicates. * Outgoing messages only. * If retain==true then this is a stale retained message and so should be * sent regardless. FIXME - this does mean retained messages will received * multiple times for overlapping subscriptions, although this is only the * case for SUBSCRIPTION with multiple subs in so is a minor concern. */ dest_ids = mosquitto__realloc(stored->dest_ids, sizeof(char *)*(size_t)(stored->dest_id_count+1)); if(dest_ids){ stored->dest_ids = dest_ids; stored->dest_id_count++; stored->dest_ids[stored->dest_id_count-1] = mosquitto__strdup(context->id); if(!stored->dest_ids[stored->dest_id_count-1]){ return MOSQ_ERR_NOMEM; } }else{ return MOSQ_ERR_NOMEM; } } #ifdef WITH_BRIDGE if(context->bridge && context->bridge->start_type == bst_lazy && context->sock == INVALID_SOCKET && context->msgs_out.inflight_count + context->msgs_out.queued_count >= context->bridge->threshold){ context->bridge->lazy_reconnect = true; } #endif if(dir == mosq_md_out && msg->qos > 0 && state != mosq_ms_queued){ util__decrement_send_quota(context); }else if(dir == mosq_md_in && msg->qos > 0 && state != mosq_ms_queued){ util__decrement_receive_quota(context); } if(dir == mosq_md_out && update){ rc = db__message_write_inflight_out_latest(context); if(rc) return rc; rc = db__message_write_queued_out(context); if(rc) return rc; } return rc; } int db__message_update_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state state, int qos) { struct mosquitto_client_msg *tail; DL_FOREACH(context->msgs_out.inflight, tail){ if(tail->mid == mid){ if(tail->qos != qos){ return MOSQ_ERR_PROTOCOL; } tail->state = state; tail->timestamp = db.now_s; return MOSQ_ERR_SUCCESS; } } return MOSQ_ERR_NOT_FOUND; } static void db__messages_delete_list(struct mosquitto_client_msg **head) { struct mosquitto_client_msg *tail, *tmp; DL_FOREACH_SAFE(*head, tail, tmp){ DL_DELETE(*head, tail); db__msg_store_ref_dec(&tail->store); mosquitto_property_free_all(&tail->properties); mosquitto__free(tail); } *head = NULL; } int db__messages_delete(struct mosquitto *context, bool force_free) { if(!context) return MOSQ_ERR_INVAL; if(force_free || context->clean_start || (context->bridge && context->bridge->clean_start)){ db__messages_delete_list(&context->msgs_in.inflight); db__messages_delete_list(&context->msgs_in.queued); context->msgs_in.inflight_bytes = 0; context->msgs_in.inflight_bytes12 = 0; context->msgs_in.inflight_count = 0; context->msgs_in.inflight_count12 = 0; context->msgs_in.queued_bytes = 0; context->msgs_in.queued_bytes12 = 0; context->msgs_in.queued_count = 0; context->msgs_in.queued_count12 = 0; } if(force_free || (context->bridge && context->bridge->clean_start_local) || (context->bridge == NULL && context->clean_start)){ db__messages_delete_list(&context->msgs_out.inflight); db__messages_delete_list(&context->msgs_out.queued); context->msgs_out.inflight_bytes = 0; context->msgs_out.inflight_bytes12 = 0; context->msgs_out.inflight_count = 0; context->msgs_out.inflight_count12 = 0; context->msgs_out.queued_bytes = 0; context->msgs_out.queued_bytes12 = 0; context->msgs_out.queued_count = 0; context->msgs_out.queued_count12 = 0; } return MOSQ_ERR_SUCCESS; } int 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) { struct mosquitto_msg_store *stored; const char *source_id; enum mosquitto_msg_origin origin; if(!topic) return MOSQ_ERR_INVAL; stored = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store)); if(stored == NULL) return MOSQ_ERR_NOMEM; stored->topic = mosquitto__strdup(topic); if(stored->topic == NULL){ db__msg_store_free(stored); return MOSQ_ERR_INVAL; } stored->qos = qos; if(db.config->retain_available == false){ stored->retain = 0; }else{ stored->retain = retain; } stored->payloadlen = payloadlen; stored->payload = mosquitto__malloc(stored->payloadlen+1); if(stored->payload == NULL){ db__msg_store_free(stored); return MOSQ_ERR_NOMEM; } /* Ensure payload is always zero terminated, this is the reason for the extra byte above */ ((uint8_t *)stored->payload)[stored->payloadlen] = 0; memcpy(stored->payload, payload, stored->payloadlen); if(context && context->id){ source_id = context->id; }else{ source_id = ""; } if(properties){ stored->properties = *properties; *properties = NULL; } if(context){ origin = mosq_mo_client; }else{ origin = mosq_mo_broker; } if(db__message_store(context, stored, message_expiry_interval, 0, origin)) return 1; return sub__messages_queue(source_id, stored->topic, stored->qos, stored->retain, &stored); } /* 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. */ int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store *stored, uint32_t message_expiry_interval, dbid_t store_id, enum mosquitto_msg_origin origin) { assert(stored); if(source && source->id){ stored->source_id = mosquitto__strdup(source->id); }else{ stored->source_id = mosquitto__strdup(""); } if(!stored->source_id){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); db__msg_store_free(stored); return MOSQ_ERR_NOMEM; } if(source && source->username){ stored->source_username = mosquitto__strdup(source->username); if(!stored->source_username){ db__msg_store_free(stored); return MOSQ_ERR_NOMEM; } } if(source){ stored->source_listener = source->listener; } stored->mid = 0; stored->origin = origin; if(message_expiry_interval > 0){ stored->message_expiry_time = db.now_real_s + message_expiry_interval; }else{ stored->message_expiry_time = 0; } stored->dest_ids = NULL; stored->dest_id_count = 0; db.msg_store_count++; db.msg_store_bytes += stored->payloadlen; if(!store_id){ stored->db_id = ++db.last_db_id; }else{ stored->db_id = store_id; } db__msg_store_add(stored); return MOSQ_ERR_SUCCESS; } int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_client_msg **client_msg) { struct mosquitto_client_msg *cmsg; *client_msg = NULL; if(!context) return MOSQ_ERR_INVAL; DL_FOREACH(context->msgs_in.inflight, cmsg){ if(cmsg->store->source_mid == mid){ *client_msg = cmsg; return MOSQ_ERR_SUCCESS; } } DL_FOREACH(context->msgs_in.queued, cmsg){ if(cmsg->store->source_mid == mid){ *client_msg = cmsg; return MOSQ_ERR_SUCCESS; } } return 1; } /* Called on reconnect to set outgoing messages to a sensible state and force a * retry, and to set incoming messages to expect an appropriate retry. */ static int db__message_reconnect_reset_outgoing(struct mosquitto *context) { struct mosquitto_client_msg *msg, *tmp; context->msgs_out.inflight_bytes = 0; context->msgs_out.inflight_bytes12 = 0; context->msgs_out.inflight_count = 0; context->msgs_out.inflight_count12 = 0; context->msgs_out.queued_bytes = 0; context->msgs_out.queued_bytes12 = 0; context->msgs_out.queued_count = 0; context->msgs_out.queued_count12 = 0; context->msgs_out.inflight_quota = context->msgs_out.inflight_maximum; DL_FOREACH_SAFE(context->msgs_out.inflight, msg, tmp){ db__msg_add_to_inflight_stats(&context->msgs_out, msg); if(msg->qos > 0){ util__decrement_send_quota(context); } switch(msg->qos){ case 0: msg->state = mosq_ms_publish_qos0; break; case 1: msg->state = mosq_ms_publish_qos1; break; case 2: if(msg->state == mosq_ms_wait_for_pubcomp){ msg->state = mosq_ms_resend_pubrel; }else{ msg->state = mosq_ms_publish_qos2; } break; } } /* Messages received when the client was disconnected are put * in the mosq_ms_queued state. If we don't change them to the * appropriate "publish" state, then the queued messages won't * get sent until the client next receives a message - and they * will be sent out of order. */ DL_FOREACH_SAFE(context->msgs_out.queued, msg, tmp){ db__msg_add_to_queued_stats(&context->msgs_out, msg); if(db__ready_for_flight(context, mosq_md_out, msg->qos)){ switch(msg->qos){ case 0: msg->state = mosq_ms_publish_qos0; break; case 1: msg->state = mosq_ms_publish_qos1; break; case 2: msg->state = mosq_ms_publish_qos2; break; } db__message_dequeue_first(context, &context->msgs_out); } } return MOSQ_ERR_SUCCESS; } /* Called on reconnect to set incoming messages to expect an appropriate retry. */ static int db__message_reconnect_reset_incoming(struct mosquitto *context) { struct mosquitto_client_msg *msg, *tmp; context->msgs_in.inflight_bytes = 0; context->msgs_in.inflight_bytes12 = 0; context->msgs_in.inflight_count = 0; context->msgs_in.inflight_count12 = 0; context->msgs_in.queued_bytes = 0; context->msgs_in.queued_bytes12 = 0; context->msgs_in.queued_count = 0; context->msgs_in.queued_count12 = 0; context->msgs_in.inflight_quota = context->msgs_in.inflight_maximum; DL_FOREACH_SAFE(context->msgs_in.inflight, msg, tmp){ db__msg_add_to_inflight_stats(&context->msgs_in, msg); if(msg->qos > 0){ util__decrement_receive_quota(context); } if(msg->qos != 2){ /* Anything msgs_in, msg); }else{ /* Message state can be preserved here because it should match * whatever the client has got. */ msg->dup = 0; } } /* Messages received when the client was disconnected are put * in the mosq_ms_queued state. If we don't change them to the * appropriate "publish" state, then the queued messages won't * get sent until the client next receives a message - and they * will be sent out of order. */ DL_FOREACH_SAFE(context->msgs_in.queued, msg, tmp){ msg->dup = 0; db__msg_add_to_queued_stats(&context->msgs_in, msg); if(db__ready_for_flight(context, mosq_md_in, msg->qos)){ switch(msg->qos){ case 0: msg->state = mosq_ms_publish_qos0; break; case 1: msg->state = mosq_ms_publish_qos1; break; case 2: msg->state = mosq_ms_publish_qos2; break; } db__message_dequeue_first(context, &context->msgs_in); } } return MOSQ_ERR_SUCCESS; } int db__message_reconnect_reset(struct mosquitto *context) { int rc; rc = db__message_reconnect_reset_outgoing(context); if(rc) return rc; return db__message_reconnect_reset_incoming(context); } int db__message_remove_incoming(struct mosquitto* context, uint16_t mid) { struct mosquitto_client_msg *tail, *tmp; if(!context) return MOSQ_ERR_INVAL; DL_FOREACH_SAFE(context->msgs_in.inflight, tail, tmp){ if(tail->mid == mid) { if(tail->store->qos != 2){ return MOSQ_ERR_PROTOCOL; } db__message_remove_from_inflight(&context->msgs_in, tail); return MOSQ_ERR_SUCCESS; } } return MOSQ_ERR_NOT_FOUND; } int db__message_release_incoming(struct mosquitto *context, uint16_t mid) { struct mosquitto_client_msg *tail, *tmp; int retain; char *topic; char *source_id; int msg_index = 0; bool deleted = false; int rc; if(!context) return MOSQ_ERR_INVAL; DL_FOREACH_SAFE(context->msgs_in.inflight, tail, tmp){ msg_index++; if(tail->mid == mid){ if(tail->store->qos != 2){ return MOSQ_ERR_PROTOCOL; } topic = tail->store->topic; retain = tail->retain; source_id = tail->store->source_id; /* topic==NULL should be a QoS 2 message that was * denied/dropped and is being processed so the client doesn't * keep resending it. That means we don't send it to other * clients. */ if(topic == NULL){ db__message_remove_from_inflight(&context->msgs_in, tail); deleted = true; }else{ rc = sub__messages_queue(source_id, topic, 2, retain, &tail->store); if(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_NO_SUBSCRIBERS){ db__message_remove_from_inflight(&context->msgs_in, tail); deleted = true; }else{ return 1; } } } } DL_FOREACH_SAFE(context->msgs_in.queued, tail, tmp){ if(db__ready_for_flight(context, mosq_md_in, tail->qos)){ break; } msg_index++; tail->timestamp = db.now_s; if(tail->qos == 2){ send__pubrec(context, tail->mid, 0, NULL); tail->state = mosq_ms_wait_for_pubrel; db__message_dequeue_first(context, &context->msgs_in); } } if(deleted){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOT_FOUND; } } void db__expire_all_messages(struct mosquitto *context) { struct mosquitto_client_msg *msg, *tmp; DL_FOREACH_SAFE(context->msgs_out.inflight, msg, tmp){ if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){ if(msg->qos > 0){ util__increment_send_quota(context); } db__message_remove_from_inflight(&context->msgs_out, msg); } } DL_FOREACH_SAFE(context->msgs_out.queued, msg, tmp){ if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){ db__message_remove_from_queued(&context->msgs_out, msg); } } DL_FOREACH_SAFE(context->msgs_in.inflight, msg, tmp){ if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){ if(msg->qos > 0){ util__increment_receive_quota(context); } db__message_remove_from_inflight(&context->msgs_in, msg); } } DL_FOREACH_SAFE(context->msgs_in.queued, msg, tmp){ if(msg->store->message_expiry_time && db.now_real_s > msg->store->message_expiry_time){ db__message_remove_from_queued(&context->msgs_in, msg); } } } static int db__message_write_inflight_out_single(struct mosquitto *context, struct mosquitto_client_msg *msg) { mosquitto_property *cmsg_props = NULL, *store_props = NULL; int rc; uint16_t mid; int retries; int retain; const char *topic; uint8_t qos; uint32_t payloadlen; const void *payload; uint32_t expiry_interval; expiry_interval = 0; if(msg->store->message_expiry_time){ if(db.now_real_s > msg->store->message_expiry_time){ /* Message is expired, must not send. */ if(msg->direction == mosq_md_out && msg->qos > 0){ util__increment_send_quota(context); } db__message_remove_from_inflight(&context->msgs_out, msg); return MOSQ_ERR_SUCCESS; }else{ expiry_interval = (uint32_t)(msg->store->message_expiry_time - db.now_real_s); } } mid = msg->mid; retries = msg->dup; retain = msg->retain; topic = msg->store->topic; qos = (uint8_t)msg->qos; payloadlen = msg->store->payloadlen; payload = msg->store->payload; cmsg_props = msg->properties; store_props = msg->store->properties; switch(msg->state){ case mosq_ms_publish_qos0: rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, cmsg_props, store_props, expiry_interval); if(rc == MOSQ_ERR_SUCCESS || rc == MOSQ_ERR_OVERSIZE_PACKET){ db__message_remove_from_inflight(&context->msgs_out, msg); }else{ return rc; } break; case mosq_ms_publish_qos1: rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, cmsg_props, store_props, expiry_interval); if(rc == MOSQ_ERR_SUCCESS){ msg->timestamp = db.now_s; msg->dup = 1; /* Any retry attempts are a duplicate. */ msg->state = mosq_ms_wait_for_puback; }else if(rc == MOSQ_ERR_OVERSIZE_PACKET){ db__message_remove_from_inflight(&context->msgs_out, msg); }else{ return rc; } break; case mosq_ms_publish_qos2: rc = send__publish(context, mid, topic, payloadlen, payload, qos, retain, retries, cmsg_props, store_props, expiry_interval); if(rc == MOSQ_ERR_SUCCESS){ msg->timestamp = db.now_s; msg->dup = 1; /* Any retry attempts are a duplicate. */ msg->state = mosq_ms_wait_for_pubrec; }else if(rc == MOSQ_ERR_OVERSIZE_PACKET){ db__message_remove_from_inflight(&context->msgs_out, msg); }else{ return rc; } break; case mosq_ms_resend_pubrel: rc = send__pubrel(context, mid, NULL); if(!rc){ msg->state = mosq_ms_wait_for_pubcomp; }else{ return rc; } break; case mosq_ms_invalid: case mosq_ms_send_pubrec: case mosq_ms_resend_pubcomp: case mosq_ms_wait_for_puback: case mosq_ms_wait_for_pubrec: case mosq_ms_wait_for_pubrel: case mosq_ms_wait_for_pubcomp: case mosq_ms_queued: break; } return MOSQ_ERR_SUCCESS; } int db__message_write_inflight_out_all(struct mosquitto *context) { struct mosquitto_client_msg *tail, *tmp; int rc; if(context->state != mosq_cs_active || context->sock == INVALID_SOCKET){ return MOSQ_ERR_SUCCESS; } DL_FOREACH_SAFE(context->msgs_out.inflight, tail, tmp){ rc = db__message_write_inflight_out_single(context, tail); if(rc) return rc; } return MOSQ_ERR_SUCCESS; } int db__message_write_inflight_out_latest(struct mosquitto *context) { struct mosquitto_client_msg *tail, *next; int rc; if(context->state != mosq_cs_active || context->sock == INVALID_SOCKET || context->msgs_out.inflight == NULL){ return MOSQ_ERR_SUCCESS; } if(context->msgs_out.inflight->prev == context->msgs_out.inflight){ /* Only one message */ return db__message_write_inflight_out_single(context, context->msgs_out.inflight); } /* Start at the end of the list and work backwards looking for the first * message in a non-publish state */ tail = context->msgs_out.inflight->prev; while(tail != context->msgs_out.inflight && (tail->state == mosq_ms_publish_qos0 || tail->state == mosq_ms_publish_qos1 || tail->state == mosq_ms_publish_qos2)){ tail = tail->prev; } /* Tail is now either the head of the list, if that message is waiting for * publish, or the oldest message not waiting for a publish. In the latter * case, any pending publishes should be next after this message. */ if(tail != context->msgs_out.inflight){ tail = tail->next; } while(tail){ next = tail->next; rc = db__message_write_inflight_out_single(context, tail); if(rc) return rc; tail = next; } return MOSQ_ERR_SUCCESS; } int db__message_write_queued_in(struct mosquitto *context) { struct mosquitto_client_msg *tail, *tmp; int rc; if(context->state != mosq_cs_active){ return MOSQ_ERR_SUCCESS; } DL_FOREACH_SAFE(context->msgs_in.queued, tail, tmp){ if(context->msgs_in.inflight_maximum != 0 && context->msgs_in.inflight_quota == 0){ break; } if(tail->qos == 2){ tail->state = mosq_ms_send_pubrec; db__message_dequeue_first(context, &context->msgs_in); rc = send__pubrec(context, tail->mid, 0, NULL); if(!rc){ tail->state = mosq_ms_wait_for_pubrel; }else{ return rc; } } } return MOSQ_ERR_SUCCESS; } int db__message_write_queued_out(struct mosquitto *context) { struct mosquitto_client_msg *tail, *tmp; if(context->state != mosq_cs_active){ return MOSQ_ERR_SUCCESS; } DL_FOREACH_SAFE(context->msgs_out.queued, tail, tmp){ if(!db__ready_for_flight(context, mosq_md_out, tail->qos)){ break; } switch(tail->qos){ case 0: tail->state = mosq_ms_publish_qos0; break; case 1: tail->state = mosq_ms_publish_qos1; break; case 2: tail->state = mosq_ms_publish_qos2; break; } db__message_dequeue_first(context, &context->msgs_out); } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/src/handle_auth.c000066400000000000000000000120571450213760600170660ustar00rootroot00000000000000/* Copyright (c) 2018-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" #include "util_mosq.h" #include "will_mosq.h" int handle__auth(struct mosquitto *context) { int rc = 0; uint8_t reason_code = 0; mosquitto_property *properties = NULL; char *auth_method = NULL; void *auth_data = NULL; uint16_t auth_data_len = 0; void *auth_data_out = NULL; uint16_t auth_data_out_len = 0; if(!context) return MOSQ_ERR_INVAL; if(context->protocol != mosq_p_mqtt5 || context->auth_method == NULL){ return MOSQ_ERR_PROTOCOL; } if(context->in_packet.command != CMD_AUTH){ return MOSQ_ERR_MALFORMED_PACKET; } if(context->in_packet.remaining_length > 0){ if(packet__read_byte(&context->in_packet, &reason_code)) return MOSQ_ERR_MALFORMED_PACKET; if(reason_code != MQTT_RC_CONTINUE_AUTHENTICATION && reason_code != MQTT_RC_REAUTHENTICATE){ send__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL); return MOSQ_ERR_PROTOCOL; } if((reason_code == MQTT_RC_REAUTHENTICATE && context->state != mosq_cs_active) || (reason_code == MQTT_RC_CONTINUE_AUTHENTICATION && context->state != mosq_cs_authenticating && context->state != mosq_cs_reauthenticating)){ send__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL); return MOSQ_ERR_PROTOCOL; } rc = property__read_all(CMD_AUTH, &context->in_packet, &properties); if(rc){ send__disconnect(context, MQTT_RC_UNSPECIFIED, NULL); return rc; } if(mosquitto_property_read_string(properties, MQTT_PROP_AUTHENTICATION_METHOD, &auth_method, false) == NULL){ mosquitto_property_free_all(&properties); send__disconnect(context, MQTT_RC_UNSPECIFIED, NULL); return MOSQ_ERR_PROTOCOL; } if(!auth_method || strcmp(auth_method, context->auth_method)){ /* No method, or non-matching method */ mosquitto__free(auth_method); mosquitto_property_free_all(&properties); send__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL); return MOSQ_ERR_PROTOCOL; } mosquitto__free(auth_method); mosquitto_property_read_binary(properties, MQTT_PROP_AUTHENTICATION_DATA, &auth_data, &auth_data_len, false); mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ } log__printf(NULL, MOSQ_LOG_DEBUG, "Received AUTH from %s (rc%d, %s)", context->id, reason_code, context->auth_method); if(reason_code == MQTT_RC_REAUTHENTICATE){ /* This is a re-authentication attempt */ mosquitto__set_state(context, mosq_cs_reauthenticating); rc = mosquitto_security_auth_start(context, true, auth_data, auth_data_len, &auth_data_out, &auth_data_out_len); }else{ if(context->state != mosq_cs_reauthenticating){ mosquitto__set_state(context, mosq_cs_authenticating); } rc = mosquitto_security_auth_continue(context, auth_data, auth_data_len, &auth_data_out, &auth_data_out_len); } mosquitto__free(auth_data); if(rc == MOSQ_ERR_SUCCESS){ if(context->state == mosq_cs_authenticating){ return connect__on_authorised(context, auth_data_out, auth_data_out_len); }else{ mosquitto__set_state(context, mosq_cs_active); rc = send__auth(context, MQTT_RC_SUCCESS, auth_data_out, auth_data_out_len); free(auth_data_out); return rc; } }else if(rc == MOSQ_ERR_AUTH_CONTINUE){ rc = send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len); free(auth_data_out); return rc; }else{ free(auth_data_out); if(context->state == mosq_cs_authenticating && context->will){ /* Free will without sending if this is our first authentication attempt */ will__clear(context); } if(rc == MOSQ_ERR_AUTH){ if(context->state == mosq_cs_authenticating){ send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL); mosquitto__free(context->id); context->id = NULL; }else{ send__disconnect(context, MQTT_RC_NOT_AUTHORIZED, NULL); } return MOSQ_ERR_PROTOCOL; }else if(rc == MOSQ_ERR_NOT_SUPPORTED){ /* Client has requested extended authentication, but we don't support it. */ if(context->state == mosq_cs_authenticating){ send__connack(context, 0, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL); mosquitto__free(context->id); context->id = NULL; }else{ send__disconnect(context, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL); } return MOSQ_ERR_PROTOCOL; }else{ if(context->state == mosq_cs_authenticating){ mosquitto__free(context->id); context->id = NULL; } return rc; } } } mosquitto-2.0.18/src/handle_connack.c000066400000000000000000000133421450213760600175370ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "send_mosq.h" #include "util_mosq.h" int handle__connack(struct mosquitto *context) { int rc; uint8_t connect_acknowledge; uint8_t reason_code; mosquitto_property *properties = NULL; uint32_t maximum_packet_size; uint8_t retain_available; uint16_t server_keepalive; uint16_t inflight_maximum; uint8_t max_qos = 255; if(context == NULL){ return MOSQ_ERR_INVAL; } if(context->bridge == NULL){ return MOSQ_ERR_PROTOCOL; } if(context->in_packet.command != CMD_CONNACK){ return MOSQ_ERR_MALFORMED_PACKET; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received CONNACK on connection %s.", context->id); if(packet__read_byte(&context->in_packet, &connect_acknowledge)) return MOSQ_ERR_MALFORMED_PACKET; if(packet__read_byte(&context->in_packet, &reason_code)) return MOSQ_ERR_MALFORMED_PACKET; if(context->protocol == mosq_p_mqtt5){ if(context->in_packet.remaining_length == 2 && reason_code == CONNACK_REFUSED_PROTOCOL_VERSION){ /* We have connected to a MQTT v3.x broker that doesn't support MQTT v5.0 * It has correctly replied with a CONNACK code of a bad protocol version. */ log__printf(NULL, MOSQ_LOG_NOTICE, "Warning: Remote bridge %s does not support MQTT v5.0, reconnecting using MQTT v3.1.1.", context->bridge->name); context->protocol = mosq_p_mqtt311; context->bridge->protocol_version = mosq_p_mqtt311; return MOSQ_ERR_PROTOCOL; } rc = property__read_all(CMD_CONNACK, &context->in_packet, &properties); if(rc) return rc; /* maximum-qos */ mosquitto_property_read_byte(properties, MQTT_PROP_MAXIMUM_QOS, &max_qos, false); /* maximum-packet-size */ if(mosquitto_property_read_int32(properties, MQTT_PROP_MAXIMUM_PACKET_SIZE, &maximum_packet_size, false)){ if(context->maximum_packet_size == 0 || context->maximum_packet_size > maximum_packet_size){ context->maximum_packet_size = maximum_packet_size; } } /* receive-maximum */ inflight_maximum = context->msgs_out.inflight_maximum; mosquitto_property_read_int16(properties, MQTT_PROP_RECEIVE_MAXIMUM, &inflight_maximum, false); if(context->msgs_out.inflight_maximum != inflight_maximum){ context->msgs_out.inflight_maximum = inflight_maximum; db__message_reconnect_reset(context); } /* retain-available */ if(mosquitto_property_read_byte(properties, MQTT_PROP_RETAIN_AVAILABLE, &retain_available, false)){ /* Only use broker provided value if the local config is set to available==true */ if(context->retain_available){ context->retain_available = retain_available; } } /* server-keepalive */ if(mosquitto_property_read_int16(properties, MQTT_PROP_SERVER_KEEP_ALIVE, &server_keepalive, false)){ context->keepalive = server_keepalive; } mosquitto_property_free_all(&properties); } mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ if(reason_code == MQTT_RC_SUCCESS){ #ifdef WITH_BRIDGE if(context->bridge){ rc = bridge__on_connect(context); if(rc) return rc; } #endif if(max_qos != 255){ context->max_qos = max_qos; } mosquitto__set_state(context, mosq_cs_active); rc = db__message_write_queued_out(context); if(rc) return rc; rc = db__message_write_inflight_out_all(context); return rc; }else{ if(context->protocol == mosq_p_mqtt5){ switch(reason_code){ case MQTT_RC_RETAIN_NOT_SUPPORTED: context->retain_available = 0; log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: retain not available (will retry)"); return MOSQ_ERR_CONN_LOST; case MQTT_RC_QOS_NOT_SUPPORTED: if(max_qos == 255){ if(context->max_qos != 0){ context->max_qos--; } }else{ context->max_qos = max_qos; } log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: QoS not supported (will retry)"); return MOSQ_ERR_CONN_LOST; default: log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: %s", mosquitto_reason_string(reason_code)); return MOSQ_ERR_CONN_LOST; } }else{ switch(reason_code){ case CONNACK_REFUSED_PROTOCOL_VERSION: if(context->bridge){ context->bridge->try_private_accepted = false; } log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: unacceptable protocol version"); return MOSQ_ERR_CONN_LOST; case CONNACK_REFUSED_IDENTIFIER_REJECTED: log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: identifier rejected"); return MOSQ_ERR_CONN_LOST; case CONNACK_REFUSED_SERVER_UNAVAILABLE: log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: broker unavailable"); return MOSQ_ERR_CONN_LOST; case CONNACK_REFUSED_BAD_USERNAME_PASSWORD: log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: bad user name or password"); return MOSQ_ERR_CONN_LOST; case CONNACK_REFUSED_NOT_AUTHORIZED: log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: not authorised"); return MOSQ_ERR_CONN_LOST; default: log__printf(NULL, MOSQ_LOG_ERR, "Connection Refused: unknown reason"); return MOSQ_ERR_CONN_LOST; } } } return MOSQ_ERR_CONN_LOST; } mosquitto-2.0.18/src/handle_connect.c000066400000000000000000000745351450213760600175670ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" #include "sys_tree.h" #include "time_mosq.h" #include "tls_mosq.h" #include "util_mosq.h" #include "will_mosq.h" #ifdef WITH_WEBSOCKETS # include #endif static char nibble_to_hex(uint8_t value) { if(value < 0x0A){ return (char)('0'+value); }else{ return (char)(65 /*'A'*/ +value-10); } } static char *client_id_gen(uint16_t *idlen, const char *auto_id_prefix, uint16_t auto_id_prefix_len) { char *client_id; uint8_t rnd[16]; int i; int pos; if(util__random_bytes(rnd, 16)) return NULL; *idlen = (uint16_t)(auto_id_prefix_len + 36); client_id = (char *)mosquitto__calloc((size_t)(*idlen) + 1, sizeof(char)); if(!client_id){ return NULL; } if(auto_id_prefix){ memcpy(client_id, auto_id_prefix, auto_id_prefix_len); } pos = 0; for(i=0; i<16; i++){ client_id[auto_id_prefix_len + pos + 0] = nibble_to_hex(rnd[i] & 0x0F); client_id[auto_id_prefix_len + pos + 1] = nibble_to_hex((rnd[i] >> 4) & 0x0F); pos += 2; if(pos == 8 || pos == 13 || pos == 18 || pos == 23){ client_id[auto_id_prefix_len + pos] = '-'; pos++; } } return client_id; } /* Remove any queued messages that are no longer allowed through ACL, * assuming a possible change of username. */ static void connection_check_acl(struct mosquitto *context, struct mosquitto_client_msg **head) { struct mosquitto_client_msg *msg_tail, *tmp; int access; DL_FOREACH_SAFE((*head), msg_tail, tmp){ if(msg_tail->direction == mosq_md_out){ access = MOSQ_ACL_READ; }else{ access = MOSQ_ACL_WRITE; } if(mosquitto_acl_check(context, msg_tail->store->topic, msg_tail->store->payloadlen, msg_tail->store->payload, msg_tail->store->qos, msg_tail->store->retain, access) != MOSQ_ERR_SUCCESS){ DL_DELETE((*head), msg_tail); db__msg_store_ref_dec(&msg_tail->store); mosquitto_property_free_all(&msg_tail->properties); mosquitto__free(msg_tail); } } } int connect__on_authorised(struct mosquitto *context, void *auth_data_out, uint16_t auth_data_out_len) { struct mosquitto *found_context; struct mosquitto__subleaf *leaf; mosquitto_property *connack_props = NULL; uint8_t connect_ack = 0; int i; int rc; int in_quota, out_quota; uint16_t in_maximum, out_maximum; /* Find if this client already has an entry. This must be done *after* any security checks. */ HASH_FIND(hh_id, db.contexts_by_id, context->id, strlen(context->id), found_context); if(found_context){ /* Found a matching client */ if(found_context->sock == INVALID_SOCKET){ /* Client is reconnecting after a disconnect */ /* FIXME - does anything need to be done here? */ }else{ /* Client is already connected, disconnect old version. This is * done in context__cleanup() below. */ if(db.config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_ERR, "Client %s already connected, closing old connection.", context->id); } } if(context->clean_start == false && found_context->session_expiry_interval > 0){ if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ connect_ack |= 0x01; } if(found_context->msgs_in.inflight || found_context->msgs_in.queued || found_context->msgs_out.inflight || found_context->msgs_out.queued){ in_quota = context->msgs_in.inflight_quota; out_quota = context->msgs_out.inflight_quota; in_maximum = context->msgs_in.inflight_maximum; out_maximum = context->msgs_out.inflight_maximum; memcpy(&context->msgs_in, &found_context->msgs_in, sizeof(struct mosquitto_msg_data)); memcpy(&context->msgs_out, &found_context->msgs_out, sizeof(struct mosquitto_msg_data)); memset(&found_context->msgs_in, 0, sizeof(struct mosquitto_msg_data)); memset(&found_context->msgs_out, 0, sizeof(struct mosquitto_msg_data)); context->msgs_in.inflight_quota = in_quota; context->msgs_out.inflight_quota = out_quota; context->msgs_in.inflight_maximum = in_maximum; context->msgs_out.inflight_maximum = out_maximum; db__message_reconnect_reset(context); } context->subs = found_context->subs; found_context->subs = NULL; context->sub_count = found_context->sub_count; found_context->sub_count = 0; context->last_mid = found_context->last_mid; for(i=0; isub_count; i++){ if(context->subs[i]){ leaf = context->subs[i]->hier->subs; while(leaf){ if(leaf->context == found_context){ leaf->context = context; } leaf = leaf->next; } if(context->subs[i]->shared){ leaf = context->subs[i]->shared->subs; while(leaf){ if(leaf->context == found_context){ leaf->context = context; } leaf = leaf->next; } } } } } if(context->clean_start == true){ sub__clean_session(found_context); } if((found_context->protocol == mosq_p_mqtt5 && found_context->session_expiry_interval == 0) || (found_context->protocol != mosq_p_mqtt5 && found_context->clean_start == true) || (context->clean_start == true) ){ context__send_will(found_context); } session_expiry__remove(found_context); will_delay__remove(found_context); will__clear(found_context); found_context->clean_start = true; found_context->session_expiry_interval = 0; mosquitto__set_state(found_context, mosq_cs_duplicate); if(found_context->protocol == mosq_p_mqtt5){ send__disconnect(found_context, MQTT_RC_SESSION_TAKEN_OVER, NULL); } do_disconnect(found_context, MOSQ_ERR_SUCCESS); } rc = acl__find_acls(context); if(rc){ free(auth_data_out); return rc; } if(db.config->connection_messages == true){ if(context->is_bridge){ if(context->username){ log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s:%d as %s (p%d, c%d, k%d, u'%s').", context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive, context->username); }else{ log__printf(NULL, MOSQ_LOG_NOTICE, "New bridge connected from %s:%d as %s (p%d, c%d, k%d).", context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive); } }else{ if(context->username){ log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s:%d as %s (p%d, c%d, k%d, u'%s').", context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive, context->username); }else{ log__printf(NULL, MOSQ_LOG_NOTICE, "New client connected from %s:%d as %s (p%d, c%d, k%d).", context->address, context->remote_port, context->id, context->protocol, context->clean_start, context->keepalive); } } if(context->will) { log__printf(NULL, MOSQ_LOG_DEBUG, "Will message specified (%ld bytes) (r%d, q%d).", (long)context->will->msg.payloadlen, context->will->msg.retain, context->will->msg.qos); log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", context->will->msg.topic); } else { log__printf(NULL, MOSQ_LOG_DEBUG, "No will message specified."); } } context->ping_t = 0; context->is_dropping = false; connection_check_acl(context, &context->msgs_in.inflight); connection_check_acl(context, &context->msgs_in.queued); connection_check_acl(context, &context->msgs_out.inflight); connection_check_acl(context, &context->msgs_out.queued); context__add_to_by_id(context); #ifdef WITH_PERSISTENCE if(!context->clean_start){ db.persistence_changes++; } #endif context->max_qos = context->listener->max_qos; if(db.config->max_keepalive && (context->keepalive > db.config->max_keepalive || context->keepalive == 0)){ context->keepalive = db.config->max_keepalive; if(context->protocol == mosq_p_mqtt5){ if(mosquitto_property_add_int16(&connack_props, MQTT_PROP_SERVER_KEEP_ALIVE, context->keepalive)){ rc = MOSQ_ERR_NOMEM; goto error; } }else{ send__connack(context, connect_ack, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL); rc = MOSQ_ERR_INVAL; goto error; } } if(context->protocol == mosq_p_mqtt5){ if(context->listener->max_topic_alias > 0){ if(mosquitto_property_add_int16(&connack_props, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, context->listener->max_topic_alias)){ rc = MOSQ_ERR_NOMEM; goto error; } } if(context->assigned_id){ if(mosquitto_property_add_string(&connack_props, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, context->id)){ rc = MOSQ_ERR_NOMEM; goto error; } } if(context->auth_method){ if(mosquitto_property_add_string(&connack_props, MQTT_PROP_AUTHENTICATION_METHOD, context->auth_method)){ rc = MOSQ_ERR_NOMEM; goto error; } if(auth_data_out && auth_data_out_len > 0){ if(mosquitto_property_add_binary(&connack_props, MQTT_PROP_AUTHENTICATION_DATA, auth_data_out, auth_data_out_len)){ rc = MOSQ_ERR_NOMEM; goto error; } } } } free(auth_data_out); auth_data_out = NULL; keepalive__add(context); mosquitto__set_state(context, mosq_cs_active); rc = send__connack(context, connect_ack, CONNACK_ACCEPTED, connack_props); mosquitto_property_free_all(&connack_props); if(rc) return rc; db__expire_all_messages(context); rc = db__message_write_queued_out(context); if(rc) return rc; rc = db__message_write_inflight_out_all(context); return rc; error: free(auth_data_out); mosquitto_property_free_all(&connack_props); return rc; } static int will__read(struct mosquitto *context, const char *client_id, struct mosquitto_message_all **will, uint8_t will_qos, int will_retain) { int rc = MOSQ_ERR_SUCCESS; size_t slen; uint16_t tlen; struct mosquitto_message_all *will_struct = NULL; char *will_topic_mount = NULL; uint16_t payloadlen; mosquitto_property *properties = NULL; will_struct = mosquitto__calloc(1, sizeof(struct mosquitto_message_all)); if(!will_struct){ rc = MOSQ_ERR_NOMEM; goto error_cleanup; } if(context->protocol == PROTOCOL_VERSION_v5){ rc = property__read_all(CMD_WILL, &context->in_packet, &properties); if(rc) goto error_cleanup; rc = property__process_will(context, will_struct, &properties); mosquitto_property_free_all(&properties); if(rc) goto error_cleanup; } rc = packet__read_string(&context->in_packet, &will_struct->msg.topic, &tlen); if(rc) goto error_cleanup; if(!tlen){ rc = MOSQ_ERR_PROTOCOL; goto error_cleanup; } if(context->listener->mount_point){ slen = strlen(context->listener->mount_point) + strlen(will_struct->msg.topic) + 1; will_topic_mount = mosquitto__malloc(slen+1); if(!will_topic_mount){ rc = MOSQ_ERR_NOMEM; goto error_cleanup; } snprintf(will_topic_mount, slen, "%s%s", context->listener->mount_point, will_struct->msg.topic); will_topic_mount[slen] = '\0'; mosquitto__free(will_struct->msg.topic); will_struct->msg.topic = will_topic_mount; } if(!strncmp(will_struct->msg.topic, "$CONTROL/", strlen("$CONTROL/"))){ rc = MOSQ_ERR_ACL_DENIED; goto error_cleanup; } rc = mosquitto_pub_topic_check(will_struct->msg.topic); if(rc) goto error_cleanup; rc = packet__read_uint16(&context->in_packet, &payloadlen); if(rc) goto error_cleanup; will_struct->msg.payloadlen = payloadlen; if(will_struct->msg.payloadlen > 0){ if(db.config->message_size_limit && will_struct->msg.payloadlen > (int)db.config->message_size_limit){ log__printf(NULL, MOSQ_LOG_DEBUG, "Client %s connected with too large Will payload", client_id); if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_PACKET_TOO_LARGE, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); } rc = MOSQ_ERR_PAYLOAD_SIZE; goto error_cleanup; } will_struct->msg.payload = mosquitto__malloc((size_t)will_struct->msg.payloadlen); if(!will_struct->msg.payload){ rc = MOSQ_ERR_NOMEM; goto error_cleanup; } rc = packet__read_bytes(&context->in_packet, will_struct->msg.payload, (uint32_t)will_struct->msg.payloadlen); if(rc) goto error_cleanup; } will_struct->msg.qos = will_qos; will_struct->msg.retain = will_retain; *will = will_struct; return MOSQ_ERR_SUCCESS; error_cleanup: if(will_struct){ mosquitto__free(will_struct->msg.topic); mosquitto__free(will_struct->msg.payload); mosquitto_property_free_all(&will_struct->properties); mosquitto__free(will_struct); } return rc; } int handle__connect(struct mosquitto *context) { char protocol_name[7]; uint8_t protocol_version; uint8_t connect_flags; char *client_id = NULL; struct mosquitto_message_all *will_struct = NULL; uint8_t will, will_retain, will_qos, clean_start; uint8_t username_flag, password_flag; char *username = NULL, *password = NULL; int rc; uint16_t slen; mosquitto_property *properties = NULL; void *auth_data = NULL; uint16_t auth_data_len = 0; void *auth_data_out = NULL; uint16_t auth_data_out_len = 0; bool allow_zero_length_clientid; #ifdef WITH_TLS int i; X509 *client_cert = NULL; X509_NAME *name; X509_NAME_ENTRY *name_entry; ASN1_STRING *name_asn1 = NULL; BIO *subject_bio; char *data_start; long name_length; char *subject; #endif G_CONNECTION_COUNT_INC(); if(!context->listener){ return MOSQ_ERR_INVAL; } /* Don't accept multiple CONNECT commands. */ if(context->state != mosq_cs_new){ log__printf(NULL, MOSQ_LOG_NOTICE, "Bad client %s sending multiple CONNECT messages.", context->id); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } /* Read protocol name as length then bytes rather than with read_string * because the length is fixed and we can check that. Removes the need * for another malloc as well. */ if(packet__read_uint16(&context->in_packet, &slen)){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if(slen != 4 /* MQTT */ && slen != 6 /* MQIsdp */){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if(packet__read_bytes(&context->in_packet, protocol_name, slen)){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } protocol_name[slen] = '\0'; if(packet__read_byte(&context->in_packet, &protocol_version)){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if(!strcmp(protocol_name, PROTOCOL_NAME_v31)){ if((protocol_version&0x7F) != PROTOCOL_VERSION_v31){ if(db.config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol version %d in CONNECT from %s.", protocol_version, context->address); } send__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } context->protocol = mosq_p_mqtt31; if((protocol_version&0x80) == 0x80){ context->is_bridge = true; } }else if(!strcmp(protocol_name, PROTOCOL_NAME)){ if((protocol_version&0x7F) == PROTOCOL_VERSION_v311){ context->protocol = mosq_p_mqtt311; if((protocol_version&0x80) == 0x80){ context->is_bridge = true; } }else if((protocol_version&0x7F) == PROTOCOL_VERSION_v5){ context->protocol = mosq_p_mqtt5; }else{ if(db.config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol version %d in CONNECT from %s.", protocol_version, context->address); } send__connack(context, 0, CONNACK_REFUSED_PROTOCOL_VERSION, NULL); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if((context->in_packet.command&0x0F) != 0x00){ /* Reserved flags not set to 0, must disconnect. */ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } }else{ if(db.config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid protocol \"%s\" in CONNECT from %s.", protocol_name, context->address); } rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if((protocol_version&0x7F) != PROTOCOL_VERSION_v31 && context->in_packet.command != CMD_CONNECT){ return MOSQ_ERR_MALFORMED_PACKET; } if(packet__read_byte(&context->in_packet, &connect_flags)){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if((connect_flags & 0x01) != 0x00){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } } clean_start = (connect_flags & 0x02) >> 1; /* session_expiry_interval will be overriden if the properties are read later */ if(clean_start == false && protocol_version != PROTOCOL_VERSION_v5){ /* v3* has clean_start == false mean the session never expires */ context->session_expiry_interval = UINT32_MAX; }else{ context->session_expiry_interval = 0; } will = connect_flags & 0x04; will_qos = (connect_flags & 0x18) >> 3; if(will_qos == 3){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid Will QoS in CONNECT from %s.", context->address); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } will_retain = ((connect_flags & 0x20) == 0x20); password_flag = connect_flags & 0x40; username_flag = connect_flags & 0x80; if(will && will_retain && db.config->retain_available == false){ if(protocol_version == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL); } rc = MOSQ_ERR_NOT_SUPPORTED; goto handle_connect_error; } if(packet__read_uint16(&context->in_packet, &(context->keepalive))){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if(protocol_version == PROTOCOL_VERSION_v5){ rc = property__read_all(CMD_CONNECT, &context->in_packet, &properties); if(rc) goto handle_connect_error; } property__process_connect(context, &properties); if(will && will_qos > context->listener->max_qos){ if(protocol_version == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_QOS_NOT_SUPPORTED, NULL); } rc = MOSQ_ERR_NOT_SUPPORTED; goto handle_connect_error; } if(mosquitto_property_read_string(properties, MQTT_PROP_AUTHENTICATION_METHOD, &context->auth_method, false)){ mosquitto_property_read_binary(properties, MQTT_PROP_AUTHENTICATION_DATA, &auth_data, &auth_data_len, false); } mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ if(packet__read_string(&context->in_packet, &client_id, &slen)){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } if(slen == 0){ if(context->protocol == mosq_p_mqtt31){ send__connack(context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; }else{ /* mqtt311/mqtt5 */ mosquitto__free(client_id); client_id = NULL; if(db.config->per_listener_settings){ allow_zero_length_clientid = context->listener->security_options.allow_zero_length_clientid; }else{ allow_zero_length_clientid = db.config->security_options.allow_zero_length_clientid; } if((context->protocol == mosq_p_mqtt311 && clean_start == 0) || allow_zero_length_clientid == false){ if(context->protocol == mosq_p_mqtt311){ send__connack(context, 0, CONNACK_REFUSED_IDENTIFIER_REJECTED, NULL); }else{ send__connack(context, 0, MQTT_RC_UNSPECIFIED, NULL); } rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; }else{ if(db.config->per_listener_settings){ client_id = client_id_gen(&slen, context->listener->security_options.auto_id_prefix, context->listener->security_options.auto_id_prefix_len); }else{ client_id = client_id_gen(&slen, db.config->security_options.auto_id_prefix, db.config->security_options.auto_id_prefix_len); } if(!client_id){ rc = MOSQ_ERR_NOMEM; goto handle_connect_error; } context->assigned_id = true; } } } /* clientid_prefixes check */ if(db.config->clientid_prefixes){ if(strncmp(db.config->clientid_prefixes, client_id, strlen(db.config->clientid_prefixes))){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } } if(will){ rc = will__read(context, client_id, &will_struct, will_qos, will_retain); if(rc) goto handle_connect_error; }else{ if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if(will_qos != 0 || will_retain != 0){ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } } } if(username_flag){ rc = packet__read_string(&context->in_packet, &username, &slen); if(rc == MOSQ_ERR_NOMEM){ rc = MOSQ_ERR_NOMEM; goto handle_connect_error; }else if(rc != MOSQ_ERR_SUCCESS){ if(context->protocol == mosq_p_mqtt31){ /* Username flag given, but no username. Ignore. */ username_flag = 0; }else{ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } } }else{ if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt31){ if(password_flag){ /* username_flag == 0 && password_flag == 1 is forbidden */ log__printf(NULL, MOSQ_LOG_ERR, "Protocol error from %s: password without username, closing connection.", client_id); rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } } } if(password_flag){ rc = packet__read_binary(&context->in_packet, (uint8_t **)&password, &slen); if(rc == MOSQ_ERR_NOMEM){ rc = MOSQ_ERR_NOMEM; goto handle_connect_error; }else if(rc == MOSQ_ERR_MALFORMED_PACKET){ if(context->protocol == mosq_p_mqtt31){ /* Password flag given, but no password. Ignore. */ }else{ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } } } if(context->in_packet.pos != context->in_packet.remaining_length){ /* Surplus data at end of packet, this must be an error. */ rc = MOSQ_ERR_PROTOCOL; goto handle_connect_error; } /* Once context->id is set, if we return from this function with an error * we must make sure that context->id is freed and set to NULL, so that the * client isn't erroneously removed from the by_id hash table. */ context->id = client_id; client_id = NULL; #ifdef WITH_TLS if(context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){ /* Don't need the username or password if provided */ mosquitto__free(username); username = NULL; mosquitto__free(password); password = NULL; if(!context->ssl){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } #ifdef FINAL_WITH_TLS_PSK if(context->listener->psk_hint){ /* Client should have provided an identity to get this far. */ if(!context->username){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } }else{ #endif /* FINAL_WITH_TLS_PSK */ client_cert = SSL_get_peer_certificate(context->ssl); if(!client_cert){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } name = X509_get_subject_name(client_cert); if(!name){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } if (context->listener->use_identity_as_username) { /* use_identity_as_username */ i = X509_NAME_get_index_by_NID(name, NID_commonName, -1); if(i == -1){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } name_entry = X509_NAME_get_entry(name, i); if(name_entry){ name_asn1 = X509_NAME_ENTRY_get_data(name_entry); if (name_asn1 == NULL) { if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } const char *new_username; #if OPENSSL_VERSION_NUMBER < 0x10100000L new_username = (const char *) ASN1_STRING_data(name_asn1); #else new_username = (const char *) ASN1_STRING_get0_data(name_asn1); #endif if(mosquitto_validate_utf8(new_username, (int)strlen(new_username))){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } X509_free(client_cert); return MOSQ_ERR_AUTH; } context->username = mosquitto__strdup(new_username); if(!context->username){ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_SERVER_UNAVAILABLE, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_SERVER_UNAVAILABLE, NULL); } rc = MOSQ_ERR_NOMEM; goto handle_connect_error; } /* Make sure there isn't an embedded NUL character in the CN */ if ((size_t)ASN1_STRING_length(name_asn1) != strlen(context->username)) { if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_BAD_USERNAME_OR_PASSWORD, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_BAD_USERNAME_PASSWORD, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } } } else { /* use_subject_as_username */ subject_bio = BIO_new(BIO_s_mem()); X509_NAME_print_ex(subject_bio, X509_get_subject_name(client_cert), 0, XN_FLAG_RFC2253); data_start = NULL; name_length = BIO_get_mem_data(subject_bio, &data_start); subject = mosquitto__malloc(sizeof(char)*(size_t)(name_length+1)); if(!subject){ BIO_free(subject_bio); rc = MOSQ_ERR_NOMEM; goto handle_connect_error; } memcpy(subject, data_start, (size_t)name_length); subject[name_length] = '\0'; BIO_free(subject_bio); context->username = subject; } if(!context->username){ rc = MOSQ_ERR_AUTH; goto handle_connect_error; } X509_free(client_cert); client_cert = NULL; #ifdef FINAL_WITH_TLS_PSK } #endif /* FINAL_WITH_TLS_PSK */ }else #endif /* WITH_TLS */ { /* FIXME - these ensure the mosquitto_client_id() and * mosquitto_client_username() functions work, but is hacky */ context->username = username; context->password = password; username = NULL; /* Avoid free() in error: below. */ password = NULL; } if(context->listener->use_username_as_clientid){ if(context->username){ mosquitto__free(context->id); context->id = mosquitto__strdup(context->username); if(!context->id){ rc = MOSQ_ERR_NOMEM; goto handle_connect_error; } }else{ if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; } } context->clean_start = clean_start; context->will = will_struct; will_struct = NULL; if(context->auth_method){ rc = mosquitto_security_auth_start(context, false, auth_data, auth_data_len, &auth_data_out, &auth_data_out_len); mosquitto__free(auth_data); auth_data = NULL; if(rc == MOSQ_ERR_SUCCESS){ return connect__on_authorised(context, auth_data_out, auth_data_out_len); }else if(rc == MOSQ_ERR_AUTH_CONTINUE){ mosquitto__set_state(context, mosq_cs_authenticating); rc = send__auth(context, MQTT_RC_CONTINUE_AUTHENTICATION, auth_data_out, auth_data_out_len); free(auth_data_out); return rc; }else{ free(auth_data_out); auth_data_out = NULL; will__clear(context); if(rc == MOSQ_ERR_AUTH){ send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL); mosquitto__free(context->id); context->id = NULL; goto handle_connect_error; }else if(rc == MOSQ_ERR_NOT_SUPPORTED){ /* Client has requested extended authentication, but we don't support it. */ send__connack(context, 0, MQTT_RC_BAD_AUTHENTICATION_METHOD, NULL); mosquitto__free(context->id); context->id = NULL; goto handle_connect_error; }else{ mosquitto__free(context->id); context->id = NULL; goto handle_connect_error; } } }else{ #ifdef WITH_TLS if(context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){ /* Authentication assumed to be cleared */ }else #endif { rc = mosquitto_unpwd_check(context); switch(rc){ case MOSQ_ERR_SUCCESS: break; case MOSQ_ERR_AUTH: if(context->protocol == mosq_p_mqtt5){ send__connack(context, 0, MQTT_RC_NOT_AUTHORIZED, NULL); }else{ send__connack(context, 0, CONNACK_REFUSED_NOT_AUTHORIZED, NULL); } rc = MOSQ_ERR_AUTH; goto handle_connect_error; break; default: rc = MOSQ_ERR_UNKNOWN; goto handle_connect_error; break; } } return connect__on_authorised(context, NULL, 0); } handle_connect_error: mosquitto_property_free_all(&properties); mosquitto__free(auth_data); mosquitto__free(client_id); mosquitto__free(username); mosquitto__free(password); if(will_struct){ mosquitto_property_free_all(&will_struct->properties); mosquitto__free(will_struct->msg.payload); mosquitto__free(will_struct->msg.topic); mosquitto__free(will_struct); } if(context->will){ mosquitto_property_free_all(&context->will->properties); mosquitto__free(context->will->msg.payload); mosquitto__free(context->will->msg.topic); mosquitto__free(context->will); context->will = NULL; } #ifdef WITH_TLS if(client_cert) X509_free(client_cert); #endif /* We return an error here which means the client is freed later on. */ context->clean_start = true; context->session_expiry_interval = 0; context->will_delay_interval = 0; return rc; } mosquitto-2.0.18/src/handle_disconnect.c000066400000000000000000000044021450213760600202510ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" #include "send_mosq.h" #include "util_mosq.h" #include "will_mosq.h" int handle__disconnect(struct mosquitto *context) { int rc; uint8_t reason_code = 0; mosquitto_property *properties = NULL; if(!context){ return MOSQ_ERR_INVAL; } if(context->in_packet.command != CMD_DISCONNECT){ return MOSQ_ERR_MALFORMED_PACKET; } if(context->protocol == mosq_p_mqtt5 && context->in_packet.remaining_length > 0){ /* FIXME - must handle reason code */ rc = packet__read_byte(&context->in_packet, &reason_code); if(rc) return rc; if(context->in_packet.remaining_length > 1){ rc = property__read_all(CMD_DISCONNECT, &context->in_packet, &properties); if(rc) return rc; } } rc = property__process_disconnect(context, &properties); if(rc){ mosquitto_property_free_all(&properties); return rc; } mosquitto_property_free_all(&properties); /* FIXME - TEMPORARY UNTIL PROPERTIES PROCESSED */ if(context->in_packet.pos != context->in_packet.remaining_length){ return MOSQ_ERR_PROTOCOL; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received DISCONNECT from %s", context->id); if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if((context->in_packet.command&0x0F) != 0x00){ do_disconnect(context, MOSQ_ERR_PROTOCOL); return MOSQ_ERR_PROTOCOL; } } if(reason_code == MQTT_RC_DISCONNECT_WITH_WILL_MSG){ mosquitto__set_state(context, mosq_cs_disconnect_with_will); }else{ will__clear(context); mosquitto__set_state(context, mosq_cs_disconnecting); } do_disconnect(context, MOSQ_ERR_SUCCESS); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/src/handle_publish.c000066400000000000000000000253241450213760600175740ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "alias_mosq.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "sys_tree.h" #include "util_mosq.h" int handle__publish(struct mosquitto *context) { uint8_t dup; int rc = 0; int rc2; uint8_t header = context->in_packet.command; int res = 0; struct mosquitto_msg_store *msg, *stored = NULL; struct mosquitto_client_msg *cmsg_stored = NULL; size_t len; uint16_t slen; char *topic_mount; mosquitto_property *properties = NULL; mosquitto_property *p, *p_prev; mosquitto_property *msg_properties_last; uint32_t message_expiry_interval = 0; int topic_alias = -1; uint8_t reason_code = 0; uint16_t mid = 0; if(context->state != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } msg = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store)); if(msg == NULL){ return MOSQ_ERR_NOMEM; } dup = (header & 0x08)>>3; msg->qos = (header & 0x06)>>1; if(dup == 1 && msg->qos == 0){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid PUBLISH (QoS=0 and DUP=1) from %s, disconnecting.", context->id); db__msg_store_free(msg); return MOSQ_ERR_MALFORMED_PACKET; } if(msg->qos == 3){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid QoS in PUBLISH from %s, disconnecting.", context->id); db__msg_store_free(msg); return MOSQ_ERR_MALFORMED_PACKET; } if(msg->qos > context->max_qos){ log__printf(NULL, MOSQ_LOG_INFO, "Too high QoS in PUBLISH from %s, disconnecting.", context->id); db__msg_store_free(msg); return MOSQ_ERR_QOS_NOT_SUPPORTED; } msg->retain = (header & 0x01); if(msg->retain && db.config->retain_available == false){ db__msg_store_free(msg); return MOSQ_ERR_RETAIN_NOT_SUPPORTED; } if(packet__read_string(&context->in_packet, &msg->topic, &slen)){ db__msg_store_free(msg); return MOSQ_ERR_MALFORMED_PACKET; } if(!slen && context->protocol != mosq_p_mqtt5){ /* Invalid publish topic, disconnect client. */ db__msg_store_free(msg); return MOSQ_ERR_MALFORMED_PACKET; } if(msg->qos > 0){ if(packet__read_uint16(&context->in_packet, &mid)){ db__msg_store_free(msg); return MOSQ_ERR_MALFORMED_PACKET; } if(mid == 0){ db__msg_store_free(msg); return MOSQ_ERR_PROTOCOL; } /* It is important to have a separate copy of mid, because msg may be * freed before we want to send a PUBACK/PUBREC. */ msg->source_mid = mid; } /* Handle properties */ if(context->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_PUBLISH, &context->in_packet, &properties); if(rc){ db__msg_store_free(msg); return rc; } p = properties; p_prev = NULL; msg->properties = NULL; msg_properties_last = NULL; while(p){ switch(p->identifier){ case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_CORRELATION_DATA: case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_USER_PROPERTY: if(msg->properties){ msg_properties_last->next = p; msg_properties_last = p; }else{ msg->properties = p; msg_properties_last = p; } if(p_prev){ p_prev->next = p->next; p = p_prev->next; }else{ properties = p->next; p = properties; } msg_properties_last->next = NULL; break; case MQTT_PROP_TOPIC_ALIAS: topic_alias = p->value.i16; p_prev = p; p = p->next; break; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: message_expiry_interval = p->value.i32; p_prev = p; p = p->next; break; case MQTT_PROP_SUBSCRIPTION_IDENTIFIER: p_prev = p; p = p->next; break; default: p = p->next; break; } } } mosquitto_property_free_all(&properties); if(topic_alias == 0 || (context->listener && topic_alias > context->listener->max_topic_alias)){ db__msg_store_free(msg); return MOSQ_ERR_TOPIC_ALIAS_INVALID; }else if(topic_alias > 0){ if(msg->topic){ rc = alias__add(context, msg->topic, (uint16_t)topic_alias); if(rc){ db__msg_store_free(msg); return rc; } }else{ rc = alias__find(context, &msg->topic, (uint16_t)topic_alias); if(rc){ db__msg_store_free(msg); return MOSQ_ERR_PROTOCOL; } } } #ifdef WITH_BRIDGE rc = bridge__remap_topic_in(context, &msg->topic); if(rc){ db__msg_store_free(msg); return rc; } #endif if(mosquitto_pub_topic_check(msg->topic) != MOSQ_ERR_SUCCESS){ /* Invalid publish topic, just swallow it. */ db__msg_store_free(msg); return MOSQ_ERR_MALFORMED_PACKET; } msg->payloadlen = context->in_packet.remaining_length - context->in_packet.pos; G_PUB_BYTES_RECEIVED_INC(msg->payloadlen); if(context->listener && context->listener->mount_point){ len = strlen(context->listener->mount_point) + strlen(msg->topic) + 1; topic_mount = mosquitto__malloc(len+1); if(!topic_mount){ db__msg_store_free(msg); return MOSQ_ERR_NOMEM; } snprintf(topic_mount, len, "%s%s", context->listener->mount_point, msg->topic); topic_mount[len] = '\0'; mosquitto__free(msg->topic); msg->topic = topic_mount; } if(msg->payloadlen){ if(db.config->message_size_limit && msg->payloadlen > db.config->message_size_limit){ log__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, msg->qos, msg->retain, msg->source_mid, msg->topic, (long)msg->payloadlen); reason_code = MQTT_RC_PACKET_TOO_LARGE; goto process_bad_message; } msg->payload = mosquitto__malloc(msg->payloadlen+1); if(msg->payload == NULL){ db__msg_store_free(msg); return MOSQ_ERR_NOMEM; } /* Ensure payload is always zero terminated, this is the reason for the extra byte above */ ((uint8_t *)msg->payload)[msg->payloadlen] = 0; if(packet__read_bytes(&context->in_packet, msg->payload, msg->payloadlen)){ db__msg_store_free(msg); return MOSQ_ERR_MALFORMED_PACKET; } } /* Check for topic access */ rc = mosquitto_acl_check(context, msg->topic, msg->payloadlen, msg->payload, msg->qos, msg->retain, MOSQ_ACL_WRITE); if(rc == MOSQ_ERR_ACL_DENIED){ log__printf(NULL, MOSQ_LOG_DEBUG, "Denied PUBLISH from %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", context->id, dup, msg->qos, msg->retain, msg->source_mid, msg->topic, (long)msg->payloadlen); reason_code = MQTT_RC_NOT_AUTHORIZED; goto process_bad_message; }else if(rc != MOSQ_ERR_SUCCESS){ db__msg_store_free(msg); return rc; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received PUBLISH from %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", context->id, dup, msg->qos, msg->retain, msg->source_mid, msg->topic, (long)msg->payloadlen); if(!strncmp(msg->topic, "$CONTROL/", 9)){ #ifdef WITH_CONTROL rc = control__process(context, msg); db__msg_store_free(msg); return rc; #else reason_code = MQTT_RC_IMPLEMENTATION_SPECIFIC; goto process_bad_message; #endif } { rc = plugin__handle_message(context, msg); if(rc == MOSQ_ERR_ACL_DENIED){ log__printf(NULL, MOSQ_LOG_DEBUG, "Denied PUBLISH from %s (d%d, q%d, r%d, m%d, '%s', ... (%ld bytes))", context->id, dup, msg->qos, msg->retain, msg->source_mid, msg->topic, (long)msg->payloadlen); reason_code = MQTT_RC_NOT_AUTHORIZED; goto process_bad_message; }else if(rc != MOSQ_ERR_SUCCESS){ db__msg_store_free(msg); return rc; } } if(msg->qos > 0){ db__message_store_find(context, msg->source_mid, &cmsg_stored); } if(cmsg_stored && cmsg_stored->store && msg->source_mid != 0 && (cmsg_stored->store->qos != msg->qos || cmsg_stored->store->payloadlen != msg->payloadlen || strcmp(cmsg_stored->store->topic, msg->topic) || memcmp(cmsg_stored->store->payload, msg->payload, msg->payloadlen) )){ log__printf(NULL, MOSQ_LOG_WARNING, "Reused message ID %u from %s detected. Clearing from storage.", msg->source_mid, context->id); db__message_remove_incoming(context, msg->source_mid); cmsg_stored = NULL; } if(!cmsg_stored){ if(msg->qos == 0 || db__ready_for_flight(context, mosq_md_in, msg->qos) ){ dup = 0; rc = db__message_store(context, msg, message_expiry_interval, 0, mosq_mo_client); if(rc) return rc; }else{ /* Client isn't allowed any more incoming messages, so fail early */ reason_code = MQTT_RC_QUOTA_EXCEEDED; goto process_bad_message; } stored = msg; msg = NULL; dup = 0; }else{ db__msg_store_free(msg); msg = NULL; stored = cmsg_stored->store; cmsg_stored->dup++; dup = cmsg_stored->dup; } switch(stored->qos){ case 0: rc2 = sub__messages_queue(context->id, stored->topic, stored->qos, stored->retain, &stored); if(rc2 > 0) rc = 1; break; case 1: util__decrement_receive_quota(context); rc2 = sub__messages_queue(context->id, stored->topic, stored->qos, stored->retain, &stored); /* stored may now be free, so don't refer to it */ if(rc2 == MOSQ_ERR_SUCCESS || context->protocol != mosq_p_mqtt5){ if(send__puback(context, mid, 0, NULL)) rc = 1; }else if(rc2 == MOSQ_ERR_NO_SUBSCRIBERS){ if(send__puback(context, mid, MQTT_RC_NO_MATCHING_SUBSCRIBERS, NULL)) rc = 1; }else{ rc = rc2; } break; case 2: if(dup == 0){ res = db__message_insert(context, stored->source_mid, mosq_md_in, stored->qos, stored->retain, stored, NULL, false); }else{ res = 0; } /* db__message_insert() returns 2 to indicate dropped message * due to queue. This isn't an error so don't disconnect them. */ /* FIXME - this is no longer necessary due to failing early above */ if(!res){ if(dup == 0 || dup == 1){ rc2 = send__pubrec(context, stored->source_mid, 0, NULL); if(rc2) rc = rc2; }else{ return MOSQ_ERR_PROTOCOL; } }else if(res == 1){ rc = 1; } break; } db__message_write_queued_in(context); return rc; process_bad_message: rc = 1; if(msg){ switch(msg->qos){ case 0: rc = MOSQ_ERR_SUCCESS; break; case 1: rc = send__puback(context, msg->source_mid, reason_code, NULL); break; case 2: rc = send__pubrec(context, msg->source_mid, reason_code, NULL); break; } db__msg_store_free(msg); } if(context->out_packet_count >= db.config->max_queued_messages){ rc = MQTT_RC_QUOTA_EXCEEDED; } return rc; } mosquitto-2.0.18/src/handle_subscribe.c000066400000000000000000000151331450213760600201040ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "property_mosq.h" int handle__subscribe(struct mosquitto *context) { int rc = 0; int rc2; uint16_t mid; char *sub; uint8_t subscription_options; uint32_t subscription_identifier = 0; uint8_t qos; uint8_t retain_handling = 0; uint8_t *payload = NULL, *tmp_payload; uint32_t payloadlen = 0; size_t len; uint16_t slen; char *sub_mount; mosquitto_property *properties = NULL; bool allowed; if(!context) return MOSQ_ERR_INVAL; if(context->state != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(context->in_packet.command != (CMD_SUBSCRIBE|2)){ return MOSQ_ERR_MALFORMED_PACKET; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received SUBSCRIBE from %s", context->id); if(context->protocol != mosq_p_mqtt31){ if((context->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_MALFORMED_PACKET; } } if(packet__read_uint16(&context->in_packet, &mid)) return MOSQ_ERR_MALFORMED_PACKET; if(mid == 0) return MOSQ_ERR_MALFORMED_PACKET; if(context->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_SUBSCRIBE, &context->in_packet, &properties); if(rc){ /* FIXME - it would be better if property__read_all() returned * MOSQ_ERR_MALFORMED_PACKET, but this is would change the library * return codes so needs doc changes as well. */ if(rc == MOSQ_ERR_PROTOCOL){ return MOSQ_ERR_MALFORMED_PACKET; }else{ return rc; } } if(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &subscription_identifier, false)){ /* If the identifier was force set to 0, this is an error */ if(subscription_identifier == 0){ mosquitto_property_free_all(&properties); return MOSQ_ERR_MALFORMED_PACKET; } } mosquitto_property_free_all(&properties); /* Note - User Property not handled */ } while(context->in_packet.pos < context->in_packet.remaining_length){ sub = NULL; if(packet__read_string(&context->in_packet, &sub, &slen)){ mosquitto__free(payload); return MOSQ_ERR_MALFORMED_PACKET; } if(sub){ if(!slen){ log__printf(NULL, MOSQ_LOG_INFO, "Empty subscription string from %s, disconnecting.", context->address); mosquitto__free(sub); mosquitto__free(payload); return MOSQ_ERR_MALFORMED_PACKET; } if(mosquitto_sub_topic_check(sub)){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid subscription string from %s, disconnecting.", context->address); mosquitto__free(sub); mosquitto__free(payload); return MOSQ_ERR_MALFORMED_PACKET; } if(packet__read_byte(&context->in_packet, &subscription_options)){ mosquitto__free(sub); mosquitto__free(payload); return MOSQ_ERR_MALFORMED_PACKET; } if(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt311){ qos = subscription_options; if(context->is_bridge){ subscription_options = MQTT_SUB_OPT_RETAIN_AS_PUBLISHED | MQTT_SUB_OPT_NO_LOCAL; } }else{ qos = subscription_options & 0x03; subscription_options &= 0xFC; retain_handling = (subscription_options & 0x30); if(retain_handling == 0x30 || (subscription_options & 0xC0) != 0){ mosquitto__free(sub); mosquitto__free(payload); return MOSQ_ERR_MALFORMED_PACKET; } } if(qos > 2){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid QoS in subscription command from %s, disconnecting.", context->address); mosquitto__free(sub); mosquitto__free(payload); return MOSQ_ERR_MALFORMED_PACKET; } if(qos > context->max_qos){ qos = context->max_qos; } if(context->listener && context->listener->mount_point){ len = strlen(context->listener->mount_point) + slen + 1; sub_mount = mosquitto__malloc(len+1); if(!sub_mount){ mosquitto__free(sub); mosquitto__free(payload); return MOSQ_ERR_NOMEM; } snprintf(sub_mount, len, "%s%s", context->listener->mount_point, sub); sub_mount[len] = '\0'; mosquitto__free(sub); sub = sub_mount; } log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s (QoS %d)", sub, qos); allowed = true; rc2 = mosquitto_acl_check(context, sub, 0, NULL, qos, false, MOSQ_ACL_SUBSCRIBE); switch(rc2){ case MOSQ_ERR_SUCCESS: break; case MOSQ_ERR_ACL_DENIED: allowed = false; if(context->protocol == mosq_p_mqtt5){ qos = MQTT_RC_NOT_AUTHORIZED; }else if(context->protocol == mosq_p_mqtt311){ qos = 0x80; } break; default: mosquitto__free(sub); return rc2; } if(allowed){ rc2 = sub__add(context, sub, qos, subscription_identifier, subscription_options, &db.subs); if(rc2 > 0){ mosquitto__free(sub); return rc2; } if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt31){ if(rc2 == MOSQ_ERR_SUCCESS || rc2 == MOSQ_ERR_SUB_EXISTS){ if(retain__queue(context, sub, qos, 0)) rc = 1; } }else{ if((retain_handling == MQTT_SUB_OPT_SEND_RETAIN_ALWAYS) || (rc2 == MOSQ_ERR_SUCCESS && retain_handling == MQTT_SUB_OPT_SEND_RETAIN_NEW)){ if(retain__queue(context, sub, qos, subscription_identifier)) rc = 1; } } log__printf(NULL, MOSQ_LOG_SUBSCRIBE, "%s %d %s", context->id, qos, sub); } mosquitto__free(sub); tmp_payload = mosquitto__realloc(payload, payloadlen + 1); if(tmp_payload){ payload = tmp_payload; payload[payloadlen] = qos; payloadlen++; }else{ mosquitto__free(payload); return MOSQ_ERR_NOMEM; } } } if(context->protocol != mosq_p_mqtt31){ if(payloadlen == 0){ /* No subscriptions specified, protocol error. */ return MOSQ_ERR_MALFORMED_PACKET; } } if(send__suback(context, mid, payloadlen, payload)) rc = 1; mosquitto__free(payload); #ifdef WITH_PERSISTENCE db.persistence_changes++; #endif if(context->current_out_packet == NULL){ rc = db__message_write_queued_out(context); if(rc) return rc; rc = db__message_write_inflight_out_latest(context); if(rc) return rc; } return rc; } mosquitto-2.0.18/src/handle_unsubscribe.c000066400000000000000000000107251450213760600204510ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "send_mosq.h" int handle__unsubscribe(struct mosquitto *context) { uint16_t mid; char *sub; uint16_t slen; int rc; uint8_t reason = 0; int reason_code_count = 0; int reason_code_max; uint8_t *reason_codes = NULL, *reason_tmp; mosquitto_property *properties = NULL; bool allowed; if(!context) return MOSQ_ERR_INVAL; if(context->state != mosq_cs_active){ return MOSQ_ERR_PROTOCOL; } if(context->in_packet.command != (CMD_UNSUBSCRIBE|2)){ return MOSQ_ERR_MALFORMED_PACKET; } log__printf(NULL, MOSQ_LOG_DEBUG, "Received UNSUBSCRIBE from %s", context->id); if(context->protocol != mosq_p_mqtt31){ if((context->in_packet.command&0x0F) != 0x02){ return MOSQ_ERR_MALFORMED_PACKET; } } if(packet__read_uint16(&context->in_packet, &mid)) return MOSQ_ERR_MALFORMED_PACKET; if(mid == 0) return MOSQ_ERR_MALFORMED_PACKET; if(context->protocol == mosq_p_mqtt5){ rc = property__read_all(CMD_UNSUBSCRIBE, &context->in_packet, &properties); if(rc){ /* FIXME - it would be better if property__read_all() returned * MOSQ_ERR_MALFORMED_PACKET, but this is would change the library * return codes so needs doc changes as well. */ if(rc == MOSQ_ERR_PROTOCOL){ return MOSQ_ERR_MALFORMED_PACKET; }else{ return rc; } } /* Immediately free, we don't do anything with User Property at the moment */ mosquitto_property_free_all(&properties); } if(context->protocol == mosq_p_mqtt311 || context->protocol == mosq_p_mqtt5){ if(context->in_packet.pos == context->in_packet.remaining_length){ /* No topic specified, protocol error. */ return MOSQ_ERR_MALFORMED_PACKET; } } reason_code_max = 10; reason_codes = mosquitto__malloc((size_t)reason_code_max); if(!reason_codes){ return MOSQ_ERR_NOMEM; } while(context->in_packet.pos < context->in_packet.remaining_length){ sub = NULL; if(packet__read_string(&context->in_packet, &sub, &slen)){ mosquitto__free(reason_codes); return MOSQ_ERR_MALFORMED_PACKET; } if(!slen){ log__printf(NULL, MOSQ_LOG_INFO, "Empty unsubscription string from %s, disconnecting.", context->id); mosquitto__free(sub); mosquitto__free(reason_codes); return MOSQ_ERR_MALFORMED_PACKET; } if(mosquitto_sub_topic_check(sub)){ log__printf(NULL, MOSQ_LOG_INFO, "Invalid unsubscription string from %s, disconnecting.", context->id); mosquitto__free(sub); mosquitto__free(reason_codes); return MOSQ_ERR_MALFORMED_PACKET; } /* ACL check */ allowed = true; rc = mosquitto_acl_check(context, sub, 0, NULL, 0, false, MOSQ_ACL_UNSUBSCRIBE); switch(rc){ case MOSQ_ERR_SUCCESS: break; case MOSQ_ERR_ACL_DENIED: allowed = false; reason = MQTT_RC_NOT_AUTHORIZED; break; default: mosquitto__free(sub); mosquitto__free(reason_codes); return rc; } log__printf(NULL, MOSQ_LOG_DEBUG, "\t%s", sub); if(allowed){ rc = sub__remove(context, sub, db.subs, &reason); }else{ rc = MOSQ_ERR_SUCCESS; } log__printf(NULL, MOSQ_LOG_UNSUBSCRIBE, "%s %s", context->id, sub); mosquitto__free(sub); if(rc){ mosquitto__free(reason_codes); return rc; } reason_codes[reason_code_count] = reason; reason_code_count++; if(reason_code_count == reason_code_max){ reason_tmp = mosquitto__realloc(reason_codes, (size_t)(reason_code_max*2)); if(!reason_tmp){ mosquitto__free(reason_codes); return MOSQ_ERR_NOMEM; } reason_codes = reason_tmp; reason_code_max *= 2; } } #ifdef WITH_PERSISTENCE db.persistence_changes++; #endif log__printf(NULL, MOSQ_LOG_DEBUG, "Sending UNSUBACK to %s", context->id); /* We don't use Reason String or User Property yet. */ rc = send__unsuback(context, mid, reason_code_count, reason_codes, NULL); mosquitto__free(reason_codes); return rc; } mosquitto-2.0.18/src/keepalive.c000066400000000000000000000033741450213760600165610ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include "mosquitto_broker_internal.h" static time_t last_keepalive_check = 0; /* FIXME - this is the prototype for the future tree/trie based keepalive check implementation. */ int keepalive__add(struct mosquitto *context) { UNUSED(context); return MOSQ_ERR_SUCCESS; } void keepalive__check(void) { struct mosquitto *context, *ctxt_tmp; if(last_keepalive_check + 5 < db.now_s){ last_keepalive_check = db.now_s; /* FIXME - this needs replacing with something more efficient */ HASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){ if(context->sock != INVALID_SOCKET){ /* Local bridges never time out in this fashion. */ if(!(context->keepalive) || context->bridge || db.now_s - context->last_msg_in <= (time_t)(context->keepalive)*3/2){ }else{ /* Client has exceeded keepalive*1.5 */ do_disconnect(context, MOSQ_ERR_KEEPALIVE); } } } } } int keepalive__remove(struct mosquitto *context) { UNUSED(context); return MOSQ_ERR_SUCCESS; } void keepalive__remove_all(void) { } int keepalive__update(struct mosquitto *context) { context->last_msg_in = db.now_s; return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/src/lib_load.h000066400000000000000000000020701450213760600163560ustar00rootroot00000000000000/* Copyright (c) 2012-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef LIB_LOAD_H #define LIB_LOAD_H #ifdef WIN32 # include #else # include #endif #ifdef WIN32 # define LIB_LOAD(A) LoadLibrary(A) # define LIB_CLOSE(A) FreeLibrary(A) # define LIB_SYM(HANDLE, SYM) GetProcAddress(HANDLE, SYM) #else # define LIB_LOAD(A) dlopen(A, RTLD_NOW|RTLD_GLOBAL) # define LIB_CLOSE(A) dlclose(A) # define LIB_SYM(HANDLE, SYM) dlsym(HANDLE, SYM) #endif #define LIB_SYM_EASY(MEMBER, HANDLE, SYM) if(!(MEMBER = LIB_SYM(HANDLE, SYM)) return 1 #endif mosquitto-2.0.18/src/linker-macosx.syms000066400000000000000000000016431450213760600201360ustar00rootroot00000000000000_mosquitto_broker_publish _mosquitto_broker_publish_copy _mosquitto_callback_register _mosquitto_callback_unregister _mosquitto_calloc _mosquitto_client_address _mosquitto_client_certificate _mosquitto_client_clean_session _mosquitto_client_id _mosquitto_client_keepalive _mosquitto_client_protocol _mosquitto_client_protocol_version _mosquitto_client_sub_count _mosquitto_client_username _mosquitto_free _mosquitto_kick_client_by_clientid _mosquitto_kick_client_by_username _mosquitto_log_printf _mosquitto_malloc _mosquitto_property_add_binary _mosquitto_property_add_byte _mosquitto_property_add_int16 _mosquitto_property_add_int32 _mosquitto_property_add_string _mosquitto_property_add_string_pair _mosquitto_property_add_varint _mosquitto_property_free_all _mosquitto_pub_topic_check _mosquitto_realloc _mosquitto_set_username _mosquitto_strdup _mosquitto_sub_topic_check _mosquitto_topic_matches_sub _mosquitto_validate_utf8 mosquitto-2.0.18/src/linker.syms000066400000000000000000000017121450213760600166430ustar00rootroot00000000000000{ mosquitto_broker_publish; mosquitto_broker_publish_copy; mosquitto_callback_register; mosquitto_callback_unregister; mosquitto_calloc; mosquitto_client_address; mosquitto_client_certificate; mosquitto_client_clean_session; mosquitto_client_id; mosquitto_client_keepalive; mosquitto_client_protocol; mosquitto_client_protocol_version; mosquitto_client_sub_count; mosquitto_client_username; mosquitto_free; mosquitto_kick_client_by_clientid; mosquitto_kick_client_by_username; mosquitto_log_printf; mosquitto_malloc; mosquitto_property_add_binary; mosquitto_property_add_byte; mosquitto_property_add_int16; mosquitto_property_add_int32; mosquitto_property_add_string; mosquitto_property_add_string_pair; mosquitto_property_add_varint; mosquitto_property_free_all; mosquitto_pub_topic_check; mosquitto_realloc; mosquitto_set_username; mosquitto_strdup; mosquitto_sub_topic_check; mosquitto_topic_matches_sub; mosquitto_validate_utf8; }; mosquitto-2.0.18/src/logging.c000066400000000000000000000217031450213760600162360ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include #ifndef WIN32 #include #endif #include #if defined(__APPLE__) # include #endif #ifdef WITH_DLT #include #include #endif #include "logging_mosq.h" #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "misc_mosq.h" #include "util_mosq.h" #ifdef WIN32 HANDLE syslog_h; #endif static char log_fptr_buffer[BUFSIZ]; /* Options for logging should be: * * A combination of: * Via syslog * To a file * To stdout/stderr * To topics */ /* Give option of logging timestamp. * Logging pid. */ static unsigned int log_destinations = MQTT3_LOG_STDERR; static unsigned int log_priorities = MOSQ_LOG_ERR | MOSQ_LOG_WARNING | MOSQ_LOG_NOTICE | MOSQ_LOG_INFO; #ifdef WITH_DLT static DltContext dltContext; static bool dlt_allowed = false; void dlt_fifo_check(void) { struct stat statbuf; int fd; /* If we start DLT but the /tmp/dlt fifo doesn't exist, or isn't available * for writing then there is a big delay when we try and close the log * later, so check for it first. This has the side effect of not letting * people using DLT create the fifo after Mosquitto has started, but at the * benefit of not having a massive delay for everybody else. */ memset(&statbuf, 0, sizeof(statbuf)); if(stat("/tmp/dlt", &statbuf) == 0){ if(S_ISFIFO(statbuf.st_mode)){ fd = open("/tmp/dlt", O_NONBLOCK | O_WRONLY); if(fd != -1){ dlt_allowed = true; close(fd); } } } } #endif static int get_time(struct tm **ti) { time_t s; s = db.now_real_s; *ti = localtime(&s); if(!(*ti)){ fprintf(stderr, "Error obtaining system time.\n"); return 1; } return 0; } int log__init(struct mosquitto__config *config) { int rc = 0; log_priorities = config->log_type; log_destinations = config->log_dest; if(log_destinations & MQTT3_LOG_SYSLOG){ #ifndef WIN32 openlog("mosquitto", LOG_PID|LOG_CONS, config->log_facility); #else syslog_h = OpenEventLog(NULL, "mosquitto"); #endif } if(log_destinations & MQTT3_LOG_FILE){ config->log_fptr = mosquitto__fopen(config->log_file, "at", true); if(config->log_fptr){ setvbuf(config->log_fptr, log_fptr_buffer, _IOLBF, sizeof(log_fptr_buffer)); }else{ log_destinations = MQTT3_LOG_STDERR; log_priorities = MOSQ_LOG_ERR; log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open log file %s for writing.", config->log_file); } } if(log_destinations & MQTT3_LOG_STDOUT){ setvbuf(stdout, NULL, _IOLBF, 0); } #ifdef WITH_DLT if(log_destinations & MQTT3_LOG_DLT){ dlt_fifo_check(); if(dlt_allowed){ DLT_REGISTER_APP("MQTT","mosquitto log"); dlt_register_context(&dltContext, "MQTT", "mosquitto DLT context"); } } #endif return rc; } int log__close(struct mosquitto__config *config) { if(log_destinations & MQTT3_LOG_SYSLOG){ #ifndef WIN32 closelog(); #else CloseEventLog(syslog_h); #endif } if(log_destinations & MQTT3_LOG_FILE){ if(config->log_fptr){ fclose(config->log_fptr); config->log_fptr = NULL; } } #ifdef WITH_DLT if(dlt_allowed){ dlt_unregister_context(&dltContext); DLT_UNREGISTER_APP(); } #endif /* FIXME - do something for all destinations! */ return MOSQ_ERR_SUCCESS; } #ifdef WITH_DLT DltLogLevelType get_dlt_level(unsigned int priority) { switch (priority) { case MOSQ_LOG_ERR: return DLT_LOG_ERROR; case MOSQ_LOG_WARNING: return DLT_LOG_WARN; case MOSQ_LOG_INFO: return DLT_LOG_INFO; case MOSQ_LOG_DEBUG: return DLT_LOG_DEBUG; case MOSQ_LOG_NOTICE: case MOSQ_LOG_SUBSCRIBE: case MOSQ_LOG_UNSUBSCRIBE: return DLT_LOG_VERBOSE; default: return DLT_LOG_DEFAULT; } } #endif static int log__vprintf(unsigned int priority, const char *fmt, va_list va) { const char *topic; int syslog_priority; char log_line[1000]; size_t log_line_pos; #ifdef WIN32 char *sp; #endif bool log_timestamp = true; char *log_timestamp_format = NULL; FILE *log_fptr = NULL; if(db.config){ log_timestamp = db.config->log_timestamp; log_timestamp_format = db.config->log_timestamp_format; log_fptr = db.config->log_fptr; } if((log_priorities & priority) && log_destinations != MQTT3_LOG_NONE){ switch(priority){ case MOSQ_LOG_SUBSCRIBE: topic = "$SYS/broker/log/M/subscribe"; #ifndef WIN32 syslog_priority = LOG_NOTICE; #else syslog_priority = EVENTLOG_INFORMATION_TYPE; #endif break; case MOSQ_LOG_UNSUBSCRIBE: topic = "$SYS/broker/log/M/unsubscribe"; #ifndef WIN32 syslog_priority = LOG_NOTICE; #else syslog_priority = EVENTLOG_INFORMATION_TYPE; #endif break; case MOSQ_LOG_DEBUG: topic = "$SYS/broker/log/D"; #ifndef WIN32 syslog_priority = LOG_DEBUG; #else syslog_priority = EVENTLOG_INFORMATION_TYPE; #endif break; case MOSQ_LOG_ERR: topic = "$SYS/broker/log/E"; #ifndef WIN32 syslog_priority = LOG_ERR; #else syslog_priority = EVENTLOG_ERROR_TYPE; #endif break; case MOSQ_LOG_WARNING: topic = "$SYS/broker/log/W"; #ifndef WIN32 syslog_priority = LOG_WARNING; #else syslog_priority = EVENTLOG_WARNING_TYPE; #endif break; case MOSQ_LOG_NOTICE: topic = "$SYS/broker/log/N"; #ifndef WIN32 syslog_priority = LOG_NOTICE; #else syslog_priority = EVENTLOG_INFORMATION_TYPE; #endif break; case MOSQ_LOG_INFO: topic = "$SYS/broker/log/I"; #ifndef WIN32 syslog_priority = LOG_INFO; #else syslog_priority = EVENTLOG_INFORMATION_TYPE; #endif break; #ifdef WITH_WEBSOCKETS case MOSQ_LOG_WEBSOCKETS: topic = "$SYS/broker/log/WS"; #ifndef WIN32 syslog_priority = LOG_DEBUG; #else syslog_priority = EVENTLOG_INFORMATION_TYPE; #endif break; #endif default: topic = "$SYS/broker/log/E"; #ifndef WIN32 syslog_priority = LOG_ERR; #else syslog_priority = EVENTLOG_ERROR_TYPE; #endif } if(log_timestamp){ if(log_timestamp_format){ struct tm *ti = NULL; get_time(&ti); log_line_pos = strftime(log_line, sizeof(log_line), log_timestamp_format, ti); if(log_line_pos == 0){ log_line_pos = (size_t)snprintf(log_line, sizeof(log_line), "Time error"); } }else{ log_line_pos = (size_t)snprintf(log_line, sizeof(log_line), "%" PRIu64, (uint64_t)db.now_real_s); } if(log_line_pos < sizeof(log_line)-3){ log_line[log_line_pos] = ':'; log_line[log_line_pos+1] = ' '; log_line[log_line_pos+2] = '\0'; log_line_pos += 2; } }else{ log_line_pos = 0; } vsnprintf(&log_line[log_line_pos], sizeof(log_line)-log_line_pos, fmt, va); log_line[sizeof(log_line)-1] = '\0'; /* Ensure string is null terminated. */ if(log_destinations & MQTT3_LOG_STDOUT){ fprintf(stdout, "%s\n", log_line); } if(log_destinations & MQTT3_LOG_STDERR){ fprintf(stderr, "%s\n", log_line); } if(log_destinations & MQTT3_LOG_FILE && log_fptr){ fprintf(log_fptr, "%s\n", log_line); #ifdef WIN32 /* Windows doesn't support line buffering, so flush. */ fflush(log_fptr); #endif } if(log_destinations & MQTT3_LOG_SYSLOG){ #ifndef WIN32 syslog(syslog_priority, "%s", log_line); #else sp = (char *)log_line; ReportEvent(syslog_h, syslog_priority, 0, 0, NULL, 1, 0, &sp, NULL); #endif } if(log_destinations & MQTT3_LOG_TOPIC && priority != MOSQ_LOG_DEBUG && priority != MOSQ_LOG_INTERNAL){ db__messages_easy_queue(NULL, topic, 2, (uint32_t)strlen(log_line), log_line, 0, 20, NULL); } #ifdef WITH_DLT if(log_destinations & MQTT3_LOG_DLT && priority != MOSQ_LOG_INTERNAL){ DLT_LOG_STRING(dltContext, get_dlt_level(priority), log_line); } #endif } return MOSQ_ERR_SUCCESS; } int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { va_list va; int rc; UNUSED(mosq); va_start(va, fmt); rc = log__vprintf(priority, fmt, va); va_end(va); return rc; } void log__internal(const char *fmt, ...) { va_list va; char buf[200]; int len; va_start(va, fmt); len = vsnprintf(buf, 200, fmt, va); va_end(va); if(len >= 200){ log__printf(NULL, MOSQ_LOG_INTERNAL, "Internal log buffer too short (%d)", len); return; } #ifdef WIN32 log__printf(NULL, MOSQ_LOG_INTERNAL, "%s", buf); #else log__printf(NULL, MOSQ_LOG_INTERNAL, "%s%s%s", "\e[32m", buf, "\e[0m"); #endif } int mosquitto_log_vprintf(int level, const char *fmt, va_list va) { return log__vprintf((unsigned int)level, fmt, va); } void mosquitto_log_printf(int level, const char *fmt, ...) { va_list va; va_start(va, fmt); log__vprintf((unsigned int)level, fmt, va); va_end(va); } mosquitto-2.0.18/src/loop.c000066400000000000000000000237351450213760600155700ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. Tatsuzo Osawa - Add epoll. */ #include "config.h" #ifndef WIN32 # define _GNU_SOURCE #endif #include #ifndef WIN32 #include #else #include #include #include #endif #include #include #include #include #ifndef WIN32 # include #endif #include #include #ifdef WITH_WEBSOCKETS # include #endif #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "packet_mosq.h" #include "send_mosq.h" #include "sys_tree.h" #include "time_mosq.h" #include "util_mosq.h" extern bool flag_reload; #ifdef WITH_PERSISTENCE extern bool flag_db_backup; #endif extern bool flag_tree_print; extern int run; #if defined(WITH_WEBSOCKETS) && LWS_LIBRARY_VERSION_NUMBER == 3002000 void lws__sul_callback(struct lws_sorted_usec_list *l) { } static struct lws_sorted_usec_list sul; #endif static int single_publish(struct mosquitto *context, struct mosquitto_message_v5 *msg, uint32_t message_expiry) { struct mosquitto_msg_store *stored; uint16_t mid; stored = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store)); if(stored == NULL) return MOSQ_ERR_NOMEM; stored->topic = msg->topic; msg->topic = NULL; stored->retain = 0; stored->payloadlen = (uint32_t)msg->payloadlen; stored->payload = mosquitto__malloc(stored->payloadlen+1); if(stored->payload == NULL){ db__msg_store_free(stored); return MOSQ_ERR_NOMEM; } /* Ensure payload is always zero terminated, this is the reason for the extra byte above */ ((uint8_t *)stored->payload)[stored->payloadlen] = 0; memcpy(stored->payload, msg->payload, stored->payloadlen); if(msg->properties){ stored->properties = msg->properties; msg->properties = NULL; } if(db__message_store(context, stored, message_expiry, 0, mosq_mo_broker)) return 1; if(msg->qos){ mid = mosquitto__mid_generate(context); }else{ mid = 0; } return db__message_insert(context, mid, mosq_md_out, (uint8_t)msg->qos, 0, stored, msg->properties, true); } static void read_message_expiry_interval(mosquitto_property **proplist, uint32_t *message_expiry) { mosquitto_property *p, *previous = NULL; *message_expiry = 0; if(!proplist) return; p = *proplist; while(p){ if(p->identifier == MQTT_PROP_MESSAGE_EXPIRY_INTERVAL){ *message_expiry = p->value.i32; if(p == *proplist){ *proplist = p->next; }else{ previous->next = p->next; } property__free(&p); return; } previous = p; p = p->next; } } static void queue_plugin_msgs(void) { struct mosquitto_message_v5 *msg, *tmp; struct mosquitto *context; uint32_t message_expiry; DL_FOREACH_SAFE(db.plugin_msgs, msg, tmp){ DL_DELETE(db.plugin_msgs, msg); read_message_expiry_interval(&msg->properties, &message_expiry); if(msg->clientid){ HASH_FIND(hh_id, db.contexts_by_id, msg->clientid, strlen(msg->clientid), context); if(context){ single_publish(context, msg, message_expiry); } }else{ db__messages_easy_queue(NULL, msg->topic, (uint8_t)msg->qos, (uint32_t)msg->payloadlen, msg->payload, msg->retain, message_expiry, &msg->properties); } mosquitto__free(msg->topic); mosquitto__free(msg->payload); mosquitto_property_free_all(&msg->properties); mosquitto__free(msg->clientid); mosquitto__free(msg); } } int mosquitto_main_loop(struct mosquitto__listener_sock *listensock, int listensock_count) { #ifdef WITH_SYS_TREE time_t start_time = mosquitto_time(); #endif #ifdef WITH_PERSISTENCE time_t last_backup = mosquitto_time(); #endif #ifdef WITH_WEBSOCKETS int i; #endif int rc; #if defined(WITH_WEBSOCKETS) && LWS_LIBRARY_VERSION_NUMBER == 3002000 memset(&sul, 0, sizeof(struct lws_sorted_usec_list)); #endif db.now_s = mosquitto_time(); db.now_real_s = time(NULL); #ifdef WITH_BRIDGE rc = bridge__register_local_connections(); if(rc) return rc; #endif while(run){ queue_plugin_msgs(); context__free_disused(); #ifdef WITH_SYS_TREE if(db.config->sys_interval > 0){ sys_tree__update(db.config->sys_interval, start_time); } #endif keepalive__check(); #ifdef WITH_BRIDGE bridge_check(); #endif rc = mux__handle(listensock, listensock_count); if(rc) return rc; session_expiry__check(); will_delay__check(); #ifdef WITH_PERSISTENCE if(db.config->persistence && db.config->autosave_interval){ if(db.config->autosave_on_changes){ if(db.persistence_changes >= db.config->autosave_interval){ persist__backup(false); db.persistence_changes = 0; } }else{ if(last_backup + db.config->autosave_interval < db.now_s){ persist__backup(false); last_backup = db.now_s; } } } #endif #ifdef WITH_PERSISTENCE if(flag_db_backup){ persist__backup(false); flag_db_backup = false; } #endif if(flag_reload){ log__printf(NULL, MOSQ_LOG_INFO, "Reloading config."); config__read(db.config, true); listeners__reload_all_certificates(); mosquitto_security_cleanup(true); mosquitto_security_init(true); mosquitto_security_apply(); log__close(db.config); log__init(db.config); flag_reload = false; } if(flag_tree_print){ sub__tree_print(db.subs, 0); flag_tree_print = false; #ifdef WITH_XTREPORT xtreport(); #endif } #ifdef WITH_WEBSOCKETS for(i=0; ilistener_count; i++){ /* Extremely hacky, should be using the lws provided external poll * interface, but their interface has changed recently and ours * will soon, so for now websockets clients are second class * citizens. */ if(db.config->listeners[i].ws_context){ #if LWS_LIBRARY_VERSION_NUMBER > 3002000 lws_service(db.config->listeners[i].ws_context, -1); #elif LWS_LIBRARY_VERSION_NUMBER == 3002000 lws_sul_schedule(db.config->listeners[i].ws_context, 0, &sul, lws__sul_callback, 10); lws_service(db.config->listeners[i].ws_context, 0); #else lws_service(db.config->listeners[i].ws_context, 0); #endif } } #endif plugin__handle_tick(); } mux__cleanup(); return MOSQ_ERR_SUCCESS; } void do_disconnect(struct mosquitto *context, int reason) { const char *id; #ifdef WITH_WEBSOCKETS bool is_duplicate = false; #endif if(context->state == mosq_cs_disconnected){ return; } #ifdef WITH_WEBSOCKETS if(context->wsi){ if(context->state == mosq_cs_duplicate){ is_duplicate = true; } if(context->state != mosq_cs_disconnecting && context->state != mosq_cs_disconnect_with_will){ mosquitto__set_state(context, mosq_cs_disconnect_ws); } if(context->wsi){ lws_callback_on_writable(context->wsi); } if(context->sock != INVALID_SOCKET){ HASH_DELETE(hh_sock, db.contexts_by_sock, context); mux__delete(context); context->sock = INVALID_SOCKET; } if(is_duplicate){ /* This occurs if another client is taking over the same client id. * It is important to remove this from the by_id hash here, so it * doesn't leave us with multiple clients in the hash with the same * id. Websockets doesn't actually close the connection here, * unlike for normal clients, which means there is extra time when * there could be two clients with the same id in the hash. */ context__remove_from_by_id(context); } }else #endif { if(db.config->connection_messages == true){ if(context->id){ id = context->id; }else{ id = ""; } if(context->state != mosq_cs_disconnecting && context->state != mosq_cs_disconnect_with_will){ switch(reason){ case MOSQ_ERR_SUCCESS: break; case MOSQ_ERR_MALFORMED_PACKET: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected due to malformed packet.", id); break; case MOSQ_ERR_PROTOCOL: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected due to protocol error.", id); break; case MOSQ_ERR_CONN_LOST: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s closed its connection.", id); break; case MOSQ_ERR_AUTH: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected, not authorised.", id); break; case MOSQ_ERR_KEEPALIVE: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s has exceeded timeout, disconnecting.", id); break; case MOSQ_ERR_OVERSIZE_PACKET: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected due to oversize packet.", id); break; case MOSQ_ERR_PAYLOAD_SIZE: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected due to oversize payload.", id); break; case MOSQ_ERR_NOMEM: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected due to out of memory.", id); break; case MOSQ_ERR_NOT_SUPPORTED: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected due to using not allowed feature (QoS too high, retain not supported, or bad AUTH method).", id); break; case MOSQ_ERR_ADMINISTRATIVE_ACTION: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s been disconnected by administrative action.", id); break; case MOSQ_ERR_ERRNO: log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected: %s.", id, strerror(errno)); break; default: log__printf(NULL, MOSQ_LOG_NOTICE, "Bad socket read/write on client %s: %s", id, mosquitto_strerror(reason)); break; } }else{ if(reason == MOSQ_ERR_ADMINISTRATIVE_ACTION){ log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s been disconnected by administrative action.", id); }else{ log__printf(NULL, MOSQ_LOG_NOTICE, "Client %s disconnected.", id); } } } mux__delete(context); context__disconnect(context); } } mosquitto-2.0.18/src/memory_public.c000066400000000000000000000020041450213760600174470ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "mosquitto_broker.h" #include "memory_mosq.h" void *mosquitto_calloc(size_t nmemb, size_t size) { return mosquitto__calloc(nmemb, size); } void mosquitto_free(void *mem) { mosquitto__free(mem); } void *mosquitto_malloc(size_t size) { return mosquitto__malloc(size); } void *mosquitto_realloc(void *ptr, size_t size) { return mosquitto__realloc(ptr, size); } char *mosquitto_strdup(const char *s) { return mosquitto__strdup(s); } mosquitto-2.0.18/src/mosquitto.c000066400000000000000000000402721450213760600166560ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifndef WIN32 /* For initgroups() */ # include # include # include #endif #ifndef WIN32 #include #else #include #include #include #endif #ifndef WIN32 # include #endif #include #include #include #include #ifdef WITH_SYSTEMD # include #endif #ifdef WITH_WRAP #include #endif #ifdef WITH_WEBSOCKETS # include #endif #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "misc_mosq.h" #include "util_mosq.h" struct mosquitto_db db; static struct mosquitto__listener_sock *listensock = NULL; static int listensock_count = 0; static int listensock_index = 0; bool flag_reload = false; #ifdef WITH_PERSISTENCE bool flag_db_backup = false; #endif bool flag_tree_print = false; int run; #ifdef WITH_WRAP #include int allow_severity = LOG_INFO; int deny_severity = LOG_INFO; #endif /* mosquitto shouldn't run as root. * This function will attempt to change to an unprivileged user and group if * running as root. The user is given in config->user. * Returns 1 on failure (unknown user, setuid/setgid failure) * Returns 0 on success. * Note that setting config->user to "root" does not produce an error, but it * strongly discouraged. */ int drop_privileges(struct mosquitto__config *config) { #if !defined(__CYGWIN__) && !defined(WIN32) struct passwd *pwd; char *err; int rc; const char *snap = getenv("SNAP_NAME"); if(snap && !strcmp(snap, "mosquitto")){ /* Don't attempt to drop privileges if running as a snap */ return MOSQ_ERR_SUCCESS; } if(geteuid() == 0){ if(config->user && strcmp(config->user, "root")){ pwd = getpwnam(config->user); if(!pwd){ if(strcmp(config->user, "mosquitto")){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to drop privileges to '%s' because this user does not exist.", config->user); return 1; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Warning: Unable to drop privileges to '%s' because this user does not exist. Trying 'nobody' instead.", config->user); pwd = getpwnam("nobody"); if(!pwd){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to drop privileges to 'nobody'."); return 1; } } } if(initgroups(config->user, pwd->pw_gid) == -1){ err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting groups whilst dropping privileges: %s.", err); return 1; } rc = setgid(pwd->pw_gid); if(rc == -1){ err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting gid whilst dropping privileges: %s.", err); return 1; } rc = setuid(pwd->pw_uid); if(rc == -1){ err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error setting uid whilst dropping privileges: %s.", err); return 1; } } if(geteuid() == 0 || getegid() == 0){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Mosquitto should not be run as root/administrator."); } } #else UNUSED(config); #endif return MOSQ_ERR_SUCCESS; } static void mosquitto__daemonise(void) { #ifndef WIN32 char *err; pid_t pid; pid = fork(); if(pid < 0){ err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error in fork: %s", err); exit(1); } if(pid > 0){ exit(0); } if(setsid() < 0){ err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error in setsid: %s", err); exit(1); } if(!freopen("/dev/null", "r", stdin)){ log__printf(NULL, MOSQ_LOG_ERR, "Error whilst daemonising (%s): %s", "stdin", strerror(errno)); exit(1); } if(!freopen("/dev/null", "w", stdout)){ log__printf(NULL, MOSQ_LOG_ERR, "Error whilst daemonising (%s): %s", "stdout", strerror(errno)); exit(1); } if(!freopen("/dev/null", "w", stderr)){ log__printf(NULL, MOSQ_LOG_ERR, "Error whilst daemonising (%s): %s", "stderr", strerror(errno)); exit(1); } #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Can't start in daemon mode in Windows."); #endif } void listener__set_defaults(struct mosquitto__listener *listener) { listener->security_options.allow_anonymous = -1; listener->security_options.allow_zero_length_clientid = true; listener->protocol = mp_mqtt; listener->max_connections = -1; listener->max_qos = 2; listener->max_topic_alias = 10; } void listeners__reload_all_certificates(void) { #ifdef WITH_TLS int i; int rc; struct mosquitto__listener *listener; for(i=0; ilistener_count; i++){ listener = &db.config->listeners[i]; if(listener->ssl_ctx && listener->certfile && listener->keyfile){ rc = net__load_certificates(listener); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error when reloading certificate '%s' or key '%s'.", listener->certfile, listener->keyfile); } } } #endif } static int listeners__start_single_mqtt(struct mosquitto__listener *listener) { int i; struct mosquitto__listener_sock *listensock_new; if(net__socket_listen(listener)){ return 1; } listensock_count += listener->sock_count; listensock_new = mosquitto__realloc(listensock, sizeof(struct mosquitto__listener_sock)*(size_t)listensock_count); if(!listensock_new){ return 1; } listensock = listensock_new; for(i=0; isock_count; i++){ if(listener->socks[i] == INVALID_SOCKET){ return 1; } listensock[listensock_index].sock = listener->socks[i]; listensock[listensock_index].listener = listener; #ifdef WITH_EPOLL listensock[listensock_index].ident = id_listener; #endif listensock_index++; } return MOSQ_ERR_SUCCESS; } #ifdef WITH_WEBSOCKETS void listeners__add_websockets(struct lws_context *ws_context, mosq_sock_t fd) { int i; struct mosquitto__listener *listener = NULL; struct mosquitto__listener_sock *listensock_new; /* Don't add more listeners after we've started the main loop */ if(run || ws_context == NULL) return; /* Find context */ for(i=0; ilistener_count; i++){ if(db.config->listeners[i].ws_in_init){ listener = &db.config->listeners[i]; break; } } if(listener == NULL){ return; } listensock_count++; listensock_new = mosquitto__realloc(listensock, sizeof(struct mosquitto__listener_sock)*(size_t)listensock_count); if(!listensock_new){ return; } listensock = listensock_new; listensock[listensock_index].sock = fd; listensock[listensock_index].listener = listener; #ifdef WITH_EPOLL listensock[listensock_index].ident = id_listener_ws; #endif listensock_index++; } #endif static int listeners__add_local(const char *host, uint16_t port) { struct mosquitto__listener *listeners; listeners = db.config->listeners; listener__set_defaults(&listeners[db.config->listener_count]); listeners[db.config->listener_count].security_options.allow_anonymous = true; listeners[db.config->listener_count].port = port; listeners[db.config->listener_count].host = mosquitto__strdup(host); if(listeners[db.config->listener_count].host == NULL){ return MOSQ_ERR_NOMEM; } if(listeners__start_single_mqtt(&listeners[db.config->listener_count])){ mosquitto__free(listeners[db.config->listener_count].host); listeners[db.config->listener_count].host = NULL; return MOSQ_ERR_UNKNOWN; } db.config->listener_count++; return MOSQ_ERR_SUCCESS; } static int listeners__start_local_only(void) { /* Attempt to open listeners bound to 127.0.0.1 and ::1 only */ int i; int rc; struct mosquitto__listener *listeners; listeners = mosquitto__realloc(db.config->listeners, 2*sizeof(struct mosquitto__listener)); if(listeners == NULL){ return MOSQ_ERR_NOMEM; } memset(listeners, 0, 2*sizeof(struct mosquitto__listener)); db.config->listener_count = 0; db.config->listeners = listeners; log__printf(NULL, MOSQ_LOG_WARNING, "Starting in local only mode. Connections will only be possible from clients running on this machine."); log__printf(NULL, MOSQ_LOG_WARNING, "Create a configuration file which defines a listener to allow remote access."); log__printf(NULL, MOSQ_LOG_WARNING, "For more details see https://mosquitto.org/documentation/authentication-methods/"); if(db.config->cmd_port_count == 0){ rc = listeners__add_local("127.0.0.1", 1883); if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM; rc = listeners__add_local("::1", 1883); if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM; }else{ for(i=0; icmd_port_count; i++){ rc = listeners__add_local("127.0.0.1", db.config->cmd_port[i]); if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM; rc = listeners__add_local("::1", db.config->cmd_port[i]); if(rc == MOSQ_ERR_NOMEM) return MOSQ_ERR_NOMEM; } } if(db.config->listener_count > 0){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_UNKNOWN; } } static int listeners__start(void) { int i; listensock_count = 0; if(db.config->local_only){ if(listeners__start_local_only()){ db__close(); if(db.config->pid_file){ (void)remove(db.config->pid_file); } return 1; } return MOSQ_ERR_SUCCESS; } for(i=0; ilistener_count; i++){ if(db.config->listeners[i].protocol == mp_mqtt){ if(listeners__start_single_mqtt(&db.config->listeners[i])){ db__close(); if(db.config->pid_file){ (void)remove(db.config->pid_file); } return 1; } }else if(db.config->listeners[i].protocol == mp_websockets){ #ifdef WITH_WEBSOCKETS mosq_websockets_init(&db.config->listeners[i], db.config); if(!db.config->listeners[i].ws_context){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to create websockets listener on port %d.", db.config->listeners[i].port); return 1; } #endif } } if(listensock == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to start any listening sockets, exiting."); return 1; } return MOSQ_ERR_SUCCESS; } static void listeners__stop(void) { int i; for(i=0; ilistener_count; i++){ #ifdef WITH_WEBSOCKETS if(db.config->listeners[i].ws_context){ lws_context_destroy(db.config->listeners[i].ws_context); } mosquitto__free(db.config->listeners[i].ws_protocol); #endif #ifdef WITH_UNIX_SOCKETS if(db.config->listeners[i].unix_socket_path != NULL){ unlink(db.config->listeners[i].unix_socket_path); } #endif } for(i=0; ipid_file){ pid = mosquitto__fopen(db.config->pid_file, "wt", false); if(pid){ fprintf(pid, "%d", getpid()); fclose(pid); }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to write pid file."); return 1; } } return MOSQ_ERR_SUCCESS; } int main(int argc, char *argv[]) { struct mosquitto__config config; #ifdef WITH_BRIDGE int i; #endif int rc; #ifdef WIN32 SYSTEMTIME st; #else struct timeval tv; #endif struct mosquitto *ctxt, *ctxt_tmp; #if defined(WIN32) || defined(__CYGWIN__) if(argc == 2){ if(!strcmp(argv[1], "run")){ service_run(); return 0; }else if(!strcmp(argv[1], "install")){ service_install(); return 0; }else if(!strcmp(argv[1], "uninstall")){ service_uninstall(); return 0; } } #endif #ifdef WIN32 GetSystemTime(&st); srand(st.wSecond + st.wMilliseconds); #else gettimeofday(&tv, NULL); srand((unsigned int)(tv.tv_sec + tv.tv_usec)); #endif #ifdef WIN32 if(_setmaxstdio(8192) != 8192){ /* Old limit was 2048 */ if(_setmaxstdio(2048) != 2048){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Unable to increase maximum allowed connections. This session may be limited to 512 connections."); } } #endif memset(&db, 0, sizeof(struct mosquitto_db)); db.now_s = mosquitto_time(); db.now_real_s = time(NULL); net__broker_init(); config__init(&config); rc = config__parse_args(&config, argc, argv); if(rc != MOSQ_ERR_SUCCESS) return rc; db.config = &config; /* Drop privileges permanently immediately after the config is loaded. * This requires the user to ensure that all certificates, log locations, * etc. are accessible my the `mosquitto` or other unprivileged user. */ rc = drop_privileges(&config); if(rc != MOSQ_ERR_SUCCESS) return rc; if(config.daemon){ mosquitto__daemonise(); } if(pid__write()) return 1; rc = db__open(&config); if(rc != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Couldn't open database."); return rc; } /* Initialise logging only after initialising the database in case we're * logging to topics */ if(log__init(&config)){ rc = 1; return rc; } log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s starting", VERSION); if(db.config_file){ log__printf(NULL, MOSQ_LOG_INFO, "Config loaded from %s.", db.config_file); }else{ log__printf(NULL, MOSQ_LOG_INFO, "Using default config."); } rc = mosquitto_security_module_init(); if(rc) return rc; rc = mosquitto_security_init(false); if(rc) return rc; /* After loading persisted clients and ACLs, try to associate them, * so persisted subscriptions can start storing messages */ HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){ if(ctxt && !ctxt->clean_start && ctxt->username){ rc = acl__find_acls(ctxt); if(rc){ log__printf(NULL, MOSQ_LOG_WARNING, "Failed to associate persisted user %s with ACLs, " "likely due to changed ports while using a per_listener_settings configuration.", ctxt->username); } } } #ifdef WITH_SYS_TREE sys_tree__init(); #endif if(listeners__start()) return 1; rc = mux__init(listensock, listensock_count); if(rc) return rc; signal__setup(); #ifdef WITH_BRIDGE bridge__start_all(); #endif log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s running", VERSION); #ifdef WITH_SYSTEMD sd_notify(0, "READY=1"); #endif run = 1; rc = mosquitto_main_loop(listensock, listensock_count); log__printf(NULL, MOSQ_LOG_INFO, "mosquitto version %s terminating", VERSION); /* FIXME - this isn't quite right, all wills with will delay zero should be * sent now, but those with positive will delay should be persisted and * restored, pending the client reconnecting in time. */ HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){ context__send_will(ctxt); } will_delay__send_all(); #ifdef WITH_PERSISTENCE persist__backup(true); #endif session_expiry__remove_all(); listeners__stop(); HASH_ITER(hh_id, db.contexts_by_id, ctxt, ctxt_tmp){ #ifdef WITH_WEBSOCKETS if(!ctxt->wsi) #endif { context__cleanup(ctxt, true); } } HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){ context__cleanup(ctxt, true); } #ifdef WITH_BRIDGE for(i=0; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. Tatsuzo Osawa - Add epoll. */ #ifndef MOSQUITTO_BROKER_INTERNAL_H #define MOSQUITTO_BROKER_INTERNAL_H #include "config.h" #include #ifdef WITH_WEBSOCKETS # include # if LWS_LIBRARY_VERSION_NUMBER >= 3002000 && !defined(LWS_WITH_EXTERNAL_POLL) # warning "libwebsockets is not compiled with LWS_WITH_EXTERNAL_POLL support. Websocket performance will be unusable." # endif #endif #include "mosquitto_internal.h" #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mosquitto.h" #include "logging_mosq.h" #include "password_mosq.h" #include "tls_mosq.h" #include "uthash.h" #ifndef __GNUC__ #define __attribute__(attrib) #endif /* Log destinations */ #define MQTT3_LOG_NONE 0x00 #define MQTT3_LOG_SYSLOG 0x01 #define MQTT3_LOG_FILE 0x02 #define MQTT3_LOG_STDOUT 0x04 #define MQTT3_LOG_STDERR 0x08 #define MQTT3_LOG_TOPIC 0x10 #define MQTT3_LOG_DLT 0x20 #define MQTT3_LOG_ALL 0xFF #define WEBSOCKET_CLIENT -2 #define CMD_PORT_LIMIT 10 #define TOPIC_HIERARCHY_LIMIT 200 typedef uint64_t dbid_t; typedef int (*FUNC_plugin_init_v5)(mosquitto_plugin_id_t *, void **, struct mosquitto_opt *, int); typedef int (*FUNC_plugin_cleanup_v5)(void *, struct mosquitto_opt *, int); typedef int (*FUNC_auth_plugin_init_v4)(void **, struct mosquitto_opt *, int); typedef int (*FUNC_auth_plugin_cleanup_v4)(void *, struct mosquitto_opt *, int); typedef int (*FUNC_auth_plugin_security_init_v4)(void *, struct mosquitto_opt *, int, bool); typedef int (*FUNC_auth_plugin_security_cleanup_v4)(void *, struct mosquitto_opt *, int, bool); typedef int (*FUNC_auth_plugin_acl_check_v4)(void *, int, struct mosquitto *, struct mosquitto_acl_msg *); typedef int (*FUNC_auth_plugin_unpwd_check_v4)(void *, struct mosquitto *, const char *, const char *); typedef int (*FUNC_auth_plugin_psk_key_get_v4)(void *, struct mosquitto *, const char *, const char *, char *, int); typedef int (*FUNC_auth_plugin_auth_start_v4)(void *, struct mosquitto *, const char *, bool, const void *, uint16_t, void **, uint16_t *); typedef int (*FUNC_auth_plugin_auth_continue_v4)(void *, struct mosquitto *, const char *, const void *, uint16_t, void **, uint16_t *); typedef int (*FUNC_auth_plugin_init_v3)(void **, struct mosquitto_opt *, int); typedef int (*FUNC_auth_plugin_cleanup_v3)(void *, struct mosquitto_opt *, int); typedef int (*FUNC_auth_plugin_security_init_v3)(void *, struct mosquitto_opt *, int, bool); typedef int (*FUNC_auth_plugin_security_cleanup_v3)(void *, struct mosquitto_opt *, int, bool); typedef int (*FUNC_auth_plugin_acl_check_v3)(void *, int, const struct mosquitto *, struct mosquitto_acl_msg *); typedef int (*FUNC_auth_plugin_unpwd_check_v3)(void *, const struct mosquitto *, const char *, const char *); typedef int (*FUNC_auth_plugin_psk_key_get_v3)(void *, const struct mosquitto *, const char *, const char *, char *, int); typedef int (*FUNC_auth_plugin_init_v2)(void **, struct mosquitto_auth_opt *, int); typedef int (*FUNC_auth_plugin_cleanup_v2)(void *, struct mosquitto_auth_opt *, int); typedef int (*FUNC_auth_plugin_security_init_v2)(void *, struct mosquitto_auth_opt *, int, bool); typedef int (*FUNC_auth_plugin_security_cleanup_v2)(void *, struct mosquitto_auth_opt *, int, bool); typedef int (*FUNC_auth_plugin_acl_check_v2)(void *, const char *, const char *, const char *, int); typedef int (*FUNC_auth_plugin_unpwd_check_v2)(void *, const char *, const char *); typedef int (*FUNC_auth_plugin_psk_key_get_v2)(void *, const char *, const char *, char *, int); enum mosquitto_msg_origin{ mosq_mo_client = 0, mosq_mo_broker = 1 }; struct mosquitto__auth_plugin{ void *lib; void *user_data; int (*plugin_version)(void); struct mosquitto_plugin_id_t *identifier; FUNC_plugin_init_v5 plugin_init_v5; FUNC_plugin_cleanup_v5 plugin_cleanup_v5; FUNC_auth_plugin_init_v4 plugin_init_v4; FUNC_auth_plugin_cleanup_v4 plugin_cleanup_v4; FUNC_auth_plugin_security_init_v4 security_init_v4; FUNC_auth_plugin_security_cleanup_v4 security_cleanup_v4; FUNC_auth_plugin_acl_check_v4 acl_check_v4; FUNC_auth_plugin_unpwd_check_v4 unpwd_check_v4; FUNC_auth_plugin_psk_key_get_v4 psk_key_get_v4; FUNC_auth_plugin_auth_start_v4 auth_start_v4; FUNC_auth_plugin_auth_continue_v4 auth_continue_v4; FUNC_auth_plugin_init_v3 plugin_init_v3; FUNC_auth_plugin_cleanup_v3 plugin_cleanup_v3; FUNC_auth_plugin_security_init_v3 security_init_v3; FUNC_auth_plugin_security_cleanup_v3 security_cleanup_v3; FUNC_auth_plugin_acl_check_v3 acl_check_v3; FUNC_auth_plugin_unpwd_check_v3 unpwd_check_v3; FUNC_auth_plugin_psk_key_get_v3 psk_key_get_v3; FUNC_auth_plugin_init_v2 plugin_init_v2; FUNC_auth_plugin_cleanup_v2 plugin_cleanup_v2; FUNC_auth_plugin_security_init_v2 security_init_v2; FUNC_auth_plugin_security_cleanup_v2 security_cleanup_v2; FUNC_auth_plugin_acl_check_v2 acl_check_v2; FUNC_auth_plugin_unpwd_check_v2 unpwd_check_v2; FUNC_auth_plugin_psk_key_get_v2 psk_key_get_v2; int version; }; struct mosquitto__auth_plugin_config { char *path; struct mosquitto_opt *options; int option_count; bool deny_special_chars; struct mosquitto__auth_plugin plugin; }; struct mosquitto__callback{ UT_hash_handle hh; /* For callbacks that register for e.g. a specific topic */ struct mosquitto__callback *next, *prev; /* For typical callbacks */ MOSQ_FUNC_generic_callback cb; void *userdata; char *data; /* e.g. topic for control event */ }; struct plugin__callbacks{ struct mosquitto__callback *tick; struct mosquitto__callback *acl_check; struct mosquitto__callback *basic_auth; struct mosquitto__callback *control; struct mosquitto__callback *disconnect; struct mosquitto__callback *ext_auth_continue; struct mosquitto__callback *ext_auth_start; struct mosquitto__callback *message; struct mosquitto__callback *psk_key; struct mosquitto__callback *reload; }; struct mosquitto__security_options { /* Any options that get added here also need considering * in config__read() with regards whether allow_anonymous * should be disabled when these options are set. */ struct mosquitto__unpwd *unpwd; struct mosquitto__unpwd *psk_id; struct mosquitto__acl_user *acl_list; struct mosquitto__acl *acl_patterns; char *password_file; char *psk_file; char *acl_file; struct mosquitto__auth_plugin_config *auth_plugin_configs; int auth_plugin_config_count; int8_t allow_anonymous; bool allow_zero_length_clientid; char *auto_id_prefix; uint16_t auto_id_prefix_len; struct plugin__callbacks plugin_callbacks; mosquitto_plugin_id_t *pid; /* For registering as a "plugin" */ }; #ifdef WITH_EPOLL enum struct_ident{ id_invalid = 0, id_listener = 1, id_client = 2, id_listener_ws = 3, }; #endif struct mosquitto__listener { uint16_t port; char *host; char *bind_interface; int max_connections; char *mount_point; mosq_sock_t *socks; int sock_count; int client_count; enum mosquitto_protocol protocol; int socket_domain; bool use_username_as_clientid; uint8_t max_qos; uint16_t max_topic_alias; #ifdef WITH_TLS char *cafile; char *capath; char *certfile; char *keyfile; char *tls_engine; char *tls_engine_kpass_sha1; char *ciphers; char *ciphers_tls13; char *psk_hint; SSL_CTX *ssl_ctx; char *crlfile; char *tls_version; char *dhparamfile; bool use_identity_as_username; bool use_subject_as_username; bool require_certificate; enum mosquitto__keyform tls_keyform; #endif #ifdef WITH_WEBSOCKETS struct lws_context *ws_context; bool ws_in_init; char *http_dir; struct lws_protocols *ws_protocol; #endif struct mosquitto__security_options security_options; #ifdef WITH_UNIX_SOCKETS char *unix_socket_path; #endif }; struct mosquitto__listener_sock{ #ifdef WITH_EPOLL /* This *must* be the first element in the struct. */ int ident; #endif mosq_sock_t sock; struct mosquitto__listener *listener; }; typedef struct mosquitto_plugin_id_t{ struct mosquitto__listener *listener; } mosquitto_plugin_id_t; struct mosquitto__config { bool allow_duplicate_messages; int autosave_interval; bool autosave_on_changes; bool check_retain_source; char *clientid_prefixes; bool connection_messages; uint16_t cmd_port[CMD_PORT_LIMIT]; int cmd_port_count; bool daemon; struct mosquitto__listener default_listener; struct mosquitto__listener *listeners; int listener_count; bool local_only; unsigned int log_dest; int log_facility; unsigned int log_type; bool log_timestamp; char *log_timestamp_format; char *log_file; FILE *log_fptr; size_t max_inflight_bytes; size_t max_queued_bytes; int max_queued_messages; uint32_t max_packet_size; uint32_t message_size_limit; uint16_t max_inflight_messages; uint16_t max_keepalive; uint8_t max_qos; bool persistence; char *persistence_location; char *persistence_file; char *persistence_filepath; time_t persistent_client_expiration; char *pid_file; bool queue_qos0_messages; bool per_listener_settings; bool retain_available; bool set_tcp_nodelay; int sys_interval; bool upgrade_outgoing_qos; char *user; #ifdef WITH_WEBSOCKETS int websockets_log_level; uint16_t websockets_headers_size; #endif #ifdef WITH_BRIDGE struct mosquitto__bridge *bridges; int bridge_count; #endif struct mosquitto__security_options security_options; }; struct mosquitto__subleaf { struct mosquitto__subleaf *prev; struct mosquitto__subleaf *next; struct mosquitto *context; uint32_t identifier; uint8_t qos; bool no_local; bool retain_as_published; }; struct mosquitto__subshared { UT_hash_handle hh; char *name; struct mosquitto__subleaf *subs; }; struct mosquitto__subhier { UT_hash_handle hh; struct mosquitto__subhier *parent; struct mosquitto__subhier *children; struct mosquitto__subleaf *subs; struct mosquitto__subshared *shared; char *topic; uint16_t topic_len; }; struct mosquitto__client_sub { struct mosquitto__subhier *hier; struct mosquitto__subshared *shared; char topic_filter[]; }; struct sub__token { struct sub__token *next; char *topic; uint16_t topic_len; }; struct mosquitto__retainhier { UT_hash_handle hh; struct mosquitto__retainhier *parent; struct mosquitto__retainhier *children; struct mosquitto_msg_store *retained; char *topic; uint16_t topic_len; }; struct mosquitto_msg_store_load{ UT_hash_handle hh; dbid_t db_id; struct mosquitto_msg_store *store; }; struct mosquitto_msg_store{ struct mosquitto_msg_store *next; struct mosquitto_msg_store *prev; dbid_t db_id; char *source_id; char *source_username; struct mosquitto__listener *source_listener; char **dest_ids; int dest_id_count; int ref_count; char* topic; mosquitto_property *properties; void *payload; time_t message_expiry_time; uint32_t payloadlen; enum mosquitto_msg_origin origin; uint16_t source_mid; uint16_t mid; uint8_t qos; bool retain; }; struct mosquitto_client_msg{ struct mosquitto_client_msg *prev; struct mosquitto_client_msg *next; struct mosquitto_msg_store *store; mosquitto_property *properties; time_t timestamp; uint16_t mid; uint8_t qos; bool retain; enum mosquitto_msg_direction direction; enum mosquitto_msg_state state; uint8_t dup; }; struct mosquitto__unpwd{ UT_hash_handle hh; char *username; char *password; char *clientid; #ifdef WITH_TLS unsigned char *salt; unsigned int password_len; unsigned int salt_len; int iterations; #endif enum mosquitto_pwhash_type hashtype; }; struct mosquitto__acl{ struct mosquitto__acl *next; char *topic; int access; int ucount; int ccount; }; struct mosquitto__acl_user{ struct mosquitto__acl_user *next; char *username; struct mosquitto__acl *acl; }; struct mosquitto_message_v5{ struct mosquitto_message_v5 *next, *prev; char *topic; void *payload; mosquitto_property *properties; char *clientid; /* Used only by mosquitto_broker_publish*() to indicate this message is for a specific client. */ int payloadlen; int qos; bool retain; }; struct mosquitto_db{ dbid_t last_db_id; struct mosquitto__subhier *subs; struct mosquitto__retainhier *retains; struct mosquitto *contexts_by_id; struct mosquitto *contexts_by_sock; struct mosquitto *contexts_for_free; #ifdef WITH_BRIDGE struct mosquitto **bridges; #endif struct clientid__index_hash *clientid_index_hash; struct mosquitto_msg_store *msg_store; struct mosquitto_msg_store_load *msg_store_load; time_t now_s; /* Monotonic clock, where possible */ time_t now_real_s; /* Read clock, for measuring session/message expiry */ #ifdef WITH_BRIDGE int bridge_count; #endif int msg_store_count; unsigned long msg_store_bytes; char *config_file; struct mosquitto__config *config; int auth_plugin_count; bool verbose; #ifdef WITH_SYS_TREE int subscription_count; int shared_subscription_count; int retained_count; #endif int persistence_changes; struct mosquitto *ll_for_free; #ifdef WITH_EPOLL int epollfd; #endif struct mosquitto_message_v5 *plugin_msgs; }; enum mosquitto__bridge_direction{ bd_out = 0, bd_in = 1, bd_both = 2 }; enum mosquitto_bridge_start_type{ bst_automatic = 0, bst_lazy = 1, bst_manual = 2, bst_once = 3 }; struct mosquitto__bridge_topic{ char *topic; char *local_prefix; char *remote_prefix; char *local_topic; /* topic prefixed with local_prefix */ char *remote_topic; /* topic prefixed with remote_prefix */ enum mosquitto__bridge_direction direction; uint8_t qos; }; struct bridge_address{ char *address; uint16_t port; }; struct mosquitto__bridge{ char *name; struct bridge_address *addresses; int cur_address; int address_count; time_t primary_retry; mosq_sock_t primary_retry_sock; bool round_robin; bool try_private; bool try_private_accepted; bool clean_start; int8_t clean_start_local; uint16_t keepalive; struct mosquitto__bridge_topic *topics; int topic_count; bool topic_remapping; enum mosquitto__protocol protocol_version; time_t restart_t; char *remote_clientid; char *remote_username; char *remote_password; char *local_clientid; char *local_username; char *local_password; char *notification_topic; char *bind_address; bool notifications; bool notifications_local_only; enum mosquitto_bridge_start_type start_type; int idle_timeout; int restart_timeout; int backoff_base; int backoff_cap; int threshold; uint32_t maximum_packet_size; bool lazy_reconnect; bool attempt_unsubscribe; bool initial_notification_done; bool outgoing_retain; #ifdef WITH_TLS bool tls_insecure; bool tls_ocsp_required; char *tls_cafile; char *tls_capath; char *tls_certfile; char *tls_keyfile; char *tls_version; char *tls_alpn; # ifdef FINAL_WITH_TLS_PSK char *tls_psk_identity; char *tls_psk; # endif #endif }; #ifdef WITH_WEBSOCKETS struct libws_mqtt_hack { char *http_dir; struct mosquitto__listener *listener; }; struct libws_mqtt_data { struct mosquitto *mosq; }; #endif #include extern struct mosquitto_db db; /* ============================================================ * Main functions * ============================================================ */ int mosquitto_main_loop(struct mosquitto__listener_sock *listensock, int listensock_count); /* ============================================================ * Config functions * ============================================================ */ /* Initialise config struct to default values. */ void config__init(struct mosquitto__config *config); /* Parse command line options into config. */ int config__parse_args(struct mosquitto__config *config, int argc, char *argv[]); /* Read configuration data from config->config_file into config. * If reload is true, don't process config options that shouldn't be reloaded (listeners etc) * Returns 0 on success, 1 if there is a configuration error or if a file cannot be opened. */ int config__read(struct mosquitto__config *config, bool reload); /* Free all config data. */ void config__cleanup(struct mosquitto__config *config); int config__get_dir_files(const char *include_dir, char ***files, int *file_count); int drop_privileges(struct mosquitto__config *config); /* ============================================================ * Server send functions * ============================================================ */ int send__connack(struct mosquitto *context, uint8_t ack, uint8_t reason_code, const mosquitto_property *properties); int send__suback(struct mosquitto *context, uint16_t mid, uint32_t payloadlen, const void *payload); int send__unsuback(struct mosquitto *context, uint16_t mid, int reason_code_count, uint8_t *reason_codes, const mosquitto_property *properties); int send__auth(struct mosquitto *context, uint8_t reason_code, const void *auth_data, uint16_t auth_data_len); /* ============================================================ * Network functions * ============================================================ */ void net__broker_init(void); void net__broker_cleanup(void); struct mosquitto *net__socket_accept(struct mosquitto__listener_sock *listensock); int net__socket_listen(struct mosquitto__listener *listener); int net__socket_get_address(mosq_sock_t sock, char *buf, size_t len, uint16_t *remote_address); int net__tls_load_verify(struct mosquitto__listener *listener); int net__tls_server_ctx(struct mosquitto__listener *listener); int net__load_certificates(struct mosquitto__listener *listener); /* ============================================================ * Read handling functions * ============================================================ */ int handle__packet(struct mosquitto *context); int handle__connack(struct mosquitto *context); int handle__connect(struct mosquitto *context); int handle__disconnect(struct mosquitto *context); int handle__publish(struct mosquitto *context); int handle__subscribe(struct mosquitto *context); int handle__unsubscribe(struct mosquitto *context); int handle__auth(struct mosquitto *context); /* ============================================================ * Database handling * ============================================================ */ int db__open(struct mosquitto__config *config); int db__close(void); #ifdef WITH_PERSISTENCE int persist__backup(bool shutdown); int persist__restore(void); #endif /* Return the number of in-flight messages in count. */ int db__message_count(int *count); int db__message_delete_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state expect_state, int qos); int db__message_insert(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, uint8_t qos, bool retain, struct mosquitto_msg_store *stored, mosquitto_property *properties, bool update); int db__message_remove_incoming(struct mosquitto* context, uint16_t mid); int db__message_release_incoming(struct mosquitto *context, uint16_t mid); int db__message_update_outgoing(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_state state, int qos); void db__message_dequeue_first(struct mosquitto *context, struct mosquitto_msg_data *msg_data); int db__messages_delete(struct mosquitto *context, bool force_free); int 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); int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store *stored, uint32_t message_expiry_interval, dbid_t store_id, enum mosquitto_msg_origin origin); int db__message_store_find(struct mosquitto *context, uint16_t mid, struct mosquitto_client_msg **client_msg); void db__msg_store_add(struct mosquitto_msg_store *store); void db__msg_store_remove(struct mosquitto_msg_store *store); void db__msg_store_ref_inc(struct mosquitto_msg_store *store); void db__msg_store_ref_dec(struct mosquitto_msg_store **store); void db__msg_store_clean(void); void db__msg_store_compact(void); void db__msg_store_free(struct mosquitto_msg_store *store); int db__message_reconnect_reset(struct mosquitto *context); bool db__ready_for_flight(struct mosquitto *context, enum mosquitto_msg_direction dir, int qos); bool db__ready_for_queue(struct mosquitto *context, int qos, struct mosquitto_msg_data *msg_data); void sys_tree__init(void); void sys_tree__update(int interval, time_t start_time); int db__message_write_inflight_out_all(struct mosquitto *context); int db__message_write_inflight_out_latest(struct mosquitto *context); int db__message_write_queued_out(struct mosquitto *context); int db__message_write_queued_in(struct mosquitto *context); void db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg); void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg); void db__expire_all_messages(struct mosquitto *context); /* ============================================================ * Subscription functions * ============================================================ */ int sub__add(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier **root); struct mosquitto__subhier *sub__add_hier_entry(struct mosquitto__subhier *parent, struct mosquitto__subhier **sibling, const char *topic, uint16_t len); int sub__remove(struct mosquitto *context, const char *sub, struct mosquitto__subhier *root, uint8_t *reason); void sub__tree_print(struct mosquitto__subhier *root, int level); int sub__clean_session(struct mosquitto *context); int sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store **stored); int sub__topic_tokenise(const char *subtopic, char **local_sub, char ***topics, const char **sharename); void sub__topic_tokens_free(struct sub__token *tokens); /* ============================================================ * Context functions * ============================================================ */ struct mosquitto *context__init(mosq_sock_t sock); void context__cleanup(struct mosquitto *context, bool force_free); void context__disconnect(struct mosquitto *context); void context__add_to_disused(struct mosquitto *context); void context__free_disused(void); void context__send_will(struct mosquitto *context); void context__add_to_by_id(struct mosquitto *context); void context__remove_from_by_id(struct mosquitto *context); int connect__on_authorised(struct mosquitto *context, void *auth_data_out, uint16_t auth_data_out_len); /* ============================================================ * Control functions * ============================================================ */ #ifdef WITH_CONTROL int control__process(struct mosquitto *context, struct mosquitto_msg_store *stored); void control__cleanup(void); #endif int control__register_callback(struct mosquitto__security_options *opts, MOSQ_FUNC_generic_callback cb_func, const char *topic, void *userdata); int control__unregister_callback(struct mosquitto__security_options *opts, MOSQ_FUNC_generic_callback cb_func, const char *topic); /* ============================================================ * Logging functions * ============================================================ */ int log__init(struct mosquitto__config *config); int log__close(struct mosquitto__config *config); void log__internal(const char *fmt, ...) __attribute__((format(printf, 1, 2))); /* ============================================================ * Bridge functions * ============================================================ */ #ifdef WITH_BRIDGE void bridge__start_all(void); int bridge__new(struct mosquitto__bridge *bridge); void bridge__cleanup(struct mosquitto *context); int bridge__connect(struct mosquitto *context); int bridge__connect_step1(struct mosquitto *context); int bridge__connect_step2(struct mosquitto *context); int bridge__connect_step3(struct mosquitto *context); int bridge__on_connect(struct mosquitto *context); void bridge__packet_cleanup(struct mosquitto *context); void bridge_check(void); int bridge__register_local_connections(void); int 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); int bridge__remap_topic_in(struct mosquitto *context, char **topic); #endif /* ============================================================ * IO multiplex related functions * ============================================================ */ int mux__init(struct mosquitto__listener_sock *listensock, int listensock_count); int mux__loop_prepare(void); int mux__add_out(struct mosquitto *context); int mux__remove_out(struct mosquitto *context); int mux__add_in(struct mosquitto *context); int mux__delete(struct mosquitto *context); int mux__wait(void); int mux__handle(struct mosquitto__listener_sock *listensock, int listensock_count); int mux__cleanup(void); /* ============================================================ * Listener related functions * ============================================================ */ void listener__set_defaults(struct mosquitto__listener *listener); void listeners__reload_all_certificates(void); #ifdef WITH_WEBSOCKETS void listeners__add_websockets(struct lws_context *ws_context, mosq_sock_t fd); #endif /* ============================================================ * Plugin related functions * ============================================================ */ int plugin__load_v5(struct mosquitto__listener *listener, struct mosquitto__auth_plugin *plugin, struct mosquitto_opt *auth_options, int auth_option_count, void *lib); void plugin__handle_disconnect(struct mosquitto *context, int reason); int plugin__handle_message(struct mosquitto *context, struct mosquitto_msg_store *stored); void LIB_ERROR(void); void plugin__handle_tick(void); /* ============================================================ * Property related functions * ============================================================ */ int keepalive__add(struct mosquitto *context); void keepalive__check(void); int keepalive__remove(struct mosquitto *context); void keepalive__remove_all(void); int keepalive__update(struct mosquitto *context); /* ============================================================ * Property related functions * ============================================================ */ int property__process_connect(struct mosquitto *context, mosquitto_property **props); int property__process_will(struct mosquitto *context, struct mosquitto_message_all *msg, mosquitto_property **props); int property__process_disconnect(struct mosquitto *context, mosquitto_property **props); /* ============================================================ * Retain tree related functions * ============================================================ */ int retain__init(void); void retain__clean(struct mosquitto__retainhier **retainhier); int retain__queue(struct mosquitto *context, const char *sub, uint8_t sub_qos, uint32_t subscription_identifier); int retain__store(const char *topic, struct mosquitto_msg_store *stored, char **split_topics); /* ============================================================ * Security related functions * ============================================================ */ int acl__find_acls(struct mosquitto *context); int mosquitto_security_module_init(void); int mosquitto_security_module_cleanup(void); int mosquitto_security_init(bool reload); int mosquitto_security_apply(void); int mosquitto_security_cleanup(bool reload); int mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void* payload, uint8_t qos, bool retain, int access); int mosquitto_unpwd_check(struct mosquitto *context); int mosquitto_psk_key_get(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len); int mosquitto_security_init_default(bool reload); int mosquitto_security_apply_default(void); int mosquitto_security_cleanup_default(bool reload); int mosquitto_psk_key_get_default(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len); int 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); int mosquitto_security_auth_continue(struct mosquitto *context, const void *data_in, uint16_t data_len, void **data_out, uint16_t *data_out_len); void unpwd__free_item(struct mosquitto__unpwd **unpwd, struct mosquitto__unpwd *item); /* ============================================================ * Session expiry * ============================================================ */ int session_expiry__add(struct mosquitto *context); int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time); void session_expiry__remove(struct mosquitto *context); void session_expiry__remove_all(void); void session_expiry__check(void); void session_expiry__send_all(void); /* ============================================================ * Signals * ============================================================ */ void handle_sigint(int signal); void handle_sigusr1(int signal); void handle_sigusr2(int signal); #ifdef SIGHUP void handle_sighup(int signal); #endif /* ============================================================ * Window service and signal related functions * ============================================================ */ #if defined(WIN32) || defined(__CYGWIN__) void service_install(void); void service_uninstall(void); void service_run(void); DWORD WINAPI SigThreadProc(void* data); #endif /* ============================================================ * Websockets related functions * ============================================================ */ #ifdef WITH_WEBSOCKETS void mosq_websockets_init(struct mosquitto__listener *listener, const struct mosquitto__config *conf); #endif void do_disconnect(struct mosquitto *context, int reason); /* ============================================================ * Will delay * ============================================================ */ int will_delay__add(struct mosquitto *context); void will_delay__check(void); void will_delay__send_all(void); void will_delay__remove(struct mosquitto *mosq); /* ============================================================ * Other * ============================================================ */ #ifdef WITH_XTREPORT void xtreport(void); #endif #endif mosquitto-2.0.18/src/mux.c000066400000000000000000000034701450213760600154220ustar00rootroot00000000000000/* Copyright (c) 2009-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. Tatsuzo Osawa - Add epoll. */ #include "mux.h" int mux__init(struct mosquitto__listener_sock *listensock, int listensock_count) { #ifdef WITH_EPOLL return mux_epoll__init(listensock, listensock_count); #else return mux_poll__init(listensock, listensock_count); #endif } int mux__add_out(struct mosquitto *context) { #ifdef WITH_EPOLL return mux_epoll__add_out(context); #else return mux_poll__add_out(context); #endif } int mux__remove_out(struct mosquitto *context) { #ifdef WITH_EPOLL return mux_epoll__remove_out(context); #else return mux_poll__remove_out(context); #endif } int mux__add_in(struct mosquitto *context) { #ifdef WITH_EPOLL return mux_epoll__add_in(context); #else return mux_poll__add_in(context); #endif } int mux__delete(struct mosquitto *context) { #ifdef WITH_EPOLL return mux_epoll__delete(context); #else return mux_poll__delete(context); #endif } int mux__handle(struct mosquitto__listener_sock *listensock, int listensock_count) { #ifdef WITH_EPOLL UNUSED(listensock); UNUSED(listensock_count); return mux_epoll__handle(); #else return mux_poll__handle(listensock, listensock_count); #endif } int mux__cleanup(void) { #ifdef WITH_EPOLL return mux_epoll__cleanup(); #else return mux_poll__cleanup(); #endif } mosquitto-2.0.18/src/mux.h000066400000000000000000000026101450213760600154220ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef MUX_H #define MUX_H #include "mosquitto_broker_internal.h" int mux_epoll__init(struct mosquitto__listener_sock *listensock, int listensock_count); int mux_epoll__add_out(struct mosquitto *context); int mux_epoll__remove_out(struct mosquitto *context); int mux_epoll__add_in(struct mosquitto *context); int mux_epoll__delete(struct mosquitto *context); int mux_epoll__handle(void); int mux_epoll__cleanup(void); int mux_poll__init(struct mosquitto__listener_sock *listensock, int listensock_count); int mux_poll__add_out(struct mosquitto *context); int mux_poll__remove_out(struct mosquitto *context); int mux_poll__add_in(struct mosquitto *context); int mux_poll__delete(struct mosquitto *context); int mux_poll__handle(struct mosquitto__listener_sock *listensock, int listensock_count); int mux_poll__cleanup(void); #endif mosquitto-2.0.18/src/mux_epoll.c000066400000000000000000000160751450213760600166220ustar00rootroot00000000000000/* Copyright (c) 2009-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. Tatsuzo Osawa - Add epoll. */ #include "config.h" #ifdef WITH_EPOLL #ifndef WIN32 # define _GNU_SOURCE #endif #include #ifndef WIN32 #ifdef WITH_EPOLL #include #define MAX_EVENTS 1000 #endif #include #include #else #include #include #include #endif #include #include #include #include #ifndef WIN32 # include #endif #include #ifdef WITH_WEBSOCKETS # include #endif #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mux.h" #include "packet_mosq.h" #include "send_mosq.h" #include "sys_tree.h" #include "time_mosq.h" #include "util_mosq.h" #ifdef WIN32 # error "epoll not supported on WIN32" #endif static void loop_handle_reads_writes(struct mosquitto *context, uint32_t events); static sigset_t my_sigblock; static struct epoll_event ep_events[MAX_EVENTS]; int mux_epoll__init(struct mosquitto__listener_sock *listensock, int listensock_count) { struct epoll_event ev; int i; #ifndef WIN32 sigemptyset(&my_sigblock); sigaddset(&my_sigblock, SIGINT); sigaddset(&my_sigblock, SIGTERM); sigaddset(&my_sigblock, SIGUSR1); sigaddset(&my_sigblock, SIGUSR2); sigaddset(&my_sigblock, SIGHUP); #endif memset(&ep_events, 0, sizeof(struct epoll_event)*MAX_EVENTS); db.epollfd = 0; if ((db.epollfd = epoll_create(MAX_EVENTS)) == -1) { log__printf(NULL, MOSQ_LOG_ERR, "Error in epoll creating: %s", strerror(errno)); return MOSQ_ERR_UNKNOWN; } memset(&ev, 0, sizeof(struct epoll_event)); for(i=0; ievents & EPOLLOUT)) { memset(&ev, 0, sizeof(struct epoll_event)); ev.data.ptr = context; ev.events = EPOLLIN | EPOLLOUT; if(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1) { if((errno != EEXIST)||(epoll_ctl(db.epollfd, EPOLL_CTL_MOD, context->sock, &ev) == -1)) { log__printf(NULL, MOSQ_LOG_DEBUG, "Error in epoll re-registering to EPOLLOUT: %s", strerror(errno)); } } context->events = EPOLLIN | EPOLLOUT; } return MOSQ_ERR_SUCCESS; } int mux_epoll__remove_out(struct mosquitto *context) { struct epoll_event ev; if(context->events & EPOLLOUT) { memset(&ev, 0, sizeof(struct epoll_event)); ev.data.ptr = context; ev.events = EPOLLIN; if(epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1) { if((errno != EEXIST)||(epoll_ctl(db.epollfd, EPOLL_CTL_MOD, context->sock, &ev) == -1)) { log__printf(NULL, MOSQ_LOG_DEBUG, "Error in epoll re-registering to EPOLLIN: %s", strerror(errno)); } } context->events = EPOLLIN; } return MOSQ_ERR_SUCCESS; } int mux_epoll__add_in(struct mosquitto *context) { struct epoll_event ev; memset(&ev, 0, sizeof(struct epoll_event)); ev.events = EPOLLIN; ev.data.ptr = context; if (epoll_ctl(db.epollfd, EPOLL_CTL_ADD, context->sock, &ev) == -1) { if(errno != EEXIST){ log__printf(NULL, MOSQ_LOG_ERR, "Error in epoll accepting: %s", strerror(errno)); } } context->events = EPOLLIN; return MOSQ_ERR_SUCCESS; } int mux_epoll__delete(struct mosquitto *context) { struct epoll_event ev; memset(&ev, 0, sizeof(struct epoll_event)); if(context->sock != INVALID_SOCKET){ if(epoll_ctl(db.epollfd, EPOLL_CTL_DEL, context->sock, &ev) == -1){ return 1; } } return 0; } int mux_epoll__handle(void) { int i; struct epoll_event ev; sigset_t origsig; struct mosquitto *context; struct mosquitto__listener_sock *listensock; int event_count; memset(&ev, 0, sizeof(struct epoll_event)); sigprocmask(SIG_SETMASK, &my_sigblock, &origsig); event_count = epoll_wait(db.epollfd, ep_events, MAX_EVENTS, 100); sigprocmask(SIG_SETMASK, &origsig, NULL); db.now_s = mosquitto_time(); db.now_real_s = time(NULL); switch(event_count){ case -1: if(errno != EINTR){ log__printf(NULL, MOSQ_LOG_ERR, "Error in epoll waiting: %s.", strerror(errno)); } break; case 0: break; default: for(i=0; iident == id_client){ loop_handle_reads_writes(context, ep_events[i].events); }else if(context->ident == id_listener){ listensock = ep_events[i].data.ptr; if (ep_events[i].events & (EPOLLIN | EPOLLPRI)){ while((context = net__socket_accept(listensock)) != NULL){ context->events = EPOLLIN; mux__add_in(context); } } #ifdef WITH_WEBSOCKETS }else if(context->ident == id_listener_ws){ /* Nothing needs to happen here, because we always call lws_service in the loop. * The important point is we've been woken up for this listener. */ #endif } } } return MOSQ_ERR_SUCCESS; } int mux_epoll__cleanup(void) { (void)close(db.epollfd); db.epollfd = 0; return MOSQ_ERR_SUCCESS; } static void loop_handle_reads_writes(struct mosquitto *context, uint32_t events) { int err; socklen_t len; int rc; if(context->sock == INVALID_SOCKET){ return; } #ifdef WITH_WEBSOCKETS if(context->wsi){ struct lws_pollfd wspoll; wspoll.fd = context->sock; wspoll.events = (int16_t)context->events; wspoll.revents = (int16_t)events; lws_service_fd(lws_get_context(context->wsi), &wspoll); return; } #endif if(events & EPOLLOUT #ifdef WITH_TLS || context->want_write || (context->ssl && context->state == mosq_cs_new) #endif ){ if(context->state == mosq_cs_connect_pending){ len = sizeof(int); if(!getsockopt(context->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){ if(err == 0){ mosquitto__set_state(context, mosq_cs_new); #if defined(WITH_ADNS) && defined(WITH_BRIDGE) if(context->bridge){ bridge__connect_step3(context); } #endif } }else{ do_disconnect(context, MOSQ_ERR_CONN_LOST); return; } } rc = packet__write(context); if(rc){ do_disconnect(context, rc); return; } } if(events & EPOLLIN #ifdef WITH_TLS || (context->ssl && context->state == mosq_cs_new) #endif ){ do{ rc = packet__read(context); if(rc){ do_disconnect(context, rc); return; } }while(SSL_DATA_PENDING(context)); }else{ if(events & (EPOLLERR | EPOLLHUP)){ do_disconnect(context, MOSQ_ERR_CONN_LOST); return; } } } #endif mosquitto-2.0.18/src/mux_poll.c000066400000000000000000000172701450213760600164530ustar00rootroot00000000000000/* Copyright (c) 2009-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifndef WITH_EPOLL #ifndef WIN32 # define _GNU_SOURCE #endif #include #ifndef WIN32 #include #include #else #include #include #include #endif #include #include #include #include #ifndef WIN32 # include #endif #include #ifdef WITH_WEBSOCKETS # include #endif #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "send_mosq.h" #include "sys_tree.h" #include "time_mosq.h" #include "util_mosq.h" #include "mux.h" static void loop_handle_reads_writes(void); static struct pollfd *pollfds = NULL; static size_t pollfd_max, pollfd_current_max; #ifndef WIN32 static sigset_t my_sigblock; #endif int mux_poll__init(struct mosquitto__listener_sock *listensock, int listensock_count) { size_t i; size_t pollfd_index = 0; #ifndef WIN32 sigemptyset(&my_sigblock); sigaddset(&my_sigblock, SIGINT); sigaddset(&my_sigblock, SIGTERM); sigaddset(&my_sigblock, SIGUSR1); sigaddset(&my_sigblock, SIGUSR2); sigaddset(&my_sigblock, SIGHUP); #endif #ifdef WIN32 pollfd_max = (size_t)_getmaxstdio(); #else pollfd_max = (size_t)sysconf(_SC_OPEN_MAX); #endif pollfds = mosquitto__calloc(pollfd_max, sizeof(struct pollfd)); if(!pollfds){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } memset(pollfds, 0, sizeof(struct pollfd)*pollfd_max); for(i=0; ievents == evt){ return MOSQ_ERR_SUCCESS; } if(context->pollfd_index != -1){ pollfds[context->pollfd_index].fd = context->sock; pollfds[context->pollfd_index].events = (short int)evt; pollfds[context->pollfd_index].revents = 0; }else{ for(i=0; isock; pollfds[i].events = POLLIN; pollfds[i].revents = 0; context->pollfd_index = (int)i; if(i > pollfd_current_max){ pollfd_current_max = i; } break; } } } context->events = evt; return MOSQ_ERR_SUCCESS; } int mux_poll__add_out(struct mosquitto *context) { return mux_poll__add(context, POLLIN | POLLOUT); } int mux_poll__remove_out(struct mosquitto *context) { if(context->events & POLLOUT) { return mux_poll__add_in(context); }else{ return MOSQ_ERR_SUCCESS; } } int mux_poll__add_in(struct mosquitto *context) { return mux_poll__add(context, POLLIN); } int mux_poll__delete(struct mosquitto *context) { size_t pollfd_index; if(context->pollfd_index != -1){ pollfds[context->pollfd_index].fd = INVALID_SOCKET; pollfds[context->pollfd_index].events = 0; pollfds[context->pollfd_index].revents = 0; pollfd_index = (size_t )context->pollfd_index; context->pollfd_index = -1; /* If this is the highest index, reduce the current max until we find * the next highest in use index. */ while(pollfd_index == pollfd_current_max && pollfd_index > 0 && pollfds[pollfd_index].fd == INVALID_SOCKET){ pollfd_index--; pollfd_current_max--; } } return MOSQ_ERR_SUCCESS; } int mux_poll__handle(struct mosquitto__listener_sock *listensock, int listensock_count) { struct mosquitto *context; int i; int fdcount; #ifndef WIN32 sigset_t origsig; #endif #ifndef WIN32 sigprocmask(SIG_SETMASK, &my_sigblock, &origsig); fdcount = poll(pollfds, pollfd_current_max+1, 100); sigprocmask(SIG_SETMASK, &origsig, NULL); #else fdcount = WSAPoll(pollfds, pollfd_current_max+1, 100); #endif db.now_s = mosquitto_time(); db.now_real_s = time(NULL); if(fdcount == -1){ # ifdef WIN32 if(WSAGetLastError() == WSAEINVAL){ /* WSAPoll() immediately returns an error if it is not given * any sockets to wait on. This can happen if we only have * websockets listeners. Sleep a little to prevent a busy loop. */ Sleep(10); }else # endif { log__printf(NULL, MOSQ_LOG_ERR, "Error in poll: %s.", strerror(errno)); } }else{ loop_handle_reads_writes(); for(i=0; iws_context){ /* Nothing needs to happen here, because we always call lws_service in the loop. * The important point is we've been woken up for this listener. */ }else #endif { while((context = net__socket_accept(&listensock[i])) != NULL){ context->pollfd_index = -1; mux__add_in(context); } } } } } return MOSQ_ERR_SUCCESS; } int mux_poll__cleanup(void) { mosquitto__free(pollfds); pollfds = NULL; return MOSQ_ERR_SUCCESS; } static void loop_handle_reads_writes(void) { struct mosquitto *context, *ctxt_tmp; int err; socklen_t len; int rc; HASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){ if(context->pollfd_index < 0){ continue; } if(pollfds[context->pollfd_index].fd == INVALID_SOCKET){ continue; } assert(pollfds[context->pollfd_index].fd == context->sock); #ifdef WITH_WEBSOCKETS if(context->wsi){ struct lws_pollfd wspoll; wspoll.fd = pollfds[context->pollfd_index].fd; wspoll.events = pollfds[context->pollfd_index].events; wspoll.revents = pollfds[context->pollfd_index].revents; lws_service_fd(lws_get_context(context->wsi), &wspoll); continue; } #endif #ifdef WITH_TLS if(pollfds[context->pollfd_index].revents & POLLOUT || context->want_write || (context->ssl && context->state == mosq_cs_new)){ #else if(pollfds[context->pollfd_index].revents & POLLOUT){ #endif if(context->state == mosq_cs_connect_pending){ len = sizeof(int); if(!getsockopt(context->sock, SOL_SOCKET, SO_ERROR, (char *)&err, &len)){ if(err == 0){ mosquitto__set_state(context, mosq_cs_new); #if defined(WITH_ADNS) && defined(WITH_BRIDGE) if(context->bridge){ bridge__connect_step3(context); continue; } #endif } }else{ do_disconnect(context, MOSQ_ERR_CONN_LOST); continue; } } rc = packet__write(context); if(rc){ do_disconnect(context, rc); continue; } } } HASH_ITER(hh_sock, db.contexts_by_sock, context, ctxt_tmp){ if(context->pollfd_index < 0){ continue; } #ifdef WITH_WEBSOCKETS if(context->wsi){ // Websocket are already handled above continue; } #endif #ifdef WITH_TLS if(pollfds[context->pollfd_index].revents & POLLIN || (context->ssl && context->state == mosq_cs_new)){ #else if(pollfds[context->pollfd_index].revents & POLLIN){ #endif do{ rc = packet__read(context); if(rc){ do_disconnect(context, rc); continue; } }while(SSL_DATA_PENDING(context)); }else{ if(context->pollfd_index >= 0 && pollfds[context->pollfd_index].revents & (POLLERR | POLLNVAL | POLLHUP)){ do_disconnect(context, MOSQ_ERR_CONN_LOST); continue; } } } } #endif mosquitto-2.0.18/src/net.c000066400000000000000000000667161450213760600154130ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifndef WIN32 # include # include # include # include # include # include # include #else # include # include #endif #include #include #include #include #include #ifdef WITH_WRAP # include #endif #ifdef HAVE_NETINET_IN_H # include #endif #ifdef WITH_UNIX_SOCKETS # include "sys/stat.h" # include "sys/un.h" #endif #ifdef __QNX__ # include #endif #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "misc_mosq.h" #include "net_mosq.h" #include "util_mosq.h" #ifdef WITH_TLS # include "tls_mosq.h" # include static int tls_ex_index_context = -1; static int tls_ex_index_listener = -1; #endif #include "sys_tree.h" /* For EMFILE handling */ static mosq_sock_t spare_sock = INVALID_SOCKET; void net__broker_init(void) { spare_sock = socket(AF_INET, SOCK_STREAM, 0); net__init(); #ifdef WITH_TLS net__init_tls(); #endif } void net__broker_cleanup(void) { if(spare_sock != INVALID_SOCKET){ COMPAT_CLOSE(spare_sock); spare_sock = INVALID_SOCKET; } net__cleanup(); } static void net__print_error(unsigned int log, const char *format_str) { char *buf; #ifdef WIN32 FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, WSAGetLastError(), LANG_NEUTRAL, (LPTSTR)&buf, 0, NULL); log__printf(NULL, log, format_str, buf); LocalFree(buf); #else buf = strerror(errno); log__printf(NULL, log, format_str, buf); #endif } struct mosquitto *net__socket_accept(struct mosquitto__listener_sock *listensock) { mosq_sock_t new_sock = INVALID_SOCKET; struct mosquitto *new_context; #ifdef WITH_TLS BIO *bio; int rc; char ebuf[256]; unsigned long e; #endif #ifdef WITH_WRAP struct request_info wrap_req; char address[1024]; #endif new_sock = accept(listensock->sock, NULL, 0); if(new_sock == INVALID_SOCKET){ #ifdef WIN32 errno = WSAGetLastError(); if(errno == WSAEMFILE){ #else if(errno == EMFILE || errno == ENFILE){ #endif /* Close the spare socket, which means we should be able to accept * this connection. Accept it, then close it immediately and create * a new spare_sock. This prevents the situation of ever properly * running out of sockets. * It would be nice to send a "server not available" connack here, * but there are lots of reasons why this would be tricky (TLS * being the big one). */ COMPAT_CLOSE(spare_sock); new_sock = accept(listensock->sock, NULL, 0); if(new_sock != INVALID_SOCKET){ COMPAT_CLOSE(new_sock); } spare_sock = socket(AF_INET, SOCK_STREAM, 0); log__printf(NULL, MOSQ_LOG_WARNING, "Unable to accept new connection, system socket count has been exceeded. Try increasing \"ulimit -n\" or equivalent."); } return NULL; } G_SOCKET_CONNECTIONS_INC(); if(net__socket_nonblock(&new_sock)){ return NULL; } #ifdef WITH_WRAP /* Use tcpd / libwrap to determine whether a connection is allowed. */ request_init(&wrap_req, RQ_FILE, new_sock, RQ_DAEMON, "mosquitto", 0); fromhost(&wrap_req); if(!hosts_access(&wrap_req)){ /* Access is denied */ if(db.config->connection_messages == true){ if(!net__socket_get_address(new_sock, address, 1024, NULL)){ log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied access by tcpd.", address); } } COMPAT_CLOSE(new_sock); return NULL; } #endif if(db.config->set_tcp_nodelay){ int flag = 1; #ifdef WIN32 if (setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY, (char *)&flag, sizeof(int)) != 0) { #else if(setsockopt(new_sock, IPPROTO_TCP, TCP_NODELAY, &flag, sizeof(int)) != 0){ #endif log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Unable to set TCP_NODELAY."); } } new_context = context__init(new_sock); if(!new_context){ COMPAT_CLOSE(new_sock); return NULL; } new_context->listener = listensock->listener; if(!new_context->listener){ context__cleanup(new_context, true); return NULL; } new_context->listener->client_count++; if(new_context->listener->max_connections > 0 && new_context->listener->client_count > new_context->listener->max_connections){ if(db.config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", new_context->address); } context__cleanup(new_context, true); return NULL; } #ifdef WITH_TLS /* TLS init */ if(new_context->listener->ssl_ctx){ new_context->ssl = SSL_new(new_context->listener->ssl_ctx); if(!new_context->ssl){ context__cleanup(new_context, true); return NULL; } SSL_set_ex_data(new_context->ssl, tls_ex_index_context, new_context); SSL_set_ex_data(new_context->ssl, tls_ex_index_listener, new_context->listener); new_context->want_write = true; bio = BIO_new_socket(new_sock, BIO_NOCLOSE); SSL_set_bio(new_context->ssl, bio, bio); ERR_clear_error(); rc = SSL_accept(new_context->ssl); if(rc != 1){ rc = SSL_get_error(new_context->ssl, rc); if(rc == SSL_ERROR_WANT_READ){ /* We always want to read. */ }else if(rc == SSL_ERROR_WANT_WRITE){ new_context->want_write = true; }else{ if(db.config->connection_messages == true){ e = ERR_get_error(); while(e){ log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s failed: %s.", new_context->address, ERR_error_string(e, ebuf)); e = ERR_get_error(); } } context__cleanup(new_context, true); return NULL; } } } #endif if(db.config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_NOTICE, "New connection from %s:%d on port %d.", new_context->address, new_context->remote_port, new_context->listener->port); } return new_context; } #ifdef WITH_TLS static int client_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx) { UNUSED(ctx); /* Preverify should check expiry, revocation. */ return preverify_ok; } #endif #ifdef FINAL_WITH_TLS_PSK static unsigned int psk_server_callback(SSL *ssl, const char *identity, unsigned char *psk, unsigned int max_psk_len) { struct mosquitto *context; struct mosquitto__listener *listener; char *psk_key = NULL; int len; const char *psk_hint; if(!identity) return 0; context = SSL_get_ex_data(ssl, tls_ex_index_context); if(!context) return 0; listener = SSL_get_ex_data(ssl, tls_ex_index_listener); if(!listener) return 0; psk_hint = listener->psk_hint; /* The hex to BN conversion results in the length halving, so we can pass * max_psk_len*2 as the max hex key here. */ psk_key = mosquitto__calloc(1, (size_t)max_psk_len*2 + 1); if(!psk_key) return 0; if(mosquitto_psk_key_get(context, psk_hint, identity, psk_key, (int)max_psk_len*2) != MOSQ_ERR_SUCCESS){ mosquitto__free(psk_key); return 0; } len = mosquitto__hex2bin(psk_key, psk, (int)max_psk_len); if (len < 0){ mosquitto__free(psk_key); return 0; } if(listener->use_identity_as_username){ if(mosquitto_validate_utf8(identity, (int)strlen(identity))){ mosquitto__free(psk_key); return 0; } context->username = mosquitto__strdup(identity); if(!context->username){ mosquitto__free(psk_key); return 0; } } mosquitto__free(psk_key); return (unsigned int)len; } #endif #ifdef WITH_TLS int net__tls_server_ctx(struct mosquitto__listener *listener) { char buf[256]; int rc; FILE *dhparamfile; DH *dhparam = NULL; if(listener->ssl_ctx){ SSL_CTX_free(listener->ssl_ctx); } #if OPENSSL_VERSION_NUMBER < 0x10100000L listener->ssl_ctx = SSL_CTX_new(SSLv23_server_method()); #else listener->ssl_ctx = SSL_CTX_new(TLS_server_method()); #endif if(!listener->ssl_ctx){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to create TLS context."); return MOSQ_ERR_TLS; } #ifdef SSL_OP_NO_TLSv1_3 if(db.config->per_listener_settings){ if(listener->security_options.psk_file){ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_TLSv1_3); } }else{ if(db.config->security_options.psk_file){ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_TLSv1_3); } } #endif if(listener->tls_version == NULL){ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); #ifdef SSL_OP_NO_TLSv1_3 }else if(!strcmp(listener->tls_version, "tlsv1.3")){ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2); #endif }else if(!strcmp(listener->tls_version, "tlsv1.2")){ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1); }else if(!strcmp(listener->tls_version, "tlsv1.1")){ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_SSLv3 | SSL_OP_NO_TLSv1); }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unsupported tls_version \"%s\".", listener->tls_version); return MOSQ_ERR_TLS; } /* Use a new key when using temporary/ephemeral DH parameters. * This shouldn't be necessary, but we can't guarantee that `dhparam` has * been generated using strong primes. */ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_SINGLE_DH_USE); #ifdef SSL_OP_NO_COMPRESSION /* Disable compression */ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_COMPRESSION); #endif #ifdef SSL_OP_CIPHER_SERVER_PREFERENCE /* Server chooses cipher */ SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_CIPHER_SERVER_PREFERENCE); #endif #ifdef SSL_MODE_RELEASE_BUFFERS /* Use even less memory per SSL connection. */ SSL_CTX_set_mode(listener->ssl_ctx, SSL_MODE_RELEASE_BUFFERS); #endif #ifdef WITH_EC #if OPENSSL_VERSION_NUMBER >= 0x10002000L && OPENSSL_VERSION_NUMBER < 0x10100000L SSL_CTX_set_ecdh_auto(listener->ssl_ctx, 1); #endif #endif #if OPENSSL_VERSION_NUMBER >= 0x10100000L SSL_CTX_set_dh_auto(listener->ssl_ctx, 1); #endif #ifdef SSL_OP_NO_RENEGOTIATION SSL_CTX_set_options(listener->ssl_ctx, SSL_OP_NO_RENEGOTIATION); #endif snprintf(buf, 256, "mosquitto-%d", listener->port); SSL_CTX_set_session_id_context(listener->ssl_ctx, (unsigned char *)buf, (unsigned int)strlen(buf)); if(listener->ciphers){ rc = SSL_CTX_set_cipher_list(listener->ssl_ctx, listener->ciphers); if(rc == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", listener->ciphers); return MOSQ_ERR_TLS; } }else{ rc = SSL_CTX_set_cipher_list(listener->ssl_ctx, "DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2:@STRENGTH"); if(rc == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", listener->ciphers); return MOSQ_ERR_TLS; } } #if OPENSSL_VERSION_NUMBER >= 0x10101000 && (!defined(LIBRESSL_VERSION_NUMBER) || LIBRESSL_VERSION_NUMBER > 0x3040000FL) if(listener->ciphers_tls13){ rc = SSL_CTX_set_ciphersuites(listener->ssl_ctx, listener->ciphers_tls13); if(rc == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set TLS 1.3 ciphersuites. Check cipher_tls13 list \"%s\".", listener->ciphers_tls13); return MOSQ_ERR_TLS; } } #endif if(listener->dhparamfile){ dhparamfile = mosquitto__fopen(listener->dhparamfile, "r", true); if(!dhparamfile){ log__printf(NULL, MOSQ_LOG_ERR, "Error loading dhparamfile \"%s\".", listener->dhparamfile); return MOSQ_ERR_TLS; } dhparam = PEM_read_DHparams(dhparamfile, NULL, NULL, NULL); fclose(dhparamfile); if(dhparam == NULL || SSL_CTX_set_tmp_dh(listener->ssl_ctx, dhparam) != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error loading dhparamfile \"%s\".", listener->dhparamfile); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } } return MOSQ_ERR_SUCCESS; } #endif #ifdef WITH_TLS static int net__load_crl_file(struct mosquitto__listener *listener) { X509_STORE *store; X509_LOOKUP *lookup; int rc; store = SSL_CTX_get_cert_store(listener->ssl_ctx); if(!store){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to obtain TLS store."); net__print_error(MOSQ_LOG_ERR, "Error: %s"); return MOSQ_ERR_TLS; } lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); rc = X509_load_crl_file(lookup, listener->crlfile, X509_FILETYPE_PEM); if(rc < 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load certificate revocation file \"%s\". Check crlfile.", listener->crlfile); net__print_error(MOSQ_LOG_ERR, "Error: %s"); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK); return MOSQ_ERR_SUCCESS; } #endif int net__load_certificates(struct mosquitto__listener *listener) { #ifdef WITH_TLS int rc; if(listener->require_certificate){ SSL_CTX_set_verify(listener->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, client_certificate_verify); }else{ SSL_CTX_set_verify(listener->ssl_ctx, SSL_VERIFY_NONE, client_certificate_verify); } rc = SSL_CTX_use_certificate_chain_file(listener->ssl_ctx, listener->certfile); if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load server certificate \"%s\". Check certfile.", listener->certfile); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } if(listener->tls_engine == NULL || listener->tls_keyform == mosq_k_pem){ rc = SSL_CTX_use_PrivateKey_file(listener->ssl_ctx, listener->keyfile, SSL_FILETYPE_PEM); if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load server key file \"%s\". Check keyfile.", listener->keyfile); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } } rc = SSL_CTX_check_private_key(listener->ssl_ctx); if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Server certificate/key are inconsistent."); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } /* Load CRLs if they exist. */ if(listener->crlfile){ rc = net__load_crl_file(listener); if(rc){ return rc; } } #else UNUSED(listener); #endif return MOSQ_ERR_SUCCESS; } #if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) static int net__load_engine(struct mosquitto__listener *listener) { ENGINE *engine = NULL; UI_METHOD *ui_method; EVP_PKEY *pkey; if(!listener->tls_engine){ return MOSQ_ERR_SUCCESS; } engine = ENGINE_by_id(listener->tls_engine); if(!engine){ log__printf(NULL, MOSQ_LOG_ERR, "Error loading %s engine\n", listener->tls_engine); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } if(!ENGINE_init(engine)){ log__printf(NULL, MOSQ_LOG_ERR, "Failed engine initialisation\n"); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } ENGINE_set_default(engine, ENGINE_METHOD_ALL); if(listener->tls_keyform == mosq_k_engine){ ui_method = net__get_ui_method(); if(listener->tls_engine_kpass_sha1){ if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set engine secret mode sha"); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } if(!ENGINE_ctrl_cmd(engine, ENGINE_PIN, 0, listener->tls_engine_kpass_sha1, NULL, 0)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set engine pin"); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } ui_method = NULL; } pkey = ENGINE_load_private_key(engine, listener->keyfile, ui_method, NULL); if(!pkey){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load engine private key file \"%s\".", listener->keyfile); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } if(SSL_CTX_use_PrivateKey(listener->ssl_ctx, pkey) <= 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to use engine private key file \"%s\".", listener->keyfile); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } } ENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */ return MOSQ_ERR_SUCCESS; } #endif int net__tls_load_verify(struct mosquitto__listener *listener) { #ifdef WITH_TLS int rc; # if OPENSSL_VERSION_NUMBER < 0x30000000L if(listener->cafile || listener->capath){ rc = SSL_CTX_load_verify_locations(listener->ssl_ctx, listener->cafile, listener->capath); if(rc == 0){ if(listener->cafile && listener->capath){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load CA certificates. Check cafile \"%s\" and capath \"%s\".", listener->cafile, listener->capath); }else if(listener->cafile){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load CA certificates. Check cafile \"%s\".", listener->cafile); }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load CA certificates. Check capath \"%s\".", listener->capath); } } } # else if(listener->cafile){ rc = SSL_CTX_load_verify_file(listener->ssl_ctx, listener->cafile); if(rc == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load CA certificates. Check cafile \"%s\".", listener->cafile); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } } if(listener->capath){ rc = SSL_CTX_load_verify_dir(listener->ssl_ctx, listener->capath); if(rc == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load CA certificates. Check capath \"%s\".", listener->capath); net__print_ssl_error(NULL); return MOSQ_ERR_TLS; } } # endif # if !defined(OPENSSL_NO_ENGINE) if(net__load_engine(listener)){ return MOSQ_ERR_TLS; } # endif #endif return net__load_certificates(listener); } #ifndef WIN32 static int net__bind_interface(struct mosquitto__listener *listener, struct addrinfo *rp) { /* * This binds the listener sock to a network interface. * The use of SO_BINDTODEVICE requires root access, which we don't have, so instead * use getifaddrs to find the interface addresses, and use IP of the * matching interface in the later bind(). */ struct ifaddrs *ifaddr, *ifa; bool have_interface = false; if(getifaddrs(&ifaddr) < 0){ net__print_error(MOSQ_LOG_ERR, "Error: %s"); return MOSQ_ERR_ERRNO; } for(ifa=ifaddr; ifa!=NULL; ifa=ifa->ifa_next){ if(ifa->ifa_addr == NULL){ continue; } if(!strcasecmp(listener->bind_interface, ifa->ifa_name)){ have_interface = true; if(ifa->ifa_addr->sa_family == rp->ai_addr->sa_family){ if(rp->ai_addr->sa_family == AF_INET){ if(listener->host && memcmp(&((struct sockaddr_in *)rp->ai_addr)->sin_addr, &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, sizeof(struct in_addr))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface address for %s does not match specified listener address (%s).", listener->bind_interface, listener->host); return MOSQ_ERR_INVAL; }else{ memcpy(&((struct sockaddr_in *)rp->ai_addr)->sin_addr, &((struct sockaddr_in *)ifa->ifa_addr)->sin_addr, sizeof(struct in_addr)); freeifaddrs(ifaddr); return MOSQ_ERR_SUCCESS; } }else if(rp->ai_addr->sa_family == AF_INET6){ if(listener->host && memcmp(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, sizeof(struct in6_addr))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface address for %s does not match specified listener address (%s).", listener->bind_interface, listener->host); return MOSQ_ERR_INVAL; }else{ memcpy(&((struct sockaddr_in6 *)rp->ai_addr)->sin6_addr, &((struct sockaddr_in6 *)ifa->ifa_addr)->sin6_addr, sizeof(struct in6_addr)); freeifaddrs(ifaddr); return MOSQ_ERR_SUCCESS; } } } } } freeifaddrs(ifaddr); if(have_interface){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Interface %s does not support %s configuration.", listener->bind_interface, rp->ai_addr->sa_family == AF_INET ? "IPv4" : "IPv6"); return MOSQ_ERR_NOT_SUPPORTED; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Interface %s does not exist.", listener->bind_interface); return MOSQ_ERR_NOT_FOUND; } } #endif static int net__socket_listen_tcp(struct mosquitto__listener *listener) { mosq_sock_t sock = INVALID_SOCKET; struct addrinfo hints; struct addrinfo *ainfo, *rp; char service[10]; int rc; int ss_opt = 1; #ifndef WIN32 bool interface_bound = false; #endif if(!listener) return MOSQ_ERR_INVAL; snprintf(service, 10, "%d", listener->port); memset(&hints, 0, sizeof(struct addrinfo)); if(listener->socket_domain){ hints.ai_family = listener->socket_domain; }else{ hints.ai_family = AF_UNSPEC; } hints.ai_flags = AI_PASSIVE; hints.ai_socktype = SOCK_STREAM; rc = getaddrinfo(listener->host, service, &hints, &ainfo); if (rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error creating listener: %s.", gai_strerror(rc)); return INVALID_SOCKET; } listener->sock_count = 0; listener->socks = NULL; for(rp = ainfo; rp; rp = rp->ai_next){ if(rp->ai_family == AF_INET){ log__printf(NULL, MOSQ_LOG_INFO, "Opening ipv4 listen socket on port %d.", ntohs(((struct sockaddr_in *)rp->ai_addr)->sin_port)); }else if(rp->ai_family == AF_INET6){ log__printf(NULL, MOSQ_LOG_INFO, "Opening ipv6 listen socket on port %d.", ntohs(((struct sockaddr_in6 *)rp->ai_addr)->sin6_port)); }else{ continue; } sock = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol); if(sock == INVALID_SOCKET){ net__print_error(MOSQ_LOG_WARNING, "Warning: %s"); continue; } listener->sock_count++; listener->socks = mosquitto__realloc(listener->socks, sizeof(mosq_sock_t)*(size_t)listener->sock_count); if(!listener->socks){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); freeaddrinfo(ainfo); COMPAT_CLOSE(sock); return MOSQ_ERR_NOMEM; } listener->socks[listener->sock_count-1] = sock; #ifndef WIN32 ss_opt = 1; /* Unimportant if this fails */ (void)setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &ss_opt, sizeof(ss_opt)); #endif #ifdef IPV6_V6ONLY ss_opt = 1; (void)setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, &ss_opt, sizeof(ss_opt)); #endif if(net__socket_nonblock(&sock)){ freeaddrinfo(ainfo); mosquitto__free(listener->socks); return 1; } #ifndef WIN32 if(listener->bind_interface){ /* It might be possible that an interface does not support all relevant sa_families. * We should successfully find at least one. */ rc = net__bind_interface(listener, rp); if(rc){ COMPAT_CLOSE(sock); listener->sock_count--; if(rc == MOSQ_ERR_NOT_FOUND || rc == MOSQ_ERR_INVAL){ freeaddrinfo(ainfo); return rc; }else{ continue; } } interface_bound = true; } #endif if(bind(sock, rp->ai_addr, rp->ai_addrlen) == -1){ #if defined(__linux__) if(errno == EACCES){ log__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:"); log__printf(NULL, MOSQ_LOG_ERR, " sudo setcap 'CAP_NET_BIND_SERVICE=+ep /usr/sbin/mosquitto'"); } #endif net__print_error(MOSQ_LOG_ERR, "Error: %s"); COMPAT_CLOSE(sock); freeaddrinfo(ainfo); mosquitto__free(listener->socks); return 1; } if(listen(sock, 100) == -1){ net__print_error(MOSQ_LOG_ERR, "Error: %s"); freeaddrinfo(ainfo); COMPAT_CLOSE(sock); mosquitto__free(listener->socks); return 1; } } freeaddrinfo(ainfo); #ifndef WIN32 if(listener->bind_interface && !interface_bound){ mosquitto__free(listener->socks); return 1; } #endif return 0; } #ifdef WITH_UNIX_SOCKETS static int net__socket_listen_unix(struct mosquitto__listener *listener) { struct sockaddr_un addr; int sock; int rc; mode_t old_mask; if(listener->unix_socket_path == NULL){ return MOSQ_ERR_INVAL; } if(strlen(listener->unix_socket_path) > sizeof(addr.sun_path)-1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Path to unix socket is too long \"%s\".", listener->unix_socket_path); return MOSQ_ERR_INVAL; } unlink(listener->unix_socket_path); log__printf(NULL, MOSQ_LOG_INFO, "Opening unix listen socket on path %s.", listener->unix_socket_path); memset(&addr, 0, sizeof(struct sockaddr_un)); addr.sun_family = AF_UNIX; strncpy(addr.sun_path, listener->unix_socket_path, sizeof(addr.sun_path)-1); sock = socket(AF_UNIX, SOCK_STREAM, 0); if(sock == INVALID_SOCKET){ net__print_error(MOSQ_LOG_ERR, "Error creating unix socket: %s"); return 1; } listener->sock_count++; listener->socks = mosquitto__realloc(listener->socks, sizeof(mosq_sock_t)*(size_t)listener->sock_count); if(!listener->socks){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); COMPAT_CLOSE(sock); return MOSQ_ERR_NOMEM; } listener->socks[listener->sock_count-1] = sock; old_mask = umask(0007); rc = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr_un)); umask(old_mask); if(rc == -1){ net__print_error(MOSQ_LOG_ERR, "Error binding unix socket: %s"); return 1; } if(listen(sock, 10) == -1){ net__print_error(MOSQ_LOG_ERR, "Error listening to unix socket: %s"); return 1; } if(net__socket_nonblock(&sock)){ return 1; } return 0; } #endif /* Creates a socket and listens on port 'port'. * Returns 1 on failure * Returns 0 on success. */ int net__socket_listen(struct mosquitto__listener *listener) { int rc; if(!listener) return MOSQ_ERR_INVAL; #ifdef WITH_UNIX_SOCKETS if(listener->port == 0 && listener->unix_socket_path != NULL){ rc = net__socket_listen_unix(listener); }else #endif { rc = net__socket_listen_tcp(listener); } if(rc) return rc; /* We need to have at least one working socket. */ if(listener->sock_count > 0){ #ifdef WITH_TLS if(listener->certfile && listener->keyfile){ if(net__tls_server_ctx(listener)){ return 1; } if(net__tls_load_verify(listener)){ return 1; } } # ifdef FINAL_WITH_TLS_PSK if(listener->psk_hint){ if(tls_ex_index_context == -1){ tls_ex_index_context = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL); } if(tls_ex_index_listener == -1){ tls_ex_index_listener = SSL_get_ex_new_index(0, "listener", NULL, NULL, NULL); } if(listener->certfile == NULL || listener->keyfile == NULL){ if(net__tls_server_ctx(listener)){ return 1; } } SSL_CTX_set_psk_server_callback(listener->ssl_ctx, psk_server_callback); if(listener->psk_hint){ rc = SSL_CTX_use_psk_identity_hint(listener->ssl_ctx, listener->psk_hint); if(rc == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set TLS PSK hint."); net__print_ssl_error(NULL); return 1; } } } # endif /* FINAL_WITH_TLS_PSK */ #endif /* WITH_TLS */ return 0; }else{ return 1; } } int net__socket_get_address(mosq_sock_t sock, char *buf, size_t len, uint16_t *remote_port) { struct sockaddr_storage addr; socklen_t addrlen; memset(&addr, 0, sizeof(struct sockaddr_storage)); addrlen = sizeof(addr); if(!getpeername(sock, (struct sockaddr *)&addr, &addrlen)){ if(addr.ss_family == AF_INET){ if(remote_port){ *remote_port = ntohs(((struct sockaddr_in *)&addr)->sin_port); } if(inet_ntop(AF_INET, &((struct sockaddr_in *)&addr)->sin_addr.s_addr, buf, (socklen_t)len)){ return 0; } }else if(addr.ss_family == AF_INET6){ if(remote_port){ *remote_port = ntohs(((struct sockaddr_in6 *)&addr)->sin6_port); } if(inet_ntop(AF_INET6, &((struct sockaddr_in6 *)&addr)->sin6_addr.s6_addr, buf, (socklen_t)len)){ return 0; } #ifdef WITH_UNIX_SOCKETS }else if(addr.ss_family == AF_UNIX){ struct sockaddr_un un; addrlen = sizeof(struct sockaddr_un); if(!getsockname(sock, (struct sockaddr *)&un, &addrlen)){ snprintf(buf, len, "%s", un.sun_path); }else{ snprintf(buf, len, "unix-socket"); } return 0; #endif } } return 1; } mosquitto-2.0.18/src/password_mosq.c000066400000000000000000000105161450213760600175110ustar00rootroot00000000000000/* Copyright (c) 2012-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #ifdef WITH_TLS # include # include # include # include #endif #include #include #include #include #include "mosquitto.h" #include "mosquitto_broker.h" #include "password_mosq.h" #ifdef WIN32 # include # include # ifndef __cplusplus # if defined(_MSC_VER) && _MSC_VER < 1900 # define bool char # define true 1 # define false 0 # else # include # endif # endif # define snprintf sprintf_s # include # include #else # include # include # include # include #endif #define MAX_BUFFER_LEN 65536 #define SALT_LEN 12 #ifdef WITH_TLS int base64__encode(unsigned char *in, unsigned int in_len, char **encoded) { BIO *bmem, *b64; BUF_MEM *bptr; b64 = BIO_new(BIO_f_base64()); BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bmem = BIO_new(BIO_s_mem()); b64 = BIO_push(b64, bmem); BIO_write(b64, in, (int)in_len); if(BIO_flush(b64) != 1){ BIO_free_all(b64); return 1; } BIO_get_mem_ptr(b64, &bptr); *encoded = malloc(bptr->length+1); if(!(*encoded)){ BIO_free_all(b64); return 1; } memcpy(*encoded, bptr->data, bptr->length); (*encoded)[bptr->length] = '\0'; BIO_free_all(b64); return 0; } int base64__decode(char *in, unsigned char **decoded, unsigned int *decoded_len) { BIO *bmem, *b64; size_t slen; int len; slen = strlen(in); b64 = BIO_new(BIO_f_base64()); if(!b64){ return 1; } BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL); bmem = BIO_new(BIO_s_mem()); if(!bmem){ BIO_free_all(b64); return 1; } b64 = BIO_push(b64, bmem); BIO_write(bmem, in, (int)slen); if(BIO_flush(bmem) != 1){ BIO_free_all(b64); return 1; } *decoded = mosquitto_calloc(slen, 1); if(!(*decoded)){ BIO_free_all(b64); return 1; } len = BIO_read(b64, *decoded, (int)slen); BIO_free_all(b64); if(len <= 0){ mosquitto_free(*decoded); *decoded = NULL; *decoded_len = 0; return 1; } *decoded_len = (unsigned int)len; return 0; } int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_password, int new_iterations) { int rc; unsigned int hash_len; const EVP_MD *digest; int iterations; #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX context; #else EVP_MD_CTX *context; #endif if(new_password){ rc = RAND_bytes(pw->salt, sizeof(pw->salt)); if(!rc){ return MOSQ_ERR_UNKNOWN; } iterations = new_iterations; }else{ iterations = pw->iterations; } if(iterations < 1){ return MOSQ_ERR_INVAL; } digest = EVP_get_digestbyname("sha512"); if(!digest){ return MOSQ_ERR_UNKNOWN; } if(pw->hashtype == pw_sha512){ #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX_init(&context); EVP_DigestInit_ex(&context, digest, NULL); EVP_DigestUpdate(&context, password, strlen(password)); EVP_DigestUpdate(&context, pw->salt, sizeof(pw->salt)); EVP_DigestFinal_ex(&context, pw->password_hash, &hash_len); EVP_MD_CTX_cleanup(&context); #else context = EVP_MD_CTX_new(); EVP_DigestInit_ex(context, digest, NULL); EVP_DigestUpdate(context, password, strlen(password)); EVP_DigestUpdate(context, pw->salt, sizeof(pw->salt)); EVP_DigestFinal_ex(context, pw->password_hash, &hash_len); EVP_MD_CTX_free(context); #endif }else{ pw->iterations = iterations; hash_len = sizeof(pw->password_hash); PKCS5_PBKDF2_HMAC(password, (int)strlen(password), pw->salt, sizeof(pw->salt), iterations, digest, (int)hash_len, pw->password_hash); } return MOSQ_ERR_SUCCESS; } #endif int pw__memcmp_const(const void *a, const void *b, size_t len) { size_t i; int rc = 0; if(!a || !b) return 1; for(i=0; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include enum mosquitto_pwhash_type{ pw_sha512 = 6, pw_sha512_pbkdf2 = 7, }; #define SALT_LEN 12 #define PW_DEFAULT_ITERATIONS 101 struct mosquitto_pw{ unsigned char password_hash[64]; /* For SHA512 */ unsigned char salt[SALT_LEN]; int iterations; enum mosquitto_pwhash_type hashtype; bool valid; }; int pw__hash(const char *password, struct mosquitto_pw *pw, bool new_password, int new_iterations); int pw__memcmp_const(const void *ptr1, const void *b, size_t len); int base64__encode(unsigned char *in, unsigned int in_len, char **encoded); int base64__decode(char *in, unsigned char **decoded, unsigned int *decoded_len); #endif mosquitto-2.0.18/src/persist.h000066400000000000000000000116761450213760600163160ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef PERSIST_H #define PERSIST_H #include "mosquitto_broker_internal.h" #define MOSQ_DB_VERSION 6 /* DB read/write */ extern const unsigned char magic[15]; #define DB_CHUNK_CFG 1 #define DB_CHUNK_MSG_STORE 2 #define DB_CHUNK_CLIENT_MSG 3 #define DB_CHUNK_RETAIN 4 #define DB_CHUNK_SUB 5 #define DB_CHUNK_CLIENT 6 /* End DB read/write */ #define read_e(f, b, c) if(fread(b, 1, c, f) != c){ goto error; } #define write_e(f, b, c) if(fwrite(b, 1, c, f) != c){ goto error; } /* COMPATIBILITY NOTES * * The P_* structs (persist structs) contain all of the data for a particular * data chunk. They are loaded in multiple parts, so can be rearranged without * updating the db format version. * * The PF_* structs (persist fixed structs) contain the fixed size data for a * particular data chunk. They are written to disk as is, so they must not be * rearranged without updating the db format version. When adding new members, * always use explicit sized datatypes ("uint32_t", not "long"), and check * whether what is being added can go in an existing hole in the struct. */ struct PF_header{ uint32_t chunk; uint32_t length; }; struct PF_cfg{ uint64_t last_db_id; uint8_t shutdown; uint8_t dbid_size; }; struct PF_client_v5{ int64_t session_expiry_time; uint32_t session_expiry_interval; uint16_t last_mid; uint16_t id_len; }; struct PF_client{ /* struct PF_client_v5; */ int64_t session_expiry_time; uint32_t session_expiry_interval; uint16_t last_mid; uint16_t id_len; uint16_t listener_port; uint16_t username_len; /* tail: 4 byte padding, because 64bit member * forces multiple of 8 for struct size */ }; struct P_client{ struct PF_client F; char *client_id; char *username; }; struct PF_client_msg{ dbid_t store_id; uint16_t mid; uint16_t id_len; uint8_t qos; uint8_t state; uint8_t retain_dup; uint8_t direction; }; struct P_client_msg{ struct PF_client_msg F; char *client_id; mosquitto_property *properties; }; struct PF_msg_store{ dbid_t store_id; int64_t expiry_time; uint32_t payloadlen; uint16_t source_mid; uint16_t source_id_len; uint16_t source_username_len; uint16_t topic_len; uint16_t source_port; uint8_t qos; uint8_t retain; }; struct P_msg_store{ struct PF_msg_store F; void *payload; struct mosquitto source; char *topic; mosquitto_property *properties; }; struct PF_sub{ uint32_t identifier; uint16_t id_len; uint16_t topic_len; uint8_t qos; uint8_t options; }; struct P_sub{ struct PF_sub F; char *client_id; char *topic; }; struct PF_retain{ dbid_t store_id; }; struct P_retain{ struct PF_retain F; }; int persist__read_string_len(FILE *db_fptr, char **str, uint16_t len); int persist__read_string(FILE *db_fptr, char **str); int persist__chunk_header_read(FILE *db_fptr, uint32_t *chunk, uint32_t *length); int persist__chunk_header_read_v234(FILE *db_fptr, uint32_t *chunk, uint32_t *length); int persist__chunk_cfg_read_v234(FILE *db_fptr, struct PF_cfg *chunk); int persist__chunk_client_read_v234(FILE *db_fptr, struct P_client *chunk, uint32_t db_version); int persist__chunk_client_msg_read_v234(FILE *db_fptr, struct P_client_msg *chunk); int persist__chunk_msg_store_read_v234(FILE *db_fptr, struct P_msg_store *chunk, uint32_t db_version); int persist__chunk_retain_read_v234(FILE *db_fptr, struct P_retain *chunk); int persist__chunk_sub_read_v234(FILE *db_fptr, struct P_sub *chunk); int persist__chunk_header_read_v56(FILE *db_fptr, uint32_t *chunk, uint32_t *length); int persist__chunk_cfg_read_v56(FILE *db_fptr, struct PF_cfg *chunk); int persist__chunk_client_read_v56(FILE *db_fptr, struct P_client *chunk, uint32_t db_version); int persist__chunk_client_msg_read_v56(FILE *db_fptr, struct P_client_msg *chunk, uint32_t length); int persist__chunk_msg_store_read_v56(FILE *db_fptr, struct P_msg_store *chunk, uint32_t length); int persist__chunk_retain_read_v56(FILE *db_fptr, struct P_retain *chunk); int persist__chunk_sub_read_v56(FILE *db_fptr, struct P_sub *chunk); int persist__chunk_cfg_write_v6(FILE *db_fptr, struct PF_cfg *chunk); int persist__chunk_client_write_v6(FILE *db_fptr, struct P_client *chunk); int persist__chunk_client_msg_write_v6(FILE *db_fptr, struct P_client_msg *chunk); int persist__chunk_message_store_write_v6(FILE *db_fptr, struct P_msg_store *chunk); int persist__chunk_retain_write_v6(FILE *db_fptr, struct P_retain *chunk); int persist__chunk_sub_write_v6(FILE *db_fptr, struct P_sub *chunk); #endif mosquitto-2.0.18/src/persist_read.c000066400000000000000000000341161450213760600172760ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WITH_PERSISTENCE #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "persist.h" #include "time_mosq.h" #include "misc_mosq.h" #include "util_mosq.h" uint32_t db_version; const unsigned char magic[15] = {0x00, 0xB5, 0x00, 'm','o','s','q','u','i','t','t','o',' ','d','b'}; static int persist__restore_sub(const char *client_id, const char *sub, uint8_t qos, uint32_t identifier, int options); static struct mosquitto *persist__find_or_add_context(const char *client_id, uint16_t last_mid) { struct mosquitto *context; if(!client_id) return NULL; context = NULL; HASH_FIND(hh_id, db.contexts_by_id, client_id, strlen(client_id), context); if(!context){ context = context__init(INVALID_SOCKET); if(!context) return NULL; context->id = mosquitto__strdup(client_id); if(!context->id){ mosquitto__free(context); return NULL; } context->clean_start = false; context__add_to_by_id(context); } if(last_mid){ context->last_mid = last_mid; } return context; } int persist__read_string_len(FILE *db_fptr, char **str, uint16_t len) { char *s = NULL; if(len){ s = mosquitto__malloc(len+1U); if(!s){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } if(fread(s, 1, len, db_fptr) != len){ mosquitto__free(s); return MOSQ_ERR_NOMEM; } s[len] = '\0'; } *str = s; return MOSQ_ERR_SUCCESS; } int persist__read_string(FILE *db_fptr, char **str) { uint16_t i16temp; uint16_t slen; if(fread(&i16temp, 1, sizeof(uint16_t), db_fptr) != sizeof(uint16_t)){ return MOSQ_ERR_INVAL; } slen = ntohs(i16temp); return persist__read_string_len(db_fptr, str, slen); } static int persist__client_msg_restore(struct P_client_msg *chunk) { struct mosquitto_client_msg *cmsg; struct mosquitto_msg_store_load *load; struct mosquitto *context; struct mosquitto_msg_data *msg_data; HASH_FIND(hh, db.msg_store_load, &chunk->F.store_id, sizeof(dbid_t), load); if(!load){ /* Can't find message - probably expired */ return MOSQ_ERR_SUCCESS; } context = persist__find_or_add_context(chunk->client_id, 0); if(!context){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Persistence file contains client message with no matching client. File may be corrupt."); return 0; } cmsg = mosquitto__calloc(1, sizeof(struct mosquitto_client_msg)); if(!cmsg){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } cmsg->next = NULL; cmsg->store = NULL; cmsg->mid = chunk->F.mid; cmsg->qos = chunk->F.qos; cmsg->retain = (chunk->F.retain_dup&0xF0)>>4; cmsg->timestamp = 0; cmsg->direction = chunk->F.direction; cmsg->state = chunk->F.state; cmsg->dup = chunk->F.retain_dup&0x0F; cmsg->properties = chunk->properties; cmsg->store = load->store; db__msg_store_ref_inc(cmsg->store); if(cmsg->direction == mosq_md_out){ msg_data = &context->msgs_out; }else{ msg_data = &context->msgs_in; } if(chunk->F.state == mosq_ms_queued || (chunk->F.qos > 0 && msg_data->inflight_quota == 0)){ DL_APPEND(msg_data->queued, cmsg); db__msg_add_to_queued_stats(msg_data, cmsg); }else{ DL_APPEND(msg_data->inflight, cmsg); if(chunk->F.qos > 0 && msg_data->inflight_quota > 0){ msg_data->inflight_quota--; } db__msg_add_to_inflight_stats(msg_data, cmsg); } return MOSQ_ERR_SUCCESS; } static int persist__client_chunk_restore(FILE *db_fptr) { int i, rc = 0; struct mosquitto *context; struct P_client chunk; memset(&chunk, 0, sizeof(struct P_client)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_client_read_v56(db_fptr, &chunk, db_version); }else{ rc = persist__chunk_client_read_v234(db_fptr, &chunk, db_version); } if(rc > 0){ return rc; }else if(rc < 0){ /* Client not loaded, but otherwise not an error */ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Empty client entry found in persistence database, it may be corrupt."); return MOSQ_ERR_SUCCESS; } context = persist__find_or_add_context(chunk.client_id, chunk.F.last_mid); if(context){ context->session_expiry_time = chunk.F.session_expiry_time; context->session_expiry_interval = chunk.F.session_expiry_interval; if(chunk.username && !context->username){ /* username is not freed here, it is now owned by context */ context->username = chunk.username; chunk.username = NULL; } /* in per_listener_settings mode, try to find the listener by persisted port */ if(db.config->per_listener_settings && !context->listener && chunk.F.listener_port > 0){ for(i=0; i < db.config->listener_count; i++){ if(db.config->listeners[i].port == chunk.F.listener_port){ context->listener = &db.config->listeners[i]; break; } } } session_expiry__add_from_persistence(context, chunk.F.session_expiry_time); }else{ rc = 1; } mosquitto__free(chunk.client_id); if(chunk.username){ mosquitto__free(chunk.username); } return rc; } static int persist__client_msg_chunk_restore(FILE *db_fptr, uint32_t length) { struct P_client_msg chunk; int rc; memset(&chunk, 0, sizeof(struct P_client_msg)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_client_msg_read_v56(db_fptr, &chunk, length); }else{ rc = persist__chunk_client_msg_read_v234(db_fptr, &chunk); } if(rc){ return rc; } rc = persist__client_msg_restore(&chunk); mosquitto__free(chunk.client_id); return rc; } static int persist__msg_store_chunk_restore(FILE *db_fptr, uint32_t length) { struct P_msg_store chunk; struct mosquitto_msg_store *stored = NULL; struct mosquitto_msg_store_load *load; int64_t message_expiry_interval64; uint32_t message_expiry_interval; int rc = 0; int i; memset(&chunk, 0, sizeof(struct P_msg_store)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_msg_store_read_v56(db_fptr, &chunk, length); }else{ rc = persist__chunk_msg_store_read_v234(db_fptr, &chunk, db_version); } if(rc){ return rc; } if(chunk.F.source_port){ for(i=0; ilistener_count; i++){ if(db.config->listeners[i].port == chunk.F.source_port){ chunk.source.listener = &db.config->listeners[i]; break; } } } load = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store_load)); if(!load){ mosquitto__free(chunk.source.id); mosquitto__free(chunk.source.username); mosquitto__free(chunk.topic); mosquitto__free(chunk.payload); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } if(chunk.F.expiry_time > 0){ message_expiry_interval64 = chunk.F.expiry_time - time(NULL); if(message_expiry_interval64 < 0 || message_expiry_interval64 > UINT32_MAX){ /* Expired message */ mosquitto__free(chunk.source.id); mosquitto__free(chunk.source.username); mosquitto__free(chunk.topic); mosquitto__free(chunk.payload); mosquitto__free(load); return MOSQ_ERR_SUCCESS; }else{ message_expiry_interval = (uint32_t)message_expiry_interval64; } }else{ message_expiry_interval = 0; } stored = mosquitto__calloc(1, sizeof(struct mosquitto_msg_store)); if(stored == NULL){ mosquitto__free(load); mosquitto__free(chunk.source.id); mosquitto__free(chunk.source.username); mosquitto__free(chunk.topic); mosquitto__free(chunk.payload); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } stored->source_mid = chunk.F.source_mid; stored->topic = chunk.topic; stored->qos = chunk.F.qos; stored->payloadlen = chunk.F.payloadlen; stored->retain = chunk.F.retain; stored->properties = chunk.properties; stored->payload = chunk.payload; rc = db__message_store(&chunk.source, stored, message_expiry_interval, chunk.F.store_id, mosq_mo_client); mosquitto__free(chunk.source.id); mosquitto__free(chunk.source.username); chunk.source.id = NULL; chunk.source.username = NULL; if(rc == MOSQ_ERR_SUCCESS){ stored->source_listener = chunk.source.listener; load->db_id = stored->db_id; load->store = stored; HASH_ADD(hh, db.msg_store_load, db_id, sizeof(dbid_t), load); return MOSQ_ERR_SUCCESS; }else{ mosquitto__free(load); return rc; } } static int persist__retain_chunk_restore(FILE *db_fptr) { struct mosquitto_msg_store_load *load; struct P_retain chunk; int rc; char **split_topics; char *local_topic; memset(&chunk, 0, sizeof(struct P_retain)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_retain_read_v56(db_fptr, &chunk); }else{ rc = persist__chunk_retain_read_v234(db_fptr, &chunk); } if(rc){ return rc; } HASH_FIND(hh, db.msg_store_load, &chunk.F.store_id, sizeof(dbid_t), load); if(load){ if(sub__topic_tokenise(load->store->topic, &local_topic, &split_topics, NULL)) return 1; retain__store(load->store->topic, load->store, split_topics); mosquitto__free(local_topic); mosquitto__free(split_topics); }else{ /* Can't find the message - probably expired */ } return MOSQ_ERR_SUCCESS; } static int persist__sub_chunk_restore(FILE *db_fptr) { struct P_sub chunk; int rc; memset(&chunk, 0, sizeof(struct P_sub)); if(db_version == 6 || db_version == 5){ rc = persist__chunk_sub_read_v56(db_fptr, &chunk); }else{ rc = persist__chunk_sub_read_v234(db_fptr, &chunk); } if(rc){ return rc; } rc = persist__restore_sub(chunk.client_id, chunk.topic, chunk.F.qos, chunk.F.identifier, chunk.F.options); mosquitto__free(chunk.client_id); mosquitto__free(chunk.topic); return rc; } int persist__chunk_header_read(FILE *db_fptr, uint32_t *chunk, uint32_t *length) { if(db_version == 6 || db_version == 5){ return persist__chunk_header_read_v56(db_fptr, chunk, length); }else{ return persist__chunk_header_read_v234(db_fptr, chunk, length); } } int persist__restore(void) { FILE *fptr; char header[15]; int rc = 0; uint32_t crc; uint32_t i32temp; uint32_t chunk, length; size_t rlen; char *err; struct mosquitto_msg_store_load *load, *load_tmp; struct PF_cfg cfg_chunk; assert(db.config); if(!db.config->persistence || db.config->persistence_filepath == NULL){ return MOSQ_ERR_SUCCESS; } db.msg_store_load = NULL; fptr = mosquitto__fopen(db.config->persistence_filepath, "rb", true); if(fptr == NULL) return MOSQ_ERR_SUCCESS; rlen = fread(&header, 1, 15, fptr); if(rlen == 0){ fclose(fptr); log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Persistence file is empty."); return 0; }else if(rlen != 15){ goto error; } if(!memcmp(header, magic, 15)){ /* Restore DB as normal */ read_e(fptr, &crc, sizeof(uint32_t)); read_e(fptr, &i32temp, sizeof(uint32_t)); db_version = ntohl(i32temp); /* IMPORTANT - this is where compatibility checks are made. * Is your DB change still compatible with previous versions? */ if(db_version != MOSQ_DB_VERSION){ if(db_version == 5){ /* Addition of username and listener_port to client chunk in v6 */ }else if(db_version == 4){ }else if(db_version == 3){ /* Addition of source_username and source_port to msg_store chunk in v4, v1.5.6 */ }else if(db_version == 2){ /* Addition of disconnect_t to client chunk in v3. */ }else{ fclose(fptr); log__printf(NULL, MOSQ_LOG_ERR, "Error: Unsupported persistent database format version %d (need version %d).", db_version, MOSQ_DB_VERSION); return 1; } } while(persist__chunk_header_read(fptr, &chunk, &length) == MOSQ_ERR_SUCCESS){ switch(chunk){ case DB_CHUNK_CFG: if(db_version == 6 || db_version == 5){ if(persist__chunk_cfg_read_v56(fptr, &cfg_chunk)){ fclose(fptr); return 1; } }else{ if(persist__chunk_cfg_read_v234(fptr, &cfg_chunk)){ fclose(fptr); return 1; } } if(cfg_chunk.dbid_size != sizeof(dbid_t)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Incompatible database configuration (dbid size is %d bytes, expected %lu)", cfg_chunk.dbid_size, (unsigned long)sizeof(dbid_t)); fclose(fptr); return 1; } db.last_db_id = cfg_chunk.last_db_id; break; case DB_CHUNK_MSG_STORE: if(persist__msg_store_chunk_restore(fptr, length)){ fclose(fptr); return 1; } break; case DB_CHUNK_CLIENT_MSG: if(persist__client_msg_chunk_restore(fptr, length)){ fclose(fptr); return 1; } break; case DB_CHUNK_RETAIN: if(persist__retain_chunk_restore(fptr)){ fclose(fptr); return 1; } break; case DB_CHUNK_SUB: if(persist__sub_chunk_restore(fptr)){ fclose(fptr); return 1; } break; case DB_CHUNK_CLIENT: if(persist__client_chunk_restore(fptr)){ fclose(fptr); return 1; } break; default: log__printf(NULL, MOSQ_LOG_WARNING, "Warning: Unsupported chunk \"%d\" in persistent database file. Ignoring.", chunk); fseek(fptr, length, SEEK_CUR); break; } } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to restore persistent database. Unrecognised file format."); rc = 1; } fclose(fptr); HASH_ITER(hh, db.msg_store_load, load, load_tmp){ HASH_DELETE(hh, db.msg_store_load, load); mosquitto__free(load); } return rc; error: err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); if(fptr) fclose(fptr); return 1; } static int persist__restore_sub(const char *client_id, const char *sub, uint8_t qos, uint32_t identifier, int options) { struct mosquitto *context; assert(client_id); assert(sub); context = persist__find_or_add_context(client_id, 0); if(!context) return 1; return sub__add(context, sub, qos, identifier, options, &db.subs); } #endif mosquitto-2.0.18/src/persist_read_v234.c000066400000000000000000000130761450213760600200560ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WITH_PERSISTENCE #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "persist.h" #include "time_mosq.h" #include "util_mosq.h" int persist__chunk_header_read_v234(FILE *db_fptr, uint32_t *chunk, uint32_t *length) { size_t rlen; uint16_t i16temp; uint32_t i32temp; rlen = fread(&i16temp, sizeof(uint16_t), 1, db_fptr); if(rlen != 1) return 1; rlen = fread(&i32temp, sizeof(uint32_t), 1, db_fptr); if(rlen != 1) return 1; *chunk = ntohs(i16temp); *length = ntohl(i32temp); return MOSQ_ERR_SUCCESS; } int persist__chunk_cfg_read_v234(FILE *db_fptr, struct PF_cfg *chunk) { read_e(db_fptr, &chunk->shutdown, sizeof(uint8_t)); /* shutdown */ read_e(db_fptr, &chunk->dbid_size, sizeof(uint8_t)); /* sizeof(dbid_t) */ read_e(db_fptr, &chunk->last_db_id, sizeof(dbid_t)); return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } int persist__chunk_client_read_v234(FILE *db_fptr, struct P_client *chunk, uint32_t db_version) { uint16_t i16temp; int rc; time_t temp; rc = persist__read_string(db_fptr, &chunk->client_id); if(rc){ return rc; } read_e(db_fptr, &i16temp, sizeof(uint16_t)); chunk->F.last_mid = ntohs(i16temp); if(db_version != 2){ read_e(db_fptr, &temp, sizeof(time_t)); } return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); mosquitto__free(chunk->client_id); return 1; } int persist__chunk_client_msg_read_v234(FILE *db_fptr, struct P_client_msg *chunk) { uint16_t i16temp; int rc; char *err; uint8_t retain, dup; rc = persist__read_string(db_fptr, &chunk->client_id); if(rc){ return rc; } read_e(db_fptr, &chunk->F.store_id, sizeof(dbid_t)); read_e(db_fptr, &i16temp, sizeof(uint16_t)); chunk->F.mid = ntohs(i16temp); read_e(db_fptr, &chunk->F.qos, sizeof(uint8_t)); read_e(db_fptr, &retain, sizeof(uint8_t)); read_e(db_fptr, &chunk->F.direction, sizeof(uint8_t)); read_e(db_fptr, &chunk->F.state, sizeof(uint8_t)); read_e(db_fptr, &dup, sizeof(uint8_t)); chunk->F.retain_dup = (uint8_t)((retain&0x0F)<<4 | (dup&0x0F)); return MOSQ_ERR_SUCCESS; error: err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); mosquitto__free(chunk->client_id); return 1; } int persist__chunk_msg_store_read_v234(FILE *db_fptr, struct P_msg_store *chunk, uint32_t db_version) { uint32_t i32temp; uint16_t i16temp; int rc = 0; char *err; read_e(db_fptr, &chunk->F.store_id, sizeof(dbid_t)); rc = persist__read_string(db_fptr, &chunk->source.id); if(rc){ return rc; } if(db_version == 4){ rc = persist__read_string(db_fptr, &chunk->source.username); if(rc){ mosquitto__free(chunk->source.id); return rc; } read_e(db_fptr, &i16temp, sizeof(uint16_t)); chunk->F.source_port = ntohs(i16temp); } read_e(db_fptr, &i16temp, sizeof(uint16_t)); chunk->F.source_mid = ntohs(i16temp); /* This is the mid - don't need it */ read_e(db_fptr, &i16temp, sizeof(uint16_t)); rc = persist__read_string(db_fptr, &chunk->topic); if(rc){ mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); return rc; } read_e(db_fptr, &chunk->F.qos, sizeof(uint8_t)); read_e(db_fptr, &chunk->F.retain, sizeof(uint8_t)); read_e(db_fptr, &i32temp, sizeof(uint32_t)); chunk->F.payloadlen = ntohl(i32temp); if(chunk->F.payloadlen){ chunk->payload = mosquitto_malloc(chunk->F.payloadlen+1); if(chunk->payload == NULL){ mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); mosquitto__free(chunk->topic); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } read_e(db_fptr, chunk->payload, chunk->F.payloadlen); /* Ensure zero terminated regardless of contents */ ((uint8_t *)chunk->payload)[chunk->F.payloadlen] = 0; } return MOSQ_ERR_SUCCESS; error: err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); return 1; } int persist__chunk_retain_read_v234(FILE *db_fptr, struct P_retain *chunk) { dbid_t i64temp; char *err; if(fread(&i64temp, sizeof(dbid_t), 1, db_fptr) != 1){ err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); return 1; } chunk->F.store_id = i64temp; return MOSQ_ERR_SUCCESS; } int persist__chunk_sub_read_v234(FILE *db_fptr, struct P_sub *chunk) { int rc; char *err; rc = persist__read_string(db_fptr, &chunk->client_id); if(rc){ return rc; } rc = persist__read_string(db_fptr, &chunk->topic); if(rc){ mosquitto__free(chunk->client_id); return rc; } read_e(db_fptr, &chunk->F.qos, sizeof(uint8_t)); return MOSQ_ERR_SUCCESS; error: err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); mosquitto__free(chunk->client_id); mosquitto__free(chunk->topic); return 1; } #endif mosquitto-2.0.18/src/persist_read_v5.c000066400000000000000000000165521450213760600177140ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WITH_PERSISTENCE #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "persist.h" #include "property_mosq.h" #include "time_mosq.h" #include "util_mosq.h" int persist__chunk_header_read_v56(FILE *db_fptr, uint32_t *chunk, uint32_t *length) { size_t rlen; struct PF_header header; rlen = fread(&header, sizeof(struct PF_header), 1, db_fptr); if(rlen != 1) return 1; *chunk = ntohl(header.chunk); *length = ntohl(header.length); return MOSQ_ERR_SUCCESS; } int persist__chunk_cfg_read_v56(FILE *db_fptr, struct PF_cfg *chunk) { if(fread(chunk, sizeof(struct PF_cfg), 1, db_fptr) != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } return MOSQ_ERR_SUCCESS; } int persist__chunk_client_read_v56(FILE *db_fptr, struct P_client *chunk, uint32_t db_version) { int rc; if(db_version == 6){ read_e(db_fptr, &chunk->F, sizeof(struct PF_client)); chunk->F.username_len = ntohs(chunk->F.username_len); chunk->F.listener_port = ntohs(chunk->F.listener_port); }else if(db_version == 5){ read_e(db_fptr, &chunk->F, sizeof(struct PF_client_v5)); }else{ return 1; } chunk->F.session_expiry_interval = ntohl(chunk->F.session_expiry_interval); chunk->F.last_mid = ntohs(chunk->F.last_mid); chunk->F.id_len = ntohs(chunk->F.id_len); rc = persist__read_string_len(db_fptr, &chunk->client_id, chunk->F.id_len); if(rc){ return 1; }else if(chunk->client_id == NULL){ return -1; } if(chunk->F.username_len > 0){ rc = persist__read_string_len(db_fptr, &chunk->username, chunk->F.username_len); if(rc || !chunk->username){ mosquitto__free(chunk->client_id); return 1; } } return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } int persist__chunk_client_msg_read_v56(FILE *db_fptr, struct P_client_msg *chunk, uint32_t length) { mosquitto_property *properties = NULL; struct mosquitto__packet prop_packet; int rc; read_e(db_fptr, &chunk->F, sizeof(struct PF_client_msg)); chunk->F.mid = ntohs(chunk->F.mid); chunk->F.id_len = ntohs(chunk->F.id_len); length -= (uint32_t)(sizeof(struct PF_client_msg) + chunk->F.id_len); rc = persist__read_string_len(db_fptr, &chunk->client_id, chunk->F.id_len); if(rc){ return rc; } if(length > 0){ memset(&prop_packet, 0, sizeof(struct mosquitto__packet)); prop_packet.remaining_length = length; prop_packet.payload = mosquitto__malloc(length); if(!prop_packet.payload){ return MOSQ_ERR_NOMEM; } read_e(db_fptr, prop_packet.payload, length); rc = property__read_all(CMD_PUBLISH, &prop_packet, &properties); mosquitto__free(prop_packet.payload); if(rc){ return rc; } } chunk->properties = properties; return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } int persist__chunk_msg_store_read_v56(FILE *db_fptr, struct P_msg_store *chunk, uint32_t length) { int rc = 0; mosquitto_property *properties = NULL; struct mosquitto__packet prop_packet; memset(&prop_packet, 0, sizeof(struct mosquitto__packet)); read_e(db_fptr, &chunk->F, sizeof(struct PF_msg_store)); chunk->F.payloadlen = ntohl(chunk->F.payloadlen); if(chunk->F.payloadlen > MQTT_MAX_PAYLOAD){ return MOSQ_ERR_INVAL; } chunk->F.source_mid = ntohs(chunk->F.source_mid); chunk->F.source_id_len = ntohs(chunk->F.source_id_len); chunk->F.source_username_len = ntohs(chunk->F.source_username_len); chunk->F.topic_len = ntohs(chunk->F.topic_len); chunk->F.source_port = ntohs(chunk->F.source_port); length -= (uint32_t)(sizeof(struct PF_msg_store) + chunk->F.payloadlen + chunk->F.source_id_len + chunk->F.source_username_len + chunk->F.topic_len); if(chunk->F.source_id_len){ rc = persist__read_string_len(db_fptr, &chunk->source.id, chunk->F.source_id_len); if(rc){ return rc; } } if(chunk->F.source_username_len){ rc = persist__read_string_len(db_fptr, &chunk->source.username, chunk->F.source_username_len); if(rc){ mosquitto__free(chunk->source.id); chunk->source.id = NULL; return rc; } } rc = persist__read_string_len(db_fptr, &chunk->topic, chunk->F.topic_len); if(rc){ mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); chunk->source.id = NULL; chunk->source.username = NULL; return rc; } if(chunk->F.payloadlen > 0){ chunk->payload = mosquitto__malloc(chunk->F.payloadlen+1); if(chunk->payload == NULL){ mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); mosquitto__free(chunk->topic); chunk->source.id = NULL; chunk->source.username = NULL; chunk->topic = NULL; log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } read_e(db_fptr, chunk->payload, chunk->F.payloadlen); /* Ensure zero terminated regardless of contents */ ((uint8_t *)chunk->payload)[chunk->F.payloadlen] = 0; } if(length > 0){ prop_packet.remaining_length = length; prop_packet.payload = mosquitto__malloc(length); if(!prop_packet.payload){ mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); mosquitto__free(chunk->topic); return MOSQ_ERR_NOMEM; } read_e(db_fptr, prop_packet.payload, length); rc = property__read_all(CMD_PUBLISH, &prop_packet, &properties); mosquitto__free(prop_packet.payload); if(rc){ mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); mosquitto__free(chunk->topic); return rc; } } chunk->properties = properties; return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); mosquitto__free(chunk->source.id); mosquitto__free(chunk->source.username); mosquitto__free(chunk->topic); mosquitto__free(prop_packet.payload); return 1; } int persist__chunk_retain_read_v56(FILE *db_fptr, struct P_retain *chunk) { if(fread(&chunk->F, sizeof(struct P_retain), 1, db_fptr) != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } return MOSQ_ERR_SUCCESS; } int persist__chunk_sub_read_v56(FILE *db_fptr, struct P_sub *chunk) { int rc; read_e(db_fptr, &chunk->F, sizeof(struct PF_sub)); chunk->F.identifier = ntohl(chunk->F.identifier); chunk->F.id_len = ntohs(chunk->F.id_len); chunk->F.topic_len = ntohs(chunk->F.topic_len); rc = persist__read_string_len(db_fptr, &chunk->client_id, chunk->F.id_len); if(rc){ return rc; } rc = persist__read_string_len(db_fptr, &chunk->topic, chunk->F.topic_len); if(rc){ mosquitto__free(chunk->client_id); chunk->client_id = NULL; return rc; } return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } #endif mosquitto-2.0.18/src/persist_write.c000066400000000000000000000277411450213760600175230ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WITH_PERSISTENCE #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "persist.h" #include "time_mosq.h" #include "misc_mosq.h" #include "util_mosq.h" static int persist__client_messages_save(FILE *db_fptr, struct mosquitto *context, struct mosquitto_client_msg *queue) { struct P_client_msg chunk; struct mosquitto_client_msg *cmsg; int rc; assert(db_fptr); assert(context); cmsg = queue; while(cmsg){ if(!strncmp(cmsg->store->topic, "$SYS", 4) && cmsg->store->ref_count <= 1 && cmsg->store->dest_id_count == 0){ /* This $SYS message won't have been persisted, so we can't persist * this client message. */ cmsg = cmsg->next; continue; } memset(&chunk, 0, sizeof(struct P_client_msg)); chunk.F.store_id = cmsg->store->db_id; chunk.F.mid = cmsg->mid; chunk.F.id_len = (uint16_t)strlen(context->id); chunk.F.qos = cmsg->qos; chunk.F.retain_dup = (uint8_t)((cmsg->retain&0x0F)<<4 | (cmsg->dup&0x0F)); chunk.F.direction = (uint8_t)cmsg->direction; chunk.F.state = (uint8_t)cmsg->state; chunk.client_id = context->id; chunk.properties = cmsg->properties; rc = persist__chunk_client_msg_write_v6(db_fptr, &chunk); if(rc){ return rc; } cmsg = cmsg->next; } return MOSQ_ERR_SUCCESS; } static int persist__message_store_save(FILE *db_fptr) { struct P_msg_store chunk; struct mosquitto_msg_store *stored; int rc; assert(db_fptr); stored = db.msg_store; while(stored){ if(stored->ref_count < 1 || stored->topic == NULL){ stored = stored->next; continue; } memset(&chunk, 0, sizeof(struct P_msg_store)); if(!strncmp(stored->topic, "$SYS", 4)){ if(stored->ref_count <= 1 && stored->dest_id_count == 0){ /* $SYS messages that are only retained shouldn't be persisted. */ stored = stored->next; continue; } /* Don't save $SYS messages as retained otherwise they can give * misleading information when reloaded. They should still be saved * because a disconnected durable client may have them in their * queue. */ chunk.F.retain = 0; }else{ chunk.F.retain = (uint8_t)stored->retain; } chunk.F.store_id = stored->db_id; chunk.F.expiry_time = stored->message_expiry_time; chunk.F.payloadlen = stored->payloadlen; chunk.F.source_mid = stored->source_mid; if(stored->source_id){ chunk.F.source_id_len = (uint16_t)strlen(stored->source_id); chunk.source.id = stored->source_id; }else{ chunk.F.source_id_len = 0; chunk.source.id = NULL; } if(stored->source_username){ chunk.F.source_username_len = (uint16_t)strlen(stored->source_username); chunk.source.username = stored->source_username; }else{ chunk.F.source_username_len = 0; chunk.source.username = NULL; } chunk.F.topic_len = (uint16_t)strlen(stored->topic); chunk.topic = stored->topic; if(stored->source_listener){ chunk.F.source_port = stored->source_listener->port; }else{ chunk.F.source_port = 0; } chunk.F.qos = stored->qos; chunk.payload = stored->payload; chunk.properties = stored->properties; rc = persist__chunk_message_store_write_v6(db_fptr, &chunk); if(rc){ return rc; } stored = stored->next; } return MOSQ_ERR_SUCCESS; } static int persist__client_save(FILE *db_fptr) { struct mosquitto *context, *ctxt_tmp; struct P_client chunk; int rc; assert(db_fptr); HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){ memset(&chunk, 0, sizeof(struct P_client)); if(context && #ifdef WITH_BRIDGE ((!context->bridge && context->clean_start == false) || (context->bridge && context->bridge->clean_start_local == false)) #else context->clean_start == false #endif ){ chunk.F.session_expiry_time = context->session_expiry_time; if(context->session_expiry_interval != 0 && context->session_expiry_interval != UINT32_MAX && context->session_expiry_time == 0){ chunk.F.session_expiry_time = context->session_expiry_interval + db.now_real_s; }else{ chunk.F.session_expiry_time = context->session_expiry_time; } chunk.F.session_expiry_interval = context->session_expiry_interval; chunk.F.last_mid = context->last_mid; chunk.F.id_len = (uint16_t)strlen(context->id); chunk.client_id = context->id; if(context->username){ chunk.F.username_len = (uint16_t)strlen(context->username); chunk.username = context->username; } if(context->listener){ chunk.F.listener_port = context->listener->port; } if(chunk.F.id_len == 0){ /* This should never happen, but in case we have a client with * zero length ID, don't persist them. */ continue; } rc = persist__chunk_client_write_v6(db_fptr, &chunk); if(rc){ return rc; } if(persist__client_messages_save(db_fptr, context, context->msgs_in.inflight)) return 1; if(persist__client_messages_save(db_fptr, context, context->msgs_in.queued)) return 1; if(persist__client_messages_save(db_fptr, context, context->msgs_out.inflight)) return 1; if(persist__client_messages_save(db_fptr, context, context->msgs_out.queued)) return 1; } } return MOSQ_ERR_SUCCESS; } static int persist__subs_save(FILE *db_fptr, struct mosquitto__subhier *node, const char *topic, int level) { struct mosquitto__subhier *subhier, *subhier_tmp; struct mosquitto__subleaf *sub; struct P_sub sub_chunk; char *thistopic; size_t slen; int rc; slen = strlen(topic) + node->topic_len + 2; thistopic = mosquitto__malloc(sizeof(char)*slen); if(!thistopic) return MOSQ_ERR_NOMEM; if(level > 1 || strlen(topic)){ snprintf(thistopic, slen, "%s/%s", topic, node->topic); }else{ snprintf(thistopic, slen, "%s", node->topic); } sub = node->subs; while(sub){ if(sub->context->clean_start == false && sub->context->id){ memset(&sub_chunk, 0, sizeof(struct P_sub)); sub_chunk.F.identifier = sub->identifier; sub_chunk.F.id_len = (uint16_t)strlen(sub->context->id); sub_chunk.F.topic_len = (uint16_t)strlen(thistopic); sub_chunk.F.qos = (uint8_t)sub->qos; sub_chunk.F.options = (uint8_t)(sub->no_local<<2 | sub->retain_as_published<<3); sub_chunk.client_id = sub->context->id; sub_chunk.topic = thistopic; rc = persist__chunk_sub_write_v6(db_fptr, &sub_chunk); if(rc){ mosquitto__free(thistopic); return rc; } } sub = sub->next; } HASH_ITER(hh, node->children, subhier, subhier_tmp){ persist__subs_save(db_fptr, subhier, thistopic, level+1); } mosquitto__free(thistopic); return MOSQ_ERR_SUCCESS; } static int persist__subs_save_all(FILE *db_fptr) { struct mosquitto__subhier *subhier, *subhier_tmp; HASH_ITER(hh, db.subs, subhier, subhier_tmp){ if(subhier->children){ persist__subs_save(db_fptr, subhier->children, "", 0); } } return MOSQ_ERR_SUCCESS; } static int persist__retain_save(FILE *db_fptr, struct mosquitto__retainhier *node, int level) { struct mosquitto__retainhier *retainhier, *retainhier_tmp; struct P_retain retain_chunk; int rc; if(node->retained && strncmp(node->retained->topic, "$SYS", 4)){ /* Don't save $SYS messages. */ memset(&retain_chunk, 0, sizeof(struct P_retain)); retain_chunk.F.store_id = node->retained->db_id; rc = persist__chunk_retain_write_v6(db_fptr, &retain_chunk); if(rc){ return rc; } } HASH_ITER(hh, node->children, retainhier, retainhier_tmp){ persist__retain_save(db_fptr, retainhier, level+1); } return MOSQ_ERR_SUCCESS; } static int persist__retain_save_all(FILE *db_fptr) { struct mosquitto__retainhier *retainhier, *retainhier_tmp; HASH_ITER(hh, db.retains, retainhier, retainhier_tmp){ if(retainhier->children){ persist__retain_save(db_fptr, retainhier->children, 0); } } return MOSQ_ERR_SUCCESS; } int persist__backup(bool shutdown) { int rc = 0; FILE *db_fptr = NULL; uint32_t db_version_w = htonl(MOSQ_DB_VERSION); uint32_t crc = 0; char *err; char *outfile = NULL; size_t len; struct PF_cfg cfg_chunk; if(db.config == NULL) return MOSQ_ERR_INVAL; if(db.config->persistence == false) return MOSQ_ERR_SUCCESS; if(db.config->persistence_filepath == NULL) return MOSQ_ERR_INVAL; log__printf(NULL, MOSQ_LOG_INFO, "Saving in-memory database to %s.", db.config->persistence_filepath); len = strlen(db.config->persistence_filepath)+5; outfile = mosquitto__malloc(len+1); if(!outfile){ log__printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, out of memory."); return MOSQ_ERR_NOMEM; } snprintf(outfile, len, "%s.new", db.config->persistence_filepath); outfile[len] = '\0'; #ifndef WIN32 /** * * If a system lost power during the rename operation at the * end of this file the filesystem could potentially be left * with a directory that looks like this after powerup: * * 24094 -rw-r--r-- 2 root root 4099 May 30 16:27 mosquitto.db * 24094 -rw-r--r-- 2 root root 4099 May 30 16:27 mosquitto.db.new * * The 24094 shows that mosquitto.db.new is hard-linked to the * same file as mosquitto.db. If fopen(outfile, "wb") is naively * called then mosquitto.db will be truncated and the database * potentially corrupted. * * Any existing mosquitto.db.new file must be removed prior to * opening to guarantee that it is not hard-linked to * mosquitto.db. * */ rc = unlink(outfile); if (rc != 0) { rc = 0; if (errno != ENOENT) { log__printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, unable to remove %s.", outfile); goto error; } } #endif db_fptr = mosquitto__fopen(outfile, "wb", true); if(db_fptr == NULL){ log__printf(NULL, MOSQ_LOG_INFO, "Error saving in-memory database, unable to open %s for writing.", outfile); goto error; } /* Header */ write_e(db_fptr, magic, 15); write_e(db_fptr, &crc, sizeof(uint32_t)); write_e(db_fptr, &db_version_w, sizeof(uint32_t)); memset(&cfg_chunk, 0, sizeof(struct PF_cfg)); cfg_chunk.last_db_id = db.last_db_id; cfg_chunk.shutdown = shutdown; cfg_chunk.dbid_size = sizeof(dbid_t); if(persist__chunk_cfg_write_v6(db_fptr, &cfg_chunk)){ goto error; } if(persist__message_store_save(db_fptr)){ goto error; } persist__client_save(db_fptr); persist__subs_save_all(db_fptr); persist__retain_save_all(db_fptr); #ifndef WIN32 /** * * Closing a file does not guarantee that the contents are * written to disk. Need to flush to send data from app to OS * buffers, then fsync to deliver data from OS buffers to disk * (as well as disk hardware permits). * * man close (http://linux.die.net/man/2/close, 2016-06-20): * * "successful close does not guarantee that the data has * been successfully saved to disk, as the kernel defers * writes. It is not common for a filesystem to flush * the buffers when the stream is closed. If you need * to be sure that the data is physically stored, use * fsync(2). (It will depend on the disk hardware at this * point." * * This guarantees that the new state file will not overwrite * the old state file before its contents are valid. * */ fflush(db_fptr); fsync(fileno(db_fptr)); #endif fclose(db_fptr); #ifdef WIN32 if(remove(db.config->persistence_filepath) != 0){ if(errno != ENOENT){ goto error; } } #endif if(rename(outfile, db.config->persistence_filepath) != 0){ goto error; } mosquitto__free(outfile); outfile = NULL; return rc; error: mosquitto__free(outfile); err = strerror(errno); log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", err); if(db_fptr) fclose(db_fptr); return 1; } #endif mosquitto-2.0.18/src/persist_write_v5.c000066400000000000000000000154331450213760600201300ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #ifdef WITH_PERSISTENCE #ifndef WIN32 #include #endif #include #include #include #include #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "persist.h" #include "packet_mosq.h" #include "property_mosq.h" #include "time_mosq.h" #include "util_mosq.h" int persist__chunk_cfg_write_v6(FILE *db_fptr, struct PF_cfg *chunk) { struct PF_header header; header.chunk = htonl(DB_CHUNK_CFG); header.length = htonl(sizeof(struct PF_cfg)); write_e(db_fptr, &header, sizeof(struct PF_header)); write_e(db_fptr, chunk, sizeof(struct PF_cfg)); return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } int persist__chunk_client_write_v6(FILE *db_fptr, struct P_client *chunk) { struct PF_header header; uint16_t id_len = chunk->F.id_len; uint16_t username_len = chunk->F.username_len; chunk->F.session_expiry_interval = htonl(chunk->F.session_expiry_interval); chunk->F.last_mid = htons(chunk->F.last_mid); chunk->F.id_len = htons(chunk->F.id_len); chunk->F.username_len = htons(chunk->F.username_len); chunk->F.listener_port = htons(chunk->F.listener_port); header.chunk = htonl(DB_CHUNK_CLIENT); header.length = htonl((uint32_t)sizeof(struct PF_client)+id_len+username_len); write_e(db_fptr, &header, sizeof(struct PF_header)); write_e(db_fptr, &chunk->F, sizeof(struct PF_client)); write_e(db_fptr, chunk->client_id, id_len); if(username_len > 0){ write_e(db_fptr, chunk->username, username_len); } return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } int persist__chunk_client_msg_write_v6(FILE *db_fptr, struct P_client_msg *chunk) { struct PF_header header; struct mosquitto__packet prop_packet; uint16_t id_len = chunk->F.id_len; uint32_t proplen = 0; int rc; memset(&prop_packet, 0, sizeof(struct mosquitto__packet)); if(chunk->properties){ proplen += property__get_remaining_length(chunk->properties); } chunk->F.mid = htons(chunk->F.mid); chunk->F.id_len = htons(chunk->F.id_len); header.chunk = htonl(DB_CHUNK_CLIENT_MSG); header.length = htonl((uint32_t)sizeof(struct PF_client_msg) + id_len + proplen); write_e(db_fptr, &header, sizeof(struct PF_header)); write_e(db_fptr, &chunk->F, sizeof(struct PF_client_msg)); write_e(db_fptr, chunk->client_id, id_len); if(chunk->properties){ if(proplen > 0){ prop_packet.remaining_length = proplen; prop_packet.packet_length = proplen; prop_packet.payload = mosquitto__malloc(proplen); if(!prop_packet.payload){ return MOSQ_ERR_NOMEM; } rc = property__write_all(&prop_packet, chunk->properties, true); if(rc){ mosquitto__free(prop_packet.payload); return rc; } write_e(db_fptr, prop_packet.payload, proplen); mosquitto__free(prop_packet.payload); } } return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } int persist__chunk_message_store_write_v6(FILE *db_fptr, struct P_msg_store *chunk) { struct PF_header header; uint32_t payloadlen = chunk->F.payloadlen; uint16_t source_id_len = chunk->F.source_id_len; uint16_t source_username_len = chunk->F.source_username_len; uint16_t topic_len = chunk->F.topic_len; uint32_t proplen = 0; struct mosquitto__packet prop_packet; int rc; memset(&prop_packet, 0, sizeof(struct mosquitto__packet)); if(chunk->properties){ proplen += property__get_remaining_length(chunk->properties); } chunk->F.payloadlen = htonl(chunk->F.payloadlen); chunk->F.source_mid = htons(chunk->F.source_mid); chunk->F.source_id_len = htons(chunk->F.source_id_len); chunk->F.source_username_len = htons(chunk->F.source_username_len); chunk->F.topic_len = htons(chunk->F.topic_len); chunk->F.source_port = htons(chunk->F.source_port); header.chunk = htonl(DB_CHUNK_MSG_STORE); header.length = htonl((uint32_t)sizeof(struct PF_msg_store) + topic_len + payloadlen + source_id_len + source_username_len + proplen); write_e(db_fptr, &header, sizeof(struct PF_header)); write_e(db_fptr, &chunk->F, sizeof(struct PF_msg_store)); if(source_id_len){ write_e(db_fptr, chunk->source.id, source_id_len); } if(source_username_len){ write_e(db_fptr, chunk->source.username, source_username_len); } write_e(db_fptr, chunk->topic, topic_len); if(payloadlen){ write_e(db_fptr, chunk->payload, (unsigned int)payloadlen); } if(chunk->properties){ if(proplen > 0){ prop_packet.remaining_length = proplen; prop_packet.packet_length = proplen; prop_packet.payload = mosquitto__malloc(proplen); if(!prop_packet.payload){ return MOSQ_ERR_NOMEM; } rc = property__write_all(&prop_packet, chunk->properties, true); if(rc){ mosquitto__free(prop_packet.payload); return rc; } write_e(db_fptr, prop_packet.payload, proplen); mosquitto__free(prop_packet.payload); } } return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); mosquitto__free(prop_packet.payload); return 1; } int persist__chunk_retain_write_v6(FILE *db_fptr, struct P_retain *chunk) { struct PF_header header; header.chunk = htonl(DB_CHUNK_RETAIN); header.length = htonl((uint32_t)sizeof(struct PF_retain)); write_e(db_fptr, &header, sizeof(struct PF_header)); write_e(db_fptr, &chunk->F, sizeof(struct PF_retain)); return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } int persist__chunk_sub_write_v6(FILE *db_fptr, struct P_sub *chunk) { struct PF_header header; uint16_t id_len = chunk->F.id_len; uint16_t topic_len = chunk->F.topic_len; chunk->F.identifier = htonl(chunk->F.identifier); chunk->F.id_len = htons(chunk->F.id_len); chunk->F.topic_len = htons(chunk->F.topic_len); header.chunk = htonl(DB_CHUNK_SUB); header.length = htonl((uint32_t)sizeof(struct PF_sub) + id_len + topic_len); write_e(db_fptr, &header, sizeof(struct PF_header)); write_e(db_fptr, &chunk->F, sizeof(struct PF_sub)); write_e(db_fptr, chunk->client_id, id_len); write_e(db_fptr, chunk->topic, topic_len); return MOSQ_ERR_SUCCESS; error: log__printf(NULL, MOSQ_LOG_ERR, "Error: %s.", strerror(errno)); return 1; } #endif mosquitto-2.0.18/src/plugin.c000066400000000000000000000215741450213760600161140ustar00rootroot00000000000000/* Copyright (c) 2016-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto_broker_internal.h" #include "mosquitto_internal.h" #include "mosquitto_broker.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "send_mosq.h" #include "util_mosq.h" #include "utlist.h" #include "lib_load.h" static bool check_callback_exists(struct mosquitto__callback *cb_base, MOSQ_FUNC_generic_callback cb_func) { struct mosquitto__callback *tail, *tmp; DL_FOREACH_SAFE(cb_base, tail, tmp){ if(tail->cb == cb_func){ return true; } } return false; } static int remove_callback(struct mosquitto__callback **cb_base, MOSQ_FUNC_generic_callback cb_func) { struct mosquitto__callback *tail, *tmp; DL_FOREACH_SAFE(*cb_base, tail, tmp){ if(tail->cb == cb_func){ DL_DELETE(*cb_base, tail); mosquitto__free(tail); return MOSQ_ERR_SUCCESS; } } return MOSQ_ERR_NOT_FOUND; } int plugin__load_v5(struct mosquitto__listener *listener, struct mosquitto__auth_plugin *plugin, struct mosquitto_opt *options, int option_count, void *lib) { int rc; mosquitto_plugin_id_t *pid; if(!(plugin->plugin_init_v5 = (FUNC_plugin_init_v5)LIB_SYM(lib, "mosquitto_plugin_init"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load plugin function mosquitto_plugin_init()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->plugin_cleanup_v5 = (FUNC_plugin_cleanup_v5)LIB_SYM(lib, "mosquitto_plugin_cleanup"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load plugin function mosquitto_plugin_cleanup()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } pid = mosquitto__calloc(1, sizeof(mosquitto_plugin_id_t)); if(pid == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); LIB_CLOSE(lib); return MOSQ_ERR_NOMEM; } pid->listener = listener; plugin->lib = lib; plugin->user_data = NULL; plugin->identifier = pid; if(plugin->plugin_init_v5){ rc = plugin->plugin_init_v5(pid, &plugin->user_data, options, option_count); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Plugin returned %d when initialising.", rc); return rc; } } return 0; } void plugin__handle_disconnect(struct mosquitto *context, int reason) { struct mosquitto_evt_disconnect event_data; struct mosquitto__callback *cb_base; struct mosquitto__security_options *opts; if(db.config->per_listener_settings){ if(context->listener == NULL){ return; } opts = &context->listener->security_options; }else{ opts = &db.config->security_options; memset(&event_data, 0, sizeof(event_data)); } event_data.client = context; event_data.reason = reason; DL_FOREACH(opts->plugin_callbacks.disconnect, cb_base){ cb_base->cb(MOSQ_EVT_DISCONNECT, &event_data, cb_base->userdata); } } int plugin__handle_message(struct mosquitto *context, struct mosquitto_msg_store *stored) { struct mosquitto_evt_message event_data; struct mosquitto__callback *cb_base; struct mosquitto__security_options *opts; int rc = MOSQ_ERR_SUCCESS; if(db.config->per_listener_settings){ if(context->listener == NULL){ return MOSQ_ERR_SUCCESS; } opts = &context->listener->security_options; }else{ opts = &db.config->security_options; } if(opts->plugin_callbacks.message == NULL){ return MOSQ_ERR_SUCCESS; } memset(&event_data, 0, sizeof(event_data)); event_data.client = context; event_data.topic = stored->topic; event_data.payloadlen = stored->payloadlen; event_data.payload = stored->payload; event_data.qos = stored->qos; event_data.retain = stored->retain; event_data.properties = stored->properties; DL_FOREACH(opts->plugin_callbacks.message, cb_base){ rc = cb_base->cb(MOSQ_EVT_MESSAGE, &event_data, cb_base->userdata); if(stored->topic != event_data.topic){ mosquitto__free(stored->topic); stored->topic = event_data.topic; } if(stored->payload != event_data.payload){ mosquitto__free(stored->payload); stored->payload = event_data.payload; stored->payloadlen = event_data.payloadlen; } if(stored->properties != event_data.properties){ mosquitto_property_free_all(&stored->properties); stored->properties = event_data.properties; } if(rc != MOSQ_ERR_SUCCESS){ break; } } stored->retain = event_data.retain; return rc; } void plugin__handle_tick(void) { struct mosquitto_evt_tick event_data; struct mosquitto__callback *cb_base; struct mosquitto__security_options *opts; int i; /* FIXME - set now_s and now_ns to avoid need for multiple time lookups */ if(db.config->per_listener_settings){ for(i=0; i < db.config->listener_count; i++){ opts = &db.config->listeners[i].security_options; memset(&event_data, 0, sizeof(event_data)); DL_FOREACH(opts->plugin_callbacks.tick, cb_base){ cb_base->cb(MOSQ_EVT_TICK, &event_data, cb_base->userdata); } } }else{ opts = &db.config->security_options; memset(&event_data, 0, sizeof(event_data)); DL_FOREACH(opts->plugin_callbacks.tick, cb_base){ cb_base->cb(MOSQ_EVT_TICK, &event_data, cb_base->userdata); } } } int mosquitto_callback_register( mosquitto_plugin_id_t *identifier, int event, MOSQ_FUNC_generic_callback cb_func, const void *event_data, void *userdata) { struct mosquitto__callback **cb_base = NULL, *cb_new; struct mosquitto__security_options *security_options; if(cb_func == NULL) return MOSQ_ERR_INVAL; if(identifier->listener == NULL){ security_options = &db.config->security_options; }else{ security_options = &identifier->listener->security_options; } switch(event){ case MOSQ_EVT_RELOAD: cb_base = &security_options->plugin_callbacks.reload; break; case MOSQ_EVT_ACL_CHECK: cb_base = &security_options->plugin_callbacks.acl_check; break; case MOSQ_EVT_BASIC_AUTH: cb_base = &security_options->plugin_callbacks.basic_auth; break; case MOSQ_EVT_PSK_KEY: cb_base = &security_options->plugin_callbacks.psk_key; break; case MOSQ_EVT_EXT_AUTH_START: cb_base = &security_options->plugin_callbacks.ext_auth_start; break; case MOSQ_EVT_EXT_AUTH_CONTINUE: cb_base = &security_options->plugin_callbacks.ext_auth_continue; break; case MOSQ_EVT_CONTROL: return control__register_callback(security_options, cb_func, event_data, userdata); break; case MOSQ_EVT_MESSAGE: cb_base = &security_options->plugin_callbacks.message; break; case MOSQ_EVT_TICK: cb_base = &security_options->plugin_callbacks.tick; break; case MOSQ_EVT_DISCONNECT: cb_base = &security_options->plugin_callbacks.disconnect; break; default: return MOSQ_ERR_NOT_SUPPORTED; break; } if(check_callback_exists(*cb_base, cb_func)){ return MOSQ_ERR_ALREADY_EXISTS; } cb_new = mosquitto__calloc(1, sizeof(struct mosquitto__callback)); if(cb_new == NULL){ return MOSQ_ERR_NOMEM; } DL_APPEND(*cb_base, cb_new); cb_new->cb = cb_func; cb_new->userdata = userdata; return MOSQ_ERR_SUCCESS; } int mosquitto_callback_unregister( mosquitto_plugin_id_t *identifier, int event, MOSQ_FUNC_generic_callback cb_func, const void *event_data) { struct mosquitto__callback **cb_base = NULL; struct mosquitto__security_options *security_options; if(identifier == NULL || cb_func == NULL){ return MOSQ_ERR_INVAL; } if(identifier->listener == NULL){ security_options = &db.config->security_options; }else{ security_options = &identifier->listener->security_options; } switch(event){ case MOSQ_EVT_RELOAD: cb_base = &security_options->plugin_callbacks.reload; break; case MOSQ_EVT_ACL_CHECK: cb_base = &security_options->plugin_callbacks.acl_check; break; case MOSQ_EVT_BASIC_AUTH: cb_base = &security_options->plugin_callbacks.basic_auth; break; case MOSQ_EVT_PSK_KEY: cb_base = &security_options->plugin_callbacks.psk_key; break; case MOSQ_EVT_EXT_AUTH_START: cb_base = &security_options->plugin_callbacks.ext_auth_start; break; case MOSQ_EVT_EXT_AUTH_CONTINUE: cb_base = &security_options->plugin_callbacks.ext_auth_continue; break; case MOSQ_EVT_CONTROL: return control__unregister_callback(security_options, cb_func, event_data); break; case MOSQ_EVT_MESSAGE: cb_base = &security_options->plugin_callbacks.message; break; case MOSQ_EVT_TICK: cb_base = &security_options->plugin_callbacks.tick; break; case MOSQ_EVT_DISCONNECT: cb_base = &security_options->plugin_callbacks.disconnect; break; default: return MOSQ_ERR_NOT_SUPPORTED; break; } return remove_callback(cb_base, cb_func); } mosquitto-2.0.18/src/plugin_debug.c000066400000000000000000000077401450213760600172610ustar00rootroot00000000000000/* Copyright (c) 2015-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ /* This is a skeleton authentication and access control plugin that print a * message when each check occurs. It allows everything. */ #include #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mosquitto.h" #define ANSI_GREEN "\e[0;32m" #define ANSI_BLUE "\e[0;34m" #define ANSI_MAGENTA "\e[0;35m" #define ANSI_RESET "\e[0m" void print_col(struct mosquitto *client) { switch(mosquitto_client_protocol(client)){ case mp_mqtt: printf("%s", ANSI_GREEN); break; case mp_websockets: printf("%s", ANSI_MAGENTA); break; default: break; } } int mosquitto_auth_plugin_version(void) { printf(ANSI_BLUE "PLUGIN ::: mosquitto_auth_plugin_version()" ANSI_RESET "\n"); return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { printf(ANSI_BLUE "PLUGIN ::: mosquitto_auth_plugin_init(,,%d)" ANSI_RESET "\n", auth_opt_count); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { printf(ANSI_BLUE "PLUGIN ::: mosquitto_auth_plugin_cleanup(,,%d)" ANSI_RESET "\n", auth_opt_count); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { printf(ANSI_BLUE "PLUGIN ::: mosquitto_auth_security_init(,,%d, %d)" ANSI_RESET "\n", auth_opt_count, reload); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { printf(ANSI_BLUE "PLUGIN ::: mosquitto_auth_security_cleanup(,,%d, %d)" ANSI_RESET "\n", auth_opt_count, reload); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { print_col(client); printf("PLUGIN ::: mosquitto_auth_acl_check(%p, %d, %s, %s)" ANSI_RESET "\n", user_data, access, mosquitto_client_username(client), msg->topic); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { print_col(client); printf("PLUGIN ::: mosquitto_auth_unpwd_check(%p, %s, %s)" ANSI_RESET "\n", user_data, mosquitto_client_username(client), username); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { print_col(client); printf("PLUGIN ::: mosquitto_auth_psk_key_get(%p, %s, %s)" ANSI_RESET "\n", user_data, mosquitto_client_username(client), hint); return MOSQ_ERR_SUCCESS; } 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) { print_col(client); printf("PLUGIN ::: mosquitto_auth_start(%p, %s, %s, %d, %d, %hn)" ANSI_RESET "\n", user_data, mosquitto_client_username(client), method, reauth, data_in_len, data_out_len); return MOSQ_ERR_SUCCESS; } 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) { print_col(client); printf("PLUGIN ::: mosquitto_auth_continue(%p, %s, %s, %d, %hn)" ANSI_RESET "\n", user_data, mosquitto_client_username(client), method, data_in_len, data_out_len); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/src/plugin_defer.c000066400000000000000000000036641450213760600172610ustar00rootroot00000000000000/* Copyright (c) 2015-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ /* This is a skeleton authentication and access control plugin that simply defers all checks. */ #include #include "mosquitto_broker.h" #include "mosquitto_plugin.h" #include "mosquitto.h" int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { printf("mosquitto_acl_check(u:%s)\n", mosquitto_client_username(client)); return MOSQ_ERR_PLUGIN_DEFER; } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { return MOSQ_ERR_PLUGIN_DEFER; } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_PLUGIN_DEFER; } mosquitto-2.0.18/src/plugin_public.c000066400000000000000000000150161450213760600174440ustar00rootroot00000000000000/* Copyright (c) 2016-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto_broker_internal.h" #include "mosquitto_internal.h" #include "mosquitto_broker.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "send_mosq.h" #include "util_mosq.h" #include "utlist.h" #ifdef WITH_TLS # include #endif const char *mosquitto_client_address(const struct mosquitto *client) { if(client){ return client->address; }else{ return NULL; } } bool mosquitto_client_clean_session(const struct mosquitto *client) { if(client){ return client->clean_start; }else{ return true; } } const char *mosquitto_client_id(const struct mosquitto *client) { if(client){ return client->id; }else{ return NULL; } } int mosquitto_client_keepalive(const struct mosquitto *client) { if(client){ return client->keepalive; }else{ return -1; } } void *mosquitto_client_certificate(const struct mosquitto *client) { #ifdef WITH_TLS if(client && client->ssl){ return SSL_get_peer_certificate(client->ssl); }else{ return NULL; } #else UNUSED(client); return NULL; #endif } int mosquitto_client_protocol(const struct mosquitto *client) { #ifdef WITH_WEBSOCKETS if(client && client->wsi){ return mp_websockets; }else #else UNUSED(client); #endif { return mp_mqtt; } } int mosquitto_client_protocol_version(const struct mosquitto *client) { if(client){ switch(client->protocol){ case mosq_p_mqtt31: return 3; case mosq_p_mqtt311: return 4; case mosq_p_mqtt5: return 5; default: return 0; } }else{ return 0; } } int mosquitto_client_sub_count(const struct mosquitto *client) { if(client){ return client->sub_count; }else{ return 0; } } const char *mosquitto_client_username(const struct mosquitto *client) { if(client){ #ifdef WITH_BRIDGE if(client->bridge){ return client->bridge->local_username; }else #endif { return client->username; } }else{ return NULL; } } int mosquitto_broker_publish( const char *clientid, const char *topic, int payloadlen, void *payload, int qos, bool retain, mosquitto_property *properties) { struct mosquitto_message_v5 *msg; if(topic == NULL || payloadlen < 0 || (payloadlen > 0 && payload == NULL) || qos < 0 || qos > 2){ return MOSQ_ERR_INVAL; } msg = mosquitto__malloc(sizeof(struct mosquitto_message_v5)); if(msg == NULL) return MOSQ_ERR_NOMEM; msg->next = NULL; msg->prev = NULL; if(clientid){ msg->clientid = mosquitto__strdup(clientid); if(msg->clientid == NULL){ mosquitto__free(msg); return MOSQ_ERR_NOMEM; } }else{ msg->clientid = NULL; } msg->topic = mosquitto__strdup(topic); if(msg->topic == NULL){ mosquitto__free(msg->clientid); mosquitto__free(msg); return MOSQ_ERR_NOMEM; } msg->payloadlen = payloadlen; msg->payload = payload; msg->qos = qos; msg->retain = retain; msg->properties = properties; DL_APPEND(db.plugin_msgs, msg); return MOSQ_ERR_SUCCESS; } int mosquitto_broker_publish_copy( const char *clientid, const char *topic, int payloadlen, const void *payload, int qos, bool retain, mosquitto_property *properties) { void *payload_out; if(topic == NULL || payloadlen < 0 || (payloadlen > 0 && payload == NULL) || qos < 0 || qos > 2){ return MOSQ_ERR_INVAL; } payload_out = calloc(1, (size_t)(payloadlen+1)); if(payload_out == NULL){ return MOSQ_ERR_NOMEM; } memcpy(payload_out, payload, (size_t)payloadlen); return mosquitto_broker_publish( clientid, topic, payloadlen, payload_out, qos, retain, properties); } int mosquitto_set_username(struct mosquitto *client, const char *username) { char *u_dup; char *old; int rc; if(!client) return MOSQ_ERR_INVAL; if(username){ if(mosquitto_validate_utf8(username, (int)strlen(username))){ return MOSQ_ERR_MALFORMED_UTF8; } u_dup = mosquitto__strdup(username); if(!u_dup) return MOSQ_ERR_NOMEM; }else{ u_dup = NULL; } old = client->username; client->username = u_dup; rc = acl__find_acls(client); if(rc){ client->username = old; mosquitto__free(u_dup); return rc; }else{ mosquitto__free(old); return MOSQ_ERR_SUCCESS; } } /* Check to see whether durable clients still have rights to their subscriptions. */ static void check_subscription_acls(struct mosquitto *context) { int i; int rc; uint8_t reason; for(i=0; isub_count; i++){ if(context->subs[i] == NULL){ continue; } rc = mosquitto_acl_check(context, context->subs[i]->topic_filter, 0, NULL, 0, /* FIXME */ false, MOSQ_ACL_SUBSCRIBE); if(rc != MOSQ_ERR_SUCCESS){ sub__remove(context, context->subs[i]->topic_filter, db.subs, &reason); } } } static void disconnect_client(struct mosquitto *context, bool with_will) { if(context->protocol == mosq_p_mqtt5){ send__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL); } if(with_will == false){ mosquitto__set_state(context, mosq_cs_disconnecting); } if(context->session_expiry_interval > 0){ check_subscription_acls(context); } do_disconnect(context, MOSQ_ERR_ADMINISTRATIVE_ACTION); } int mosquitto_kick_client_by_clientid(const char *clientid, bool with_will) { struct mosquitto *ctxt, *ctxt_tmp; if(clientid == NULL){ HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){ disconnect_client(ctxt, with_will); } return MOSQ_ERR_SUCCESS; }else{ HASH_FIND(hh_id, db.contexts_by_id, clientid, strlen(clientid), ctxt); if(ctxt){ disconnect_client(ctxt, with_will); return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NOT_FOUND; } } } int mosquitto_kick_client_by_username(const char *username, bool with_will) { struct mosquitto *ctxt, *ctxt_tmp; if(username == NULL){ HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){ if(ctxt->username == NULL){ disconnect_client(ctxt, with_will); } } }else{ HASH_ITER(hh_sock, db.contexts_by_sock, ctxt, ctxt_tmp){ if(ctxt->username != NULL && !strcmp(ctxt->username, username)){ disconnect_client(ctxt, with_will); } } } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/src/property_broker.c000066400000000000000000000065171450213760600200460ustar00rootroot00000000000000/* Copyright (c) 2018-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "property_mosq.h" /* Process the incoming properties, we should be able to assume that only valid * properties for CONNECT are present here. */ int property__process_connect(struct mosquitto *context, mosquitto_property **props) { mosquitto_property *p; p = *props; while(p){ if(p->identifier == MQTT_PROP_SESSION_EXPIRY_INTERVAL){ context->session_expiry_interval = p->value.i32; }else if(p->identifier == MQTT_PROP_RECEIVE_MAXIMUM){ if(p->value.i16 == 0){ return MOSQ_ERR_PROTOCOL; } context->msgs_out.inflight_maximum = p->value.i16; context->msgs_out.inflight_quota = context->msgs_out.inflight_maximum; }else if(p->identifier == MQTT_PROP_MAXIMUM_PACKET_SIZE){ if(p->value.i32 == 0){ return MOSQ_ERR_PROTOCOL; } context->maximum_packet_size = p->value.i32; } p = p->next; } return MOSQ_ERR_SUCCESS; } int property__process_will(struct mosquitto *context, struct mosquitto_message_all *msg, mosquitto_property **props) { mosquitto_property *p, *p_prev; mosquitto_property *msg_properties, *msg_properties_last; p = *props; p_prev = NULL; msg_properties = NULL; msg_properties_last = NULL; while(p){ switch(p->identifier){ case MQTT_PROP_CONTENT_TYPE: case MQTT_PROP_CORRELATION_DATA: case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR: case MQTT_PROP_RESPONSE_TOPIC: case MQTT_PROP_USER_PROPERTY: if(msg_properties){ msg_properties_last->next = p; msg_properties_last = p; }else{ msg_properties = p; msg_properties_last = p; } if(p_prev){ p_prev->next = p->next; p = p_prev->next; }else{ *props = p->next; p = *props; } msg_properties_last->next = NULL; break; case MQTT_PROP_WILL_DELAY_INTERVAL: context->will_delay_interval = p->value.i32; p_prev = p; p = p->next; break; case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL: msg->expiry_interval = p->value.i32; p_prev = p; p = p->next; break; default: msg->properties = msg_properties; return MOSQ_ERR_PROTOCOL; break; } } msg->properties = msg_properties; return MOSQ_ERR_SUCCESS; } /* Process the incoming properties, we should be able to assume that only valid * properties for DISCONNECT are present here. */ int property__process_disconnect(struct mosquitto *context, mosquitto_property **props) { mosquitto_property *p; p = *props; while(p){ if(p->identifier == MQTT_PROP_SESSION_EXPIRY_INTERVAL){ if(context->session_expiry_interval == 0 && p->value.i32 != 0){ return MOSQ_ERR_PROTOCOL; } context->session_expiry_interval = p->value.i32; } p = p->next; } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/src/read_handle.c000066400000000000000000000054301450213760600170350ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "read_handle.h" #include "send_mosq.h" #include "sys_tree.h" #include "util_mosq.h" int handle__packet(struct mosquitto *context) { int rc = MOSQ_ERR_INVAL; if(!context) return MOSQ_ERR_INVAL; switch((context->in_packet.command)&0xF0){ case CMD_PINGREQ: rc = handle__pingreq(context); break; case CMD_PINGRESP: rc = handle__pingresp(context); break; case CMD_PUBACK: rc = handle__pubackcomp(context, "PUBACK"); break; case CMD_PUBCOMP: rc = handle__pubackcomp(context, "PUBCOMP"); break; case CMD_PUBLISH: rc = handle__publish(context); break; case CMD_PUBREC: rc = handle__pubrec(context); break; case CMD_PUBREL: rc = handle__pubrel(context); break; case CMD_CONNECT: return handle__connect(context); case CMD_DISCONNECT: rc = handle__disconnect(context); break; case CMD_SUBSCRIBE: rc = handle__subscribe(context); break; case CMD_UNSUBSCRIBE: rc = handle__unsubscribe(context); break; #ifdef WITH_BRIDGE case CMD_CONNACK: rc = handle__connack(context); break; case CMD_SUBACK: rc = handle__suback(context); break; case CMD_UNSUBACK: rc = handle__unsuback(context); break; #endif case CMD_AUTH: rc = handle__auth(context); break; default: rc = MOSQ_ERR_PROTOCOL; } if(context->protocol == mosq_p_mqtt5){ if(rc == MOSQ_ERR_PROTOCOL || rc == MOSQ_ERR_DUPLICATE_PROPERTY){ send__disconnect(context, MQTT_RC_PROTOCOL_ERROR, NULL); }else if(rc == MOSQ_ERR_MALFORMED_PACKET){ send__disconnect(context, MQTT_RC_MALFORMED_PACKET, NULL); }else if(rc == MOSQ_ERR_QOS_NOT_SUPPORTED){ send__disconnect(context, MQTT_RC_QOS_NOT_SUPPORTED, NULL); }else if(rc == MOSQ_ERR_RETAIN_NOT_SUPPORTED){ send__disconnect(context, MQTT_RC_RETAIN_NOT_SUPPORTED, NULL); }else if(rc == MOSQ_ERR_TOPIC_ALIAS_INVALID){ send__disconnect(context, MQTT_RC_TOPIC_ALIAS_INVALID, NULL); }else if(rc == MOSQ_ERR_UNKNOWN || rc == MOSQ_ERR_NOMEM){ send__disconnect(context, MQTT_RC_UNSPECIFIED, NULL); } } return rc; } mosquitto-2.0.18/src/retain.c000066400000000000000000000214001450213760600160640ustar00rootroot00000000000000/* Copyright (c) 2010-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "util_mosq.h" #include "utlist.h" static struct mosquitto__retainhier *retain__add_hier_entry(struct mosquitto__retainhier *parent, struct mosquitto__retainhier **sibling, const char *topic, uint16_t len) { struct mosquitto__retainhier *child; assert(sibling); child = mosquitto__calloc(1, sizeof(struct mosquitto__retainhier)); if(!child){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return NULL; } child->parent = parent; child->topic_len = len; child->topic = mosquitto__malloc((size_t)len+1); if(!child->topic){ child->topic_len = 0; mosquitto__free(child); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return NULL; }else{ strncpy(child->topic, topic, (size_t)child->topic_len+1); } HASH_ADD_KEYPTR(hh, *sibling, child->topic, child->topic_len, child); return child; } int retain__init(void) { struct mosquitto__retainhier *retainhier; retainhier = retain__add_hier_entry(NULL, &db.retains, "", 0); if(!retainhier) return MOSQ_ERR_NOMEM; retainhier = retain__add_hier_entry(NULL, &db.retains, "$SYS", (uint16_t)strlen("$SYS")); if(!retainhier) return MOSQ_ERR_NOMEM; return MOSQ_ERR_SUCCESS; } void retain__clean_empty_hierarchy(struct mosquitto__retainhier *retainhier) { struct mosquitto__retainhier *parent; while(retainhier){ if(retainhier->children || retainhier->retained || retainhier->parent == NULL){ /* Entry is being used */ return; }else{ HASH_DELETE(hh, retainhier->parent->children, retainhier); mosquitto__free(retainhier->topic); parent = retainhier->parent; mosquitto__free(retainhier); retainhier = parent; } } } int retain__store(const char *topic, struct mosquitto_msg_store *stored, char **split_topics) { struct mosquitto__retainhier *retainhier; struct mosquitto__retainhier *branch; int i; size_t slen; assert(stored); assert(split_topics); HASH_FIND(hh, db.retains, split_topics[0], strlen(split_topics[0]), retainhier); if(retainhier == NULL){ retainhier = retain__add_hier_entry(NULL, &db.retains, split_topics[0], (uint16_t)strlen(split_topics[0])); if(!retainhier) return MOSQ_ERR_NOMEM; } for(i=0; split_topics[i] != NULL; i++){ slen = strlen(split_topics[i]); HASH_FIND(hh, retainhier->children, split_topics[i], slen, branch); if(branch == NULL){ branch = retain__add_hier_entry(retainhier, &retainhier->children, split_topics[i], (uint16_t)slen); if(branch == NULL){ return MOSQ_ERR_NOMEM; } } retainhier = branch; } #ifdef WITH_PERSISTENCE if(strncmp(topic, "$SYS", 4)){ /* Retained messages count as a persistence change, but only if * they aren't for $SYS. */ db.persistence_changes++; } #else UNUSED(topic); #endif if(retainhier->retained){ db__msg_store_ref_dec(&retainhier->retained); #ifdef WITH_SYS_TREE db.retained_count--; #endif } if(stored->payloadlen){ retainhier->retained = stored; db__msg_store_ref_inc(retainhier->retained); #ifdef WITH_SYS_TREE db.retained_count++; #endif }else{ retainhier->retained = NULL; retain__clean_empty_hierarchy(retainhier); } return MOSQ_ERR_SUCCESS; } static int retain__process(struct mosquitto__retainhier *branch, struct mosquitto *context, uint8_t sub_qos, uint32_t subscription_identifier) { int rc = 0; uint8_t qos; uint16_t mid; mosquitto_property *properties = NULL; struct mosquitto_msg_store *retained; if(branch->retained->message_expiry_time > 0 && db.now_real_s >= branch->retained->message_expiry_time){ db__msg_store_ref_dec(&branch->retained); branch->retained = NULL; #ifdef WITH_SYS_TREE db.retained_count--; #endif return MOSQ_ERR_SUCCESS; } retained = branch->retained; rc = mosquitto_acl_check(context, retained->topic, retained->payloadlen, retained->payload, retained->qos, retained->retain, MOSQ_ACL_READ); if(rc == MOSQ_ERR_ACL_DENIED){ return MOSQ_ERR_SUCCESS; }else if(rc != MOSQ_ERR_SUCCESS){ return rc; } /* Check for original source access */ if(db.config->check_retain_source && retained->origin != mosq_mo_broker && retained->source_id){ struct mosquitto retain_ctxt; memset(&retain_ctxt, 0, sizeof(struct mosquitto)); retain_ctxt.id = retained->source_id; retain_ctxt.username = retained->source_username; retain_ctxt.listener = retained->source_listener; rc = acl__find_acls(&retain_ctxt); if(rc) return rc; rc = mosquitto_acl_check(&retain_ctxt, retained->topic, retained->payloadlen, retained->payload, retained->qos, retained->retain, MOSQ_ACL_WRITE); if(rc == MOSQ_ERR_ACL_DENIED){ return MOSQ_ERR_SUCCESS; }else if(rc != MOSQ_ERR_SUCCESS){ return rc; } } if (db.config->upgrade_outgoing_qos){ qos = sub_qos; } else { qos = retained->qos; if(qos > sub_qos) qos = sub_qos; } if(qos > 0){ mid = mosquitto__mid_generate(context); }else{ mid = 0; } if(subscription_identifier > 0){ mosquitto_property_add_varint(&properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, subscription_identifier); } return db__message_insert(context, mid, mosq_md_out, qos, true, retained, properties, false); } static int retain__search(struct mosquitto__retainhier *retainhier, char **split_topics, struct mosquitto *context, const char *sub, uint8_t sub_qos, uint32_t subscription_identifier, int level) { struct mosquitto__retainhier *branch, *branch_tmp; int flag = 0; if(!strcmp(split_topics[0], "#") && split_topics[1] == NULL){ HASH_ITER(hh, retainhier->children, branch, branch_tmp){ /* Set flag to indicate that we should check for retained messages * on "foo" when we are subscribing to e.g. "foo/#" and then exit * this function and return to an earlier retain__search(). */ flag = -1; if(branch->retained){ retain__process(branch, context, sub_qos, subscription_identifier); } if(branch->children){ retain__search(branch, split_topics, context, sub, sub_qos, subscription_identifier, level+1); } } }else{ if(!strcmp(split_topics[0], "+")){ HASH_ITER(hh, retainhier->children, branch, branch_tmp){ if(split_topics[1] != NULL){ if(retain__search(branch, &(split_topics[1]), context, sub, sub_qos, subscription_identifier, level+1) == -1 || (split_topics[1] != NULL && !strcmp(split_topics[1], "#") && level>0)){ if(branch->retained){ retain__process(branch, context, sub_qos, subscription_identifier); } } }else{ if(branch->retained){ retain__process(branch, context, sub_qos, subscription_identifier); } } } }else{ HASH_FIND(hh, retainhier->children, split_topics[0], strlen(split_topics[0]), branch); if(branch){ if(split_topics[1] != NULL){ if(retain__search(branch, &(split_topics[1]), context, sub, sub_qos, subscription_identifier, level+1) == -1 || (split_topics[1] != NULL && !strcmp(split_topics[1], "#") && level>0)){ if(branch->retained){ retain__process(branch, context, sub_qos, subscription_identifier); } } }else{ if(branch->retained){ retain__process(branch, context, sub_qos, subscription_identifier); } } } } } return flag; } int retain__queue(struct mosquitto *context, const char *sub, uint8_t sub_qos, uint32_t subscription_identifier) { struct mosquitto__retainhier *retainhier; char *local_sub; char **split_topics; int rc; assert(context); assert(sub); if(!strncmp(sub, "$share/", strlen("$share/"))){ return MOSQ_ERR_SUCCESS; } rc = sub__topic_tokenise(sub, &local_sub, &split_topics, NULL); if(rc) return rc; HASH_FIND(hh, db.retains, split_topics[0], strlen(split_topics[0]), retainhier); if(retainhier){ retain__search(retainhier, split_topics, context, sub, sub_qos, subscription_identifier, 0); } mosquitto__free(local_sub); mosquitto__free(split_topics); return MOSQ_ERR_SUCCESS; } void retain__clean(struct mosquitto__retainhier **retainhier) { struct mosquitto__retainhier *peer, *retainhier_tmp; HASH_ITER(hh, *retainhier, peer, retainhier_tmp){ if(peer->retained){ db__msg_store_ref_dec(&peer->retained); } retain__clean(&peer->children); mosquitto__free(peer->topic); HASH_DELETE(hh, *retainhier, peer); mosquitto__free(peer); } } mosquitto-2.0.18/src/security.c000066400000000000000000000752651450213760600164730ustar00rootroot00000000000000/* Copyright (c) 2011-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include "mosquitto_broker.h" #include "mosquitto_broker_internal.h" #include "mosquitto_plugin.h" #include "memory_mosq.h" #include "lib_load.h" #include "utlist.h" typedef int (*FUNC_auth_plugin_version)(void); typedef int (*FUNC_plugin_version)(int, const int *); static int security__cleanup_single(struct mosquitto__security_options *opts, bool reload); void LIB_ERROR(void) { #ifdef WIN32 char *buf; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&buf, 0, NULL); log__printf(NULL, MOSQ_LOG_ERR, "Load error: %s", buf); LocalFree(buf); #else log__printf(NULL, MOSQ_LOG_ERR, "Load error: %s", dlerror()); #endif } static int security__load_v2(struct mosquitto__auth_plugin *plugin, struct mosquitto_auth_opt *auth_options, int auth_option_count, void *lib) { int rc; if(!(plugin->plugin_init_v2 = (FUNC_auth_plugin_init_v2)LIB_SYM(lib, "mosquitto_auth_plugin_init"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_plugin_init()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->plugin_cleanup_v2 = (FUNC_auth_plugin_cleanup_v2)LIB_SYM(lib, "mosquitto_auth_plugin_cleanup"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_plugin_cleanup()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->security_init_v2 = (FUNC_auth_plugin_security_init_v2)LIB_SYM(lib, "mosquitto_auth_security_init"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_security_init()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->security_cleanup_v2 = (FUNC_auth_plugin_security_cleanup_v2)LIB_SYM(lib, "mosquitto_auth_security_cleanup"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_security_cleanup()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->acl_check_v2 = (FUNC_auth_plugin_acl_check_v2)LIB_SYM(lib, "mosquitto_auth_acl_check"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_acl_check()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->unpwd_check_v2 = (FUNC_auth_plugin_unpwd_check_v2)LIB_SYM(lib, "mosquitto_auth_unpwd_check"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_unpwd_check()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->psk_key_get_v2 = (FUNC_auth_plugin_psk_key_get_v2)LIB_SYM(lib, "mosquitto_auth_psk_key_get"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_psk_key_get()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } plugin->lib = lib; plugin->user_data = NULL; if(plugin->plugin_init_v2){ rc = plugin->plugin_init_v2(&plugin->user_data, auth_options, auth_option_count); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Authentication plugin returned %d when initialising.", rc); return rc; } } return 0; } static int security__load_v3(struct mosquitto__auth_plugin *plugin, struct mosquitto_opt *auth_options, int auth_option_count, void *lib) { int rc; if(!(plugin->plugin_init_v3 = (FUNC_auth_plugin_init_v3)LIB_SYM(lib, "mosquitto_auth_plugin_init"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_plugin_init()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->plugin_cleanup_v3 = (FUNC_auth_plugin_cleanup_v3)LIB_SYM(lib, "mosquitto_auth_plugin_cleanup"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_plugin_cleanup()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->security_init_v3 = (FUNC_auth_plugin_security_init_v3)LIB_SYM(lib, "mosquitto_auth_security_init"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_security_init()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->security_cleanup_v3 = (FUNC_auth_plugin_security_cleanup_v3)LIB_SYM(lib, "mosquitto_auth_security_cleanup"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_security_cleanup()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->acl_check_v3 = (FUNC_auth_plugin_acl_check_v3)LIB_SYM(lib, "mosquitto_auth_acl_check"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_acl_check()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->unpwd_check_v3 = (FUNC_auth_plugin_unpwd_check_v3)LIB_SYM(lib, "mosquitto_auth_unpwd_check"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_unpwd_check()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->psk_key_get_v3 = (FUNC_auth_plugin_psk_key_get_v3)LIB_SYM(lib, "mosquitto_auth_psk_key_get"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_psk_key_get()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } plugin->lib = lib; plugin->user_data = NULL; if(plugin->plugin_init_v3){ rc = plugin->plugin_init_v3(&plugin->user_data, auth_options, auth_option_count); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Authentication plugin returned %d when initialising.", rc); return rc; } } return 0; } static int security__load_v4(struct mosquitto__auth_plugin *plugin, struct mosquitto_opt *auth_options, int auth_option_count, void *lib) { int rc; if(!(plugin->plugin_init_v4 = (FUNC_auth_plugin_init_v4)LIB_SYM(lib, "mosquitto_auth_plugin_init"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_plugin_init()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->plugin_cleanup_v4 = (FUNC_auth_plugin_cleanup_v4)LIB_SYM(lib, "mosquitto_auth_plugin_cleanup"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_plugin_cleanup()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->security_init_v4 = (FUNC_auth_plugin_security_init_v4)LIB_SYM(lib, "mosquitto_auth_security_init"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_security_init()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->security_cleanup_v4 = (FUNC_auth_plugin_security_cleanup_v4)LIB_SYM(lib, "mosquitto_auth_security_cleanup"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_security_cleanup()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } if(!(plugin->acl_check_v4 = (FUNC_auth_plugin_acl_check_v4)LIB_SYM(lib, "mosquitto_auth_acl_check"))){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_acl_check()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } plugin->unpwd_check_v4 = (FUNC_auth_plugin_unpwd_check_v4)LIB_SYM(lib, "mosquitto_auth_unpwd_check"); if(plugin->unpwd_check_v4){ log__printf(NULL, MOSQ_LOG_INFO, " ├── Username/password checking enabled."); }else{ log__printf(NULL, MOSQ_LOG_INFO, " ├── Username/password checking not enabled."); } plugin->psk_key_get_v4 = (FUNC_auth_plugin_psk_key_get_v4)LIB_SYM(lib, "mosquitto_auth_psk_key_get"); if(plugin->psk_key_get_v4){ log__printf(NULL, MOSQ_LOG_INFO, " ├── TLS-PSK checking enabled."); }else{ log__printf(NULL, MOSQ_LOG_INFO, " ├── TLS-PSK checking not enabled."); } plugin->auth_start_v4 = (FUNC_auth_plugin_auth_start_v4)LIB_SYM(lib, "mosquitto_auth_start"); plugin->auth_continue_v4 = (FUNC_auth_plugin_auth_continue_v4)LIB_SYM(lib, "mosquitto_auth_continue"); if(plugin->auth_start_v4){ if(plugin->auth_continue_v4){ log__printf(NULL, MOSQ_LOG_INFO, " └── Extended authentication enabled."); }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Plugin has missing mosquitto_auth_continue() function."); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } }else{ log__printf(NULL, MOSQ_LOG_INFO, " └── Extended authentication not enabled."); } plugin->lib = lib; plugin->user_data = NULL; if(plugin->plugin_init_v4){ rc = plugin->plugin_init_v4(&plugin->user_data, auth_options, auth_option_count); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Authentication plugin returned %d when initialising.", rc); return rc; } } return 0; } static int security__module_init_single(struct mosquitto__listener *listener, struct mosquitto__security_options *opts) { void *lib; int (*plugin_version)(int, const int*) = NULL; int (*plugin_auth_version)(void) = NULL; int version; int i; int rc; const int plugin_versions[] = {5, 4, 3, 2}; int plugin_version_count = sizeof(plugin_versions)/sizeof(int); if(opts->auth_plugin_config_count == 0){ return MOSQ_ERR_SUCCESS; } for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].path){ memset(&opts->auth_plugin_configs[i].plugin, 0, sizeof(struct mosquitto__auth_plugin)); log__printf(NULL, MOSQ_LOG_INFO, "Loading plugin: %s", opts->auth_plugin_configs[i].path); lib = LIB_LOAD(opts->auth_plugin_configs[i].path); if(!lib){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin \"%s\".", opts->auth_plugin_configs[i].path); LIB_ERROR(); return MOSQ_ERR_UNKNOWN; } opts->auth_plugin_configs[i].plugin.lib = NULL; if((plugin_version = (FUNC_plugin_version)LIB_SYM(lib, "mosquitto_plugin_version"))){ version = plugin_version(plugin_version_count, plugin_versions); }else if((plugin_auth_version = (FUNC_auth_plugin_version)LIB_SYM(lib, "mosquitto_auth_plugin_version"))){ version = plugin_auth_version(); }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load auth plugin function mosquitto_auth_plugin_version() or mosquitto_plugin_version()."); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } opts->auth_plugin_configs[i].plugin.version = version; if(version == 5){ rc = plugin__load_v5( listener, &opts->auth_plugin_configs[i].plugin, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, lib); if(rc){ return rc; } }else if(version == 4){ rc = security__load_v4( &opts->auth_plugin_configs[i].plugin, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, lib); if(rc){ return rc; } }else if(version == 3){ rc = security__load_v3( &opts->auth_plugin_configs[i].plugin, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, lib); if(rc){ return rc; } }else if(version == 2){ rc = security__load_v2( &opts->auth_plugin_configs[i].plugin, (struct mosquitto_auth_opt *)opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, lib); if(rc){ return rc; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unsupported auth plugin version (got %d, expected %d).", version, MOSQ_PLUGIN_VERSION); LIB_ERROR(); LIB_CLOSE(lib); return MOSQ_ERR_UNKNOWN; } } } return MOSQ_ERR_SUCCESS; } int mosquitto_security_module_init(void) { int rc = MOSQ_ERR_SUCCESS; int i; if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ rc = security__module_init_single(&db.config->listeners[i], &db.config->listeners[i].security_options); if(rc) return rc; } }else{ rc = security__module_init_single(NULL, &db.config->security_options); } return rc; } static void security__module_cleanup_single(struct mosquitto__security_options *opts) { int i; for(i=0; iauth_plugin_config_count; i++){ /* Run plugin cleanup function */ if(opts->auth_plugin_configs[i].plugin.version == 5){ opts->auth_plugin_configs[i].plugin.plugin_cleanup_v5( opts->auth_plugin_configs[i].plugin.user_data, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count); mosquitto__free(opts->auth_plugin_configs[i].plugin.identifier); opts->auth_plugin_configs[i].plugin.identifier = NULL; }else if(opts->auth_plugin_configs[i].plugin.version == 4){ opts->auth_plugin_configs[i].plugin.plugin_cleanup_v4( opts->auth_plugin_configs[i].plugin.user_data, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count); }else if(opts->auth_plugin_configs[i].plugin.version == 3){ opts->auth_plugin_configs[i].plugin.plugin_cleanup_v3( opts->auth_plugin_configs[i].plugin.user_data, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count); }else if(opts->auth_plugin_configs[i].plugin.version == 2){ opts->auth_plugin_configs[i].plugin.plugin_cleanup_v2( opts->auth_plugin_configs[i].plugin.user_data, (struct mosquitto_auth_opt *)opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count); } if(opts->auth_plugin_configs[i].plugin.lib){ LIB_CLOSE(opts->auth_plugin_configs[i].plugin.lib); } memset(&opts->auth_plugin_configs[i].plugin, 0, sizeof(struct mosquitto__auth_plugin)); } } int mosquitto_security_module_cleanup(void) { int i; mosquitto_security_cleanup(false); security__module_cleanup_single(&db.config->security_options); for(i=0; ilistener_count; i++){ security__module_cleanup_single(&db.config->listeners[i].security_options); } return MOSQ_ERR_SUCCESS; } static int security__init_single(struct mosquitto__security_options *opts, bool reload) { int i; int rc; struct mosquitto_evt_reload event_data; struct mosquitto__callback *cb_base; if(reload){ DL_FOREACH(opts->plugin_callbacks.reload, cb_base){ memset(&event_data, 0, sizeof(event_data)); event_data.options = NULL; event_data.option_count = 0; rc = cb_base->cb(MOSQ_EVT_RELOAD, &event_data, cb_base->userdata); if(rc != MOSQ_ERR_PLUGIN_DEFER){ return rc; } } } for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].plugin.version == 5){ continue; }else if(opts->auth_plugin_configs[i].plugin.version == 4){ rc = opts->auth_plugin_configs[i].plugin.security_init_v4( opts->auth_plugin_configs[i].plugin.user_data, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, reload); }else if(opts->auth_plugin_configs[i].plugin.version == 3){ rc = opts->auth_plugin_configs[i].plugin.security_init_v3( opts->auth_plugin_configs[i].plugin.user_data, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, reload); }else if(opts->auth_plugin_configs[i].plugin.version == 2){ rc = opts->auth_plugin_configs[i].plugin.security_init_v2( opts->auth_plugin_configs[i].plugin.user_data, (struct mosquitto_auth_opt *)opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, reload); }else{ rc = MOSQ_ERR_INVAL; } if(rc != MOSQ_ERR_SUCCESS){ return rc; } } return MOSQ_ERR_SUCCESS; } int mosquitto_security_init(bool reload) { int i; int rc; if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ rc = security__init_single(&db.config->listeners[i].security_options, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; } }else{ rc = security__init_single(&db.config->security_options, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; } return mosquitto_security_init_default(reload); } /* Apply security settings after a reload. * Includes: * - Disconnecting anonymous users if appropriate * - Disconnecting users with invalid passwords * - Reapplying ACLs */ int mosquitto_security_apply(void) { return mosquitto_security_apply_default(); } static int security__cleanup_single(struct mosquitto__security_options *opts, bool reload) { int i; int rc; for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].plugin.version == 5){ rc = MOSQ_ERR_SUCCESS; }else if(opts->auth_plugin_configs[i].plugin.version == 4){ rc = opts->auth_plugin_configs[i].plugin.security_cleanup_v4( opts->auth_plugin_configs[i].plugin.user_data, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, reload); }else if(opts->auth_plugin_configs[i].plugin.version == 3){ rc = opts->auth_plugin_configs[i].plugin.security_cleanup_v3( opts->auth_plugin_configs[i].plugin.user_data, opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, reload); }else if(opts->auth_plugin_configs[i].plugin.version == 2){ rc = opts->auth_plugin_configs[i].plugin.security_cleanup_v2( opts->auth_plugin_configs[i].plugin.user_data, (struct mosquitto_auth_opt *)opts->auth_plugin_configs[i].options, opts->auth_plugin_configs[i].option_count, reload); }else{ rc = MOSQ_ERR_INVAL; } if(rc != MOSQ_ERR_SUCCESS){ return rc; } } return MOSQ_ERR_SUCCESS; } int mosquitto_security_cleanup(bool reload) { int i; int rc; rc = security__cleanup_single(&db.config->security_options, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; for(i=0; ilistener_count; i++){ rc = security__cleanup_single(&db.config->listeners[i].security_options, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; } return mosquitto_security_cleanup_default(reload); } /* int mosquitto_acl_check(struct mosquitto *context, const char *topic, int access) */ static int acl__check_single(struct mosquitto__auth_plugin_config *auth_plugin, struct mosquitto *context, struct mosquitto_acl_msg *msg, int access) { const char *username; const char *topic = msg->topic; username = mosquitto_client_username(context); if(auth_plugin->deny_special_chars == true){ /* Check whether the client id or username contains a +, # or / and if * so deny access. * * Do this check for every message regardless, we have to protect the * plugins against possible pattern based attacks. */ if(username && strpbrk(username, "+#")){ log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", username); return MOSQ_ERR_ACL_DENIED; } if(context->id && strpbrk(context->id, "+#")){ log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", context->id); return MOSQ_ERR_ACL_DENIED; } } if(auth_plugin->plugin.version == 4){ if(access == MOSQ_ACL_UNSUBSCRIBE){ return MOSQ_ERR_SUCCESS; } return auth_plugin->plugin.acl_check_v4(auth_plugin->plugin.user_data, access, context, msg); }else if(auth_plugin->plugin.version == 3){ if(access == MOSQ_ACL_UNSUBSCRIBE){ return MOSQ_ERR_SUCCESS; } return auth_plugin->plugin.acl_check_v3(auth_plugin->plugin.user_data, access, context, msg); }else if(auth_plugin->plugin.version == 2){ if(access == MOSQ_ACL_SUBSCRIBE || access == MOSQ_ACL_UNSUBSCRIBE){ return MOSQ_ERR_SUCCESS; } return auth_plugin->plugin.acl_check_v2(auth_plugin->plugin.user_data, context->id, username, topic, access); }else{ return MOSQ_ERR_INVAL; } } static int acl__check_dollar(const char *topic, int access) { int rc; bool match = false; if(topic[0] != '$') return MOSQ_ERR_SUCCESS; if(!strncmp(topic, "$SYS", 4)){ if(access == MOSQ_ACL_WRITE){ /* Potentially allow write access for bridge status, otherwise explicitly deny. */ rc = mosquitto_topic_matches_sub("$SYS/broker/connection/+/state", topic, &match); if(rc == MOSQ_ERR_SUCCESS && match == true){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } }else{ return MOSQ_ERR_SUCCESS; } }else if(!strncmp(topic, "$share", 6)){ /* Only allow sub/unsub to shared subscriptions */ if(access == MOSQ_ACL_SUBSCRIBE || access == MOSQ_ACL_UNSUBSCRIBE){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } }else{ /* This is an unknown $ topic, for the moment just defer to actual tests. */ return MOSQ_ERR_SUCCESS; } } int mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void* payload, uint8_t qos, bool retain, int access) { int rc; int i; struct mosquitto__security_options *opts; struct mosquitto_acl_msg msg; struct mosquitto__callback *cb_base; struct mosquitto_evt_acl_check event_data; if(!context->id){ return MOSQ_ERR_ACL_DENIED; } if(context->bridge){ return MOSQ_ERR_SUCCESS; } rc = acl__check_dollar(topic, access); if(rc) return rc; /* * If no plugins exist we should accept at this point so set rc to success. */ rc = MOSQ_ERR_SUCCESS; if(db.config->per_listener_settings){ if(context->listener){ opts = &context->listener->security_options; }else{ return MOSQ_ERR_ACL_DENIED; } }else{ opts = &db.config->security_options; } memset(&msg, 0, sizeof(msg)); msg.topic = topic; msg.payloadlen = payloadlen; msg.payload = payload; msg.qos = qos; msg.retain = retain; DL_FOREACH(opts->plugin_callbacks.acl_check, cb_base){ /* FIXME - username deny special chars */ memset(&event_data, 0, sizeof(event_data)); event_data.client = context; event_data.access = access; event_data.topic = topic; event_data.payloadlen = payloadlen; event_data.payload = payload; event_data.qos = qos; event_data.retain = retain; event_data.properties = NULL; rc = cb_base->cb(MOSQ_EVT_ACL_CHECK, &event_data, cb_base->userdata); if(rc != MOSQ_ERR_PLUGIN_DEFER){ return rc; } } for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].plugin.version < 5){ rc = acl__check_single(&opts->auth_plugin_configs[i], context, &msg, access); if(rc != MOSQ_ERR_PLUGIN_DEFER){ return rc; } } } /* If all plugins deferred, this is a denial. If rc == MOSQ_ERR_SUCCESS * here, then no plugins were configured. */ if(rc == MOSQ_ERR_PLUGIN_DEFER){ rc = MOSQ_ERR_ACL_DENIED; } return rc; } int mosquitto_unpwd_check(struct mosquitto *context) { int rc; int i; struct mosquitto__security_options *opts; struct mosquitto_evt_basic_auth event_data; struct mosquitto__callback *cb_base; bool plugin_used = false; rc = MOSQ_ERR_PLUGIN_DEFER; if(db.config->per_listener_settings){ if(context->listener == NULL){ return MOSQ_ERR_AUTH; } opts = &context->listener->security_options; }else{ opts = &db.config->security_options; } DL_FOREACH(opts->plugin_callbacks.basic_auth, cb_base){ memset(&event_data, 0, sizeof(event_data)); event_data.client = context; event_data.username = context->username; event_data.password = context->password; rc = cb_base->cb(MOSQ_EVT_BASIC_AUTH, &event_data, cb_base->userdata); if(rc != MOSQ_ERR_PLUGIN_DEFER){ return rc; } plugin_used = true; } for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].plugin.version == 4 && opts->auth_plugin_configs[i].plugin.unpwd_check_v4){ rc = opts->auth_plugin_configs[i].plugin.unpwd_check_v4( opts->auth_plugin_configs[i].plugin.user_data, context, context->username, context->password); plugin_used = true; }else if(opts->auth_plugin_configs[i].plugin.version == 3){ rc = opts->auth_plugin_configs[i].plugin.unpwd_check_v3( opts->auth_plugin_configs[i].plugin.user_data, context, context->username, context->password); plugin_used = true; }else if(opts->auth_plugin_configs[i].plugin.version == 2){ rc = opts->auth_plugin_configs[i].plugin.unpwd_check_v2( opts->auth_plugin_configs[i].plugin.user_data, context->username, context->password); plugin_used = true; } } /* If all plugins deferred, this is a denial. If rc == MOSQ_ERR_SUCCESS * here, then no plugins were configured. Unless we have all deferred, and * anonymous logins are allowed. */ if(plugin_used == false){ if((db.config->per_listener_settings && context->listener->security_options.allow_anonymous != false) || (!db.config->per_listener_settings && db.config->security_options.allow_anonymous != false)){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } }else{ if(rc == MOSQ_ERR_PLUGIN_DEFER){ if(context->username == NULL && ((db.config->per_listener_settings && context->listener->security_options.allow_anonymous != false) || (!db.config->per_listener_settings && db.config->security_options.allow_anonymous != false))){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } } } return rc; } int mosquitto_psk_key_get(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len) { int rc; int i; struct mosquitto__security_options *opts; struct mosquitto_evt_psk_key event_data; struct mosquitto__callback *cb_base; rc = mosquitto_psk_key_get_default(context, hint, identity, key, max_key_len); if(rc != MOSQ_ERR_PLUGIN_DEFER){ return rc; } /* Default check has accepted or deferred at this point. * If no plugins exist we should accept at this point so set rc to success. */ if(db.config->per_listener_settings){ opts = &context->listener->security_options; }else{ opts = &db.config->security_options; } DL_FOREACH(opts->plugin_callbacks.psk_key, cb_base){ memset(&event_data, 0, sizeof(event_data)); event_data.client = context; event_data.hint = hint; event_data.identity = identity; event_data.key = key; event_data.max_key_len = max_key_len; rc = cb_base->cb(MOSQ_EVT_PSK_KEY, &event_data, cb_base->userdata); if(rc != MOSQ_ERR_PLUGIN_DEFER){ return rc; } } for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].plugin.version == 4 && opts->auth_plugin_configs[i].plugin.psk_key_get_v4){ rc = opts->auth_plugin_configs[i].plugin.psk_key_get_v4( opts->auth_plugin_configs[i].plugin.user_data, context, hint, identity, key, max_key_len); }else if(opts->auth_plugin_configs[i].plugin.version == 3){ rc = opts->auth_plugin_configs[i].plugin.psk_key_get_v3( opts->auth_plugin_configs[i].plugin.user_data, context, hint, identity, key, max_key_len); }else if(opts->auth_plugin_configs[i].plugin.version == 2){ rc = opts->auth_plugin_configs[i].plugin.psk_key_get_v2( opts->auth_plugin_configs[i].plugin.user_data, hint, identity, key, max_key_len); }else{ rc = MOSQ_ERR_INVAL; } if(rc != MOSQ_ERR_PLUGIN_DEFER){ return rc; } } /* If all plugins deferred, this is a denial. If rc == MOSQ_ERR_SUCCESS * here, then no plugins were configured. */ if(rc == MOSQ_ERR_PLUGIN_DEFER){ rc = MOSQ_ERR_AUTH; } return rc; } int 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) { int rc = MOSQ_ERR_PLUGIN_DEFER; int i; struct mosquitto__security_options *opts; struct mosquitto_evt_extended_auth event_data; struct mosquitto__callback *cb_base; if(!context || !context->listener || !context->auth_method) return MOSQ_ERR_INVAL; if(!data_out || !data_out_len) return MOSQ_ERR_INVAL; if(db.config->per_listener_settings){ opts = &context->listener->security_options; }else{ opts = &db.config->security_options; } DL_FOREACH(opts->plugin_callbacks.ext_auth_start, cb_base){ memset(&event_data, 0, sizeof(event_data)); event_data.client = context; event_data.auth_method = context->auth_method; event_data.data_in = data_in; event_data.data_out = NULL; event_data.data_in_len = data_in_len; event_data.data_out_len = 0; rc = cb_base->cb(MOSQ_EVT_EXT_AUTH_START, &event_data, cb_base->userdata); if(rc != MOSQ_ERR_PLUGIN_DEFER){ *data_out = event_data.data_out; *data_out_len = event_data.data_out_len; return rc; } } for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].plugin.auth_start_v4){ *data_out = NULL; *data_out_len = 0; rc = opts->auth_plugin_configs[i].plugin.auth_start_v4( opts->auth_plugin_configs[i].plugin.user_data, context, context->auth_method, reauth, data_in, data_in_len, data_out, data_out_len); if(rc == MOSQ_ERR_SUCCESS){ return MOSQ_ERR_SUCCESS; }else if(rc == MOSQ_ERR_AUTH_CONTINUE){ return MOSQ_ERR_AUTH_CONTINUE; }else if(rc != MOSQ_ERR_NOT_SUPPORTED){ return rc; } } } return MOSQ_ERR_NOT_SUPPORTED; } int mosquitto_security_auth_continue(struct mosquitto *context, const void *data_in, uint16_t data_in_len, void **data_out, uint16_t *data_out_len) { int rc = MOSQ_ERR_PLUGIN_DEFER; int i; struct mosquitto__security_options *opts; struct mosquitto_evt_extended_auth event_data; struct mosquitto__callback *cb_base; if(!context || !context->listener || !context->auth_method) return MOSQ_ERR_INVAL; if(!data_out || !data_out_len) return MOSQ_ERR_INVAL; if(db.config->per_listener_settings){ opts = &context->listener->security_options; }else{ opts = &db.config->security_options; } DL_FOREACH(opts->plugin_callbacks.ext_auth_continue, cb_base){ memset(&event_data, 0, sizeof(event_data)); event_data.client = context; event_data.data_in = data_in; event_data.data_out = NULL; event_data.data_in_len = data_in_len; event_data.data_out_len = 0; rc = cb_base->cb(MOSQ_EVT_EXT_AUTH_CONTINUE, &event_data, cb_base->userdata); if(rc != MOSQ_ERR_PLUGIN_DEFER){ *data_out = event_data.data_out; *data_out_len = event_data.data_out_len; return rc; } } for(i=0; iauth_plugin_config_count; i++){ if(opts->auth_plugin_configs[i].plugin.auth_continue_v4){ *data_out = NULL; *data_out_len = 0; rc = opts->auth_plugin_configs[i].plugin.auth_continue_v4( opts->auth_plugin_configs[i].plugin.user_data, context, context->auth_method, data_in, data_in_len, data_out, data_out_len); if(rc == MOSQ_ERR_SUCCESS){ return MOSQ_ERR_SUCCESS; }else if(rc == MOSQ_ERR_AUTH_CONTINUE){ return MOSQ_ERR_AUTH_CONTINUE; }else if(rc != MOSQ_ERR_NOT_SUPPORTED){ return rc; } } } return MOSQ_ERR_NOT_SUPPORTED; } mosquitto-2.0.18/src/security_default.c000066400000000000000000001107631450213760600201700ustar00rootroot00000000000000/* Copyright (c) 2011-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "send_mosq.h" #include "misc_mosq.h" #include "util_mosq.h" static int aclfile__parse(struct mosquitto__security_options *security_opts); static int unpwd__file_parse(struct mosquitto__unpwd **unpwd, const char *password_file); static int acl__cleanup(bool reload); static int unpwd__cleanup(struct mosquitto__unpwd **unpwd, bool reload); static int psk__file_parse(struct mosquitto__unpwd **psk_id, const char *psk_file); #ifdef WITH_TLS static int pw__digest(const char *password, const unsigned char *salt, unsigned int salt_len, unsigned char *hash, unsigned int *hash_len, enum mosquitto_pwhash_type hashtype, int iterations); #endif static int mosquitto_unpwd_check_default(int event, void *event_data, void *userdata); static int mosquitto_acl_check_default(int event, void *event_data, void *userdata); int mosquitto_security_init_default(bool reload) { int rc; int i; char *pwf; char *pskf = NULL; UNUSED(reload); /* Configure plugin identifier */ if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ db.config->listeners[i].security_options.pid = mosquitto__calloc(1, sizeof(mosquitto_plugin_id_t)); if(db.config->listeners[i].security_options.pid == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } db.config->listeners[i].security_options.pid->listener = &db.config->listeners[i]; } }else{ db.config->security_options.pid = mosquitto__calloc(1, sizeof(mosquitto_plugin_id_t)); if(db.config->security_options.pid == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } } /* Load username/password data if required. */ if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ pwf = db.config->listeners[i].security_options.password_file; if(pwf){ rc = unpwd__file_parse(&db.config->listeners[i].security_options.unpwd, pwf); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error opening password file \"%s\".", pwf); return rc; } mosquitto_callback_register(db.config->listeners[i].security_options.pid, MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL, NULL); } } }else{ if(db.config->security_options.password_file){ pwf = db.config->security_options.password_file; if(pwf){ rc = unpwd__file_parse(&db.config->security_options.unpwd, pwf); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error opening password file \"%s\".", pwf); return rc; } } mosquitto_callback_register(db.config->security_options.pid, MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL, NULL); } } /* Load acl data if required. */ if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ if(db.config->listeners[i].security_options.acl_file){ rc = aclfile__parse(&db.config->listeners[i].security_options); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error opening acl file \"%s\".", db.config->listeners[i].security_options.acl_file); return rc; } mosquitto_callback_register(db.config->listeners[i].security_options.pid, MOSQ_EVT_ACL_CHECK, mosquitto_acl_check_default, NULL, NULL); } } }else{ if(db.config->security_options.acl_file){ rc = aclfile__parse(&db.config->security_options); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error opening acl file \"%s\".", db.config->security_options.acl_file); return rc; } mosquitto_callback_register(db.config->security_options.pid, MOSQ_EVT_ACL_CHECK, mosquitto_acl_check_default, NULL, NULL); } } /* Load psk data if required. */ if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ pskf = db.config->listeners[i].security_options.psk_file; if(pskf){ rc = psk__file_parse(&db.config->listeners[i].security_options.psk_id, pskf); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error opening psk file \"%s\".", pskf); return rc; } } } }else{ pskf = db.config->security_options.psk_file; if(pskf){ rc = psk__file_parse(&db.config->security_options.psk_id, pskf); if(rc){ log__printf(NULL, MOSQ_LOG_ERR, "Error opening psk file \"%s\".", pskf); return rc; } } } return MOSQ_ERR_SUCCESS; } int mosquitto_security_cleanup_default(bool reload) { int rc; int i; rc = acl__cleanup(reload); if(rc != MOSQ_ERR_SUCCESS) return rc; rc = unpwd__cleanup(&db.config->security_options.unpwd, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; for(i=0; ilistener_count; i++){ if(db.config->listeners[i].security_options.unpwd){ rc = unpwd__cleanup(&db.config->listeners[i].security_options.unpwd, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; } } rc = unpwd__cleanup(&db.config->security_options.psk_id, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; for(i=0; ilistener_count; i++){ if(db.config->listeners[i].security_options.psk_id){ rc = unpwd__cleanup(&db.config->listeners[i].security_options.psk_id, reload); if(rc != MOSQ_ERR_SUCCESS) return rc; } } if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ if(db.config->listeners[i].security_options.pid){ mosquitto_callback_unregister(db.config->listeners[i].security_options.pid, MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL); mosquitto_callback_unregister(db.config->listeners[i].security_options.pid, MOSQ_EVT_ACL_CHECK, mosquitto_acl_check_default, NULL); mosquitto__free(db.config->listeners[i].security_options.pid); } } }else{ if(db.config->security_options.pid){ mosquitto_callback_unregister(db.config->security_options.pid, MOSQ_EVT_BASIC_AUTH, mosquitto_unpwd_check_default, NULL); mosquitto_callback_unregister(db.config->security_options.pid, MOSQ_EVT_ACL_CHECK, mosquitto_acl_check_default, NULL); mosquitto__free(db.config->security_options.pid); } } return MOSQ_ERR_SUCCESS; } static int add__acl(struct mosquitto__security_options *security_opts, const char *user, const char *topic, int access) { struct mosquitto__acl_user *acl_user=NULL, *user_tail; struct mosquitto__acl *acl, *acl_tail; char *local_topic; bool new_user = false; if(!security_opts || !topic) return MOSQ_ERR_INVAL; local_topic = mosquitto__strdup(topic); if(!local_topic){ return MOSQ_ERR_NOMEM; } if(security_opts->acl_list){ user_tail = security_opts->acl_list; while(user_tail){ if(user == NULL){ if(user_tail->username == NULL){ acl_user = user_tail; break; } }else if(user_tail->username && !strcmp(user_tail->username, user)){ acl_user = user_tail; break; } user_tail = user_tail->next; } } if(!acl_user){ acl_user = mosquitto__malloc(sizeof(struct mosquitto__acl_user)); if(!acl_user){ mosquitto__free(local_topic); return MOSQ_ERR_NOMEM; } new_user = true; if(user){ acl_user->username = mosquitto__strdup(user); if(!acl_user->username){ mosquitto__free(local_topic); mosquitto__free(acl_user); return MOSQ_ERR_NOMEM; } }else{ acl_user->username = NULL; } acl_user->next = NULL; acl_user->acl = NULL; } acl = mosquitto__malloc(sizeof(struct mosquitto__acl)); if(!acl){ mosquitto__free(local_topic); mosquitto__free(acl_user->username); mosquitto__free(acl_user); return MOSQ_ERR_NOMEM; } acl->access = access; acl->topic = local_topic; acl->next = NULL; acl->ccount = 0; acl->ucount = 0; /* Add acl to user acl list */ if(acl_user->acl){ acl_tail = acl_user->acl; if(access == MOSQ_ACL_NONE){ /* Put "deny" acls at front of the list */ acl->next = acl_tail; acl_user->acl = acl; }else{ while(acl_tail->next){ acl_tail = acl_tail->next; } acl_tail->next = acl; } }else{ acl_user->acl = acl; } if(new_user){ /* Add to end of list */ if(security_opts->acl_list){ user_tail = security_opts->acl_list; while(user_tail->next){ user_tail = user_tail->next; } user_tail->next = acl_user; }else{ security_opts->acl_list = acl_user; } } return MOSQ_ERR_SUCCESS; } static int add__acl_pattern(struct mosquitto__security_options *security_opts, const char *topic, int access) { struct mosquitto__acl *acl, *acl_tail; char *local_topic; char *s; if(!security_opts| !topic) return MOSQ_ERR_INVAL; local_topic = mosquitto__strdup(topic); if(!local_topic){ return MOSQ_ERR_NOMEM; } acl = mosquitto__malloc(sizeof(struct mosquitto__acl)); if(!acl){ mosquitto__free(local_topic); return MOSQ_ERR_NOMEM; } acl->access = access; acl->topic = local_topic; acl->next = NULL; acl->ccount = 0; s = local_topic; while(s){ s = strstr(s, "%c"); if(s){ acl->ccount++; s+=2; } } acl->ucount = 0; s = local_topic; while(s){ s = strstr(s, "%u"); if(s){ acl->ucount++; s+=2; } } if(acl->ccount == 0 && acl->ucount == 0){ log__printf(NULL, MOSQ_LOG_WARNING, "Warning: ACL pattern '%s' does not contain '%%c' or '%%u'.", topic); } if(security_opts->acl_patterns){ acl_tail = security_opts->acl_patterns; if(access == MOSQ_ACL_NONE){ /* Put "deny" acls at front of the list */ acl->next = acl_tail; security_opts->acl_patterns = acl; }else{ while(acl_tail->next){ acl_tail = acl_tail->next; } acl_tail->next = acl; } }else{ security_opts->acl_patterns = acl; } return MOSQ_ERR_SUCCESS; } static int mosquitto_acl_check_default(int event, void *event_data, void *userdata) { struct mosquitto_evt_acl_check *ed = event_data; char *local_acl; struct mosquitto__acl *acl_root; bool result; size_t i; size_t len, tlen, clen, ulen; char *s; struct mosquitto__security_options *security_opts = NULL; UNUSED(event); UNUSED(userdata); if(ed->client->bridge) return MOSQ_ERR_SUCCESS; if(ed->access == MOSQ_ACL_SUBSCRIBE || ed->access == MOSQ_ACL_UNSUBSCRIBE) return MOSQ_ERR_SUCCESS; /* FIXME - implement ACL subscription strings. */ if(db.config->per_listener_settings){ if(!ed->client->listener) return MOSQ_ERR_ACL_DENIED; security_opts = &ed->client->listener->security_options; }else{ security_opts = &db.config->security_options; } if(!security_opts->acl_file && !security_opts->acl_list && !security_opts->acl_patterns){ return MOSQ_ERR_PLUGIN_DEFER; } if(!ed->client->acl_list && !security_opts->acl_patterns) return MOSQ_ERR_ACL_DENIED; if(ed->client->acl_list){ acl_root = ed->client->acl_list->acl; }else{ acl_root = NULL; } /* Loop through all ACLs for this client. ACL denials are iterated over first. */ while(acl_root){ /* Loop through the topic looking for matches to this ACL. */ /* If subscription starts with $, acl_root->topic must also start with $. */ if(ed->topic[0] == '$' && acl_root->topic[0] != '$'){ acl_root = acl_root->next; continue; } mosquitto_topic_matches_sub(acl_root->topic, ed->topic, &result); if(result){ if(acl_root->access == MOSQ_ACL_NONE){ /* Access was explicitly denied for this topic. */ return MOSQ_ERR_ACL_DENIED; } if(ed->access & acl_root->access){ /* And access is allowed. */ return MOSQ_ERR_SUCCESS; } } acl_root = acl_root->next; } acl_root = security_opts->acl_patterns; if(acl_root){ /* We are using pattern based acls. Check whether the username or * client id contains a + or # and if so deny access. * * Without this, a malicious client may configure its username/client * id to bypass ACL checks (or have a username/client id that cannot * publish or receive messages to its own place in the hierarchy). */ if(ed->client->username && strpbrk(ed->client->username, "+#")){ log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous username \"%s\"", ed->client->username); return MOSQ_ERR_ACL_DENIED; } if(ed->client->id && strpbrk(ed->client->id, "+#")){ log__printf(NULL, MOSQ_LOG_NOTICE, "ACL denying access to client with dangerous client id \"%s\"", ed->client->id); return MOSQ_ERR_ACL_DENIED; } } /* Loop through all pattern ACLs. ACL denial patterns are iterated over first. */ if(!ed->client->id) return MOSQ_ERR_ACL_DENIED; clen = strlen(ed->client->id); while(acl_root){ tlen = strlen(acl_root->topic); if(acl_root->ucount && !ed->client->username){ acl_root = acl_root->next; continue; } if(ed->client->username){ ulen = strlen(ed->client->username); len = tlen + (size_t)acl_root->ccount*(clen-2) + (size_t)acl_root->ucount*(ulen-2); }else{ ulen = 0; len = tlen + (size_t)acl_root->ccount*(clen-2); } local_acl = mosquitto__malloc(len+1); if(!local_acl) return MOSQ_ERR_NOMEM; s = local_acl; for(i=0; itopic[i] == '%'){ if(acl_root->topic[i+1] == 'c'){ i++; strncpy(s, ed->client->id, clen); s+=clen; continue; }else if(ed->client->username && acl_root->topic[i+1] == 'u'){ i++; strncpy(s, ed->client->username, ulen); s+=ulen; continue; } } s[0] = acl_root->topic[i]; s++; } local_acl[len] = '\0'; mosquitto_topic_matches_sub(local_acl, ed->topic, &result); mosquitto__free(local_acl); if(result){ if(acl_root->access == MOSQ_ACL_NONE){ /* Access was explicitly denied for this topic pattern. */ return MOSQ_ERR_ACL_DENIED; } if(ed->access & acl_root->access){ /* And access is allowed. */ return MOSQ_ERR_SUCCESS; } } acl_root = acl_root->next; } return MOSQ_ERR_ACL_DENIED; } static int aclfile__parse(struct mosquitto__security_options *security_opts) { FILE *aclfptr = NULL; char *token; char *user = NULL; char *topic; char *access_s; int access; int rc = MOSQ_ERR_SUCCESS; size_t slen; int topic_pattern; char *saveptr = NULL; char *buf = NULL; int buflen = 256; if(!db.config) return MOSQ_ERR_INVAL; if(!security_opts) return MOSQ_ERR_INVAL; if(!security_opts->acl_file) return MOSQ_ERR_SUCCESS; buf = mosquitto__malloc((size_t)buflen); if(buf == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } aclfptr = mosquitto__fopen(security_opts->acl_file, "rt", true); if(!aclfptr){ mosquitto__free(buf); log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open acl_file \"%s\".", security_opts->acl_file); return MOSQ_ERR_UNKNOWN; } /* topic [read|write] * user */ while(fgets_extending(&buf, &buflen, aclfptr)){ slen = strlen(buf); while(slen > 0 && isspace(buf[slen-1])){ buf[slen-1] = '\0'; slen = strlen(buf); } if(buf[0] == '#'){ continue; } token = strtok_r(buf, " ", &saveptr); if(token){ if(!strcmp(token, "topic") || !strcmp(token, "pattern")){ if(!strcmp(token, "topic")){ topic_pattern = 0; }else{ topic_pattern = 1; } access_s = strtok_r(NULL, " ", &saveptr); if(!access_s){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty topic in acl_file \"%s\".", security_opts->acl_file); rc = MOSQ_ERR_INVAL; break; } token = strtok_r(NULL, "", &saveptr); if(token){ topic = misc__trimblanks(token); }else{ topic = access_s; access_s = NULL; } if(access_s){ if(!strcmp(access_s, "read")){ access = MOSQ_ACL_READ; }else if(!strcmp(access_s, "write")){ access = MOSQ_ACL_WRITE; }else if(!strcmp(access_s, "readwrite")){ access = MOSQ_ACL_READ | MOSQ_ACL_WRITE; }else if(!strcmp(access_s, "deny")){ access = MOSQ_ACL_NONE; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid topic access type \"%s\" in acl_file \"%s\".", access_s, security_opts->acl_file); rc = MOSQ_ERR_INVAL; break; } }else{ access = MOSQ_ACL_READ | MOSQ_ACL_WRITE; } rc = mosquitto_sub_topic_check(topic); if(rc != MOSQ_ERR_SUCCESS){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid ACL topic \"%s\" in acl_file \"%s\".", topic, security_opts->acl_file); rc = MOSQ_ERR_INVAL; break; } if(topic_pattern == 0){ rc = add__acl(security_opts, user, topic, access); }else{ rc = add__acl_pattern(security_opts, topic, access); } if(rc){ break; } }else if(!strcmp(token, "user")){ token = strtok_r(NULL, "", &saveptr); if(token){ token = misc__trimblanks(token); if(slen == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing username in acl_file \"%s\".", security_opts->acl_file); rc = MOSQ_ERR_INVAL; break; } mosquitto__free(user); user = mosquitto__strdup(token); if(!user){ rc = MOSQ_ERR_NOMEM; break; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing username in acl_file \"%s\".", security_opts->acl_file); rc = MOSQ_ERR_INVAL; break; } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid line in acl_file \"%s\": %s.", security_opts->acl_file, buf); rc = MOSQ_ERR_INVAL; break; } } } mosquitto__free(buf); mosquitto__free(user); fclose(aclfptr); return rc; } static void free__acl(struct mosquitto__acl *acl) { if(!acl) return; if(acl->next){ free__acl(acl->next); } mosquitto__free(acl->topic); mosquitto__free(acl); } static void acl__cleanup_single(struct mosquitto__security_options *security_opts) { struct mosquitto__acl_user *user_tail; while(security_opts->acl_list){ user_tail = security_opts->acl_list->next; free__acl(security_opts->acl_list->acl); mosquitto__free(security_opts->acl_list->username); mosquitto__free(security_opts->acl_list); security_opts->acl_list = user_tail; } if(security_opts->acl_patterns){ free__acl(security_opts->acl_patterns); security_opts->acl_patterns = NULL; } } static int acl__cleanup(bool reload) { struct mosquitto *context, *ctxt_tmp = NULL; int i; UNUSED(reload); /* As we're freeing ACLs, we must clear context->acl_list to ensure no * invalid memory accesses take place later. * This *requires* the ACLs to be reapplied after acl__cleanup() * is called if we are reloading the config. If this is not done, all * access will be denied to currently connected clients. */ HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){ context->acl_list = NULL; } if(db.config->per_listener_settings){ for(i=0; ilistener_count; i++){ acl__cleanup_single(&db.config->listeners[i].security_options); } }else{ acl__cleanup_single(&db.config->security_options); } return MOSQ_ERR_SUCCESS; } int acl__find_acls(struct mosquitto *context) { struct mosquitto__acl_user *acl_tail; struct mosquitto__security_options *security_opts; /* Associate user with its ACL, assuming we have ACLs loaded. */ if(db.config->per_listener_settings){ if(!context->listener){ return MOSQ_ERR_INVAL; } security_opts = &context->listener->security_options; }else{ security_opts = &db.config->security_options; } if(security_opts->acl_list){ acl_tail = security_opts->acl_list; while(acl_tail){ if(context->username){ if(acl_tail->username && !strcmp(context->username, acl_tail->username)){ context->acl_list = acl_tail; break; } }else{ if(acl_tail->username == NULL){ context->acl_list = acl_tail; break; } } acl_tail = acl_tail->next; } }else{ context->acl_list = NULL; } return MOSQ_ERR_SUCCESS; } static int pwfile__parse(const char *file, struct mosquitto__unpwd **root) { FILE *pwfile; struct mosquitto__unpwd *unpwd; char *username, *password; char *saveptr = NULL; char *buf; int buflen = 256; buf = mosquitto__malloc((size_t)buflen); if(buf == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } pwfile = mosquitto__fopen(file, "rt", true); if(!pwfile){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open pwfile \"%s\".", file); mosquitto__free(buf); return MOSQ_ERR_UNKNOWN; } while(!feof(pwfile)){ if(fgets_extending(&buf, &buflen, pwfile)){ if(buf[0] == '#') continue; if(!strchr(buf, ':')) continue; username = strtok_r(buf, ":", &saveptr); if(username){ unpwd = mosquitto__calloc(1, sizeof(struct mosquitto__unpwd)); if(!unpwd){ fclose(pwfile); mosquitto__free(buf); return MOSQ_ERR_NOMEM; } username = misc__trimblanks(username); if(strlen(username) > 65535){ log__printf(NULL, MOSQ_LOG_NOTICE, "Warning: Invalid line in password file '%s', username too long.", file); mosquitto__free(unpwd); continue; } unpwd->username = mosquitto__strdup(username); if(!unpwd->username){ mosquitto__free(unpwd); mosquitto__free(buf); fclose(pwfile); return MOSQ_ERR_NOMEM; } password = strtok_r(NULL, ":", &saveptr); if(password){ password = misc__trimblanks(password); if(strlen(password) > 65535){ log__printf(NULL, MOSQ_LOG_NOTICE, "Warning: Invalid line in password file '%s', password too long.", file); mosquitto__free(unpwd->username); mosquitto__free(unpwd); continue; } unpwd->password = mosquitto__strdup(password); if(!unpwd->password){ fclose(pwfile); mosquitto__free(unpwd->username); mosquitto__free(unpwd); mosquitto__free(buf); return MOSQ_ERR_NOMEM; } HASH_ADD_KEYPTR(hh, *root, unpwd->username, strlen(unpwd->username), unpwd); }else{ log__printf(NULL, MOSQ_LOG_NOTICE, "Warning: Invalid line in password file '%s': %s", file, buf); mosquitto__free(unpwd->username); mosquitto__free(unpwd); } } } } fclose(pwfile); mosquitto__free(buf); return MOSQ_ERR_SUCCESS; } void unpwd__free_item(struct mosquitto__unpwd **unpwd, struct mosquitto__unpwd *item) { mosquitto__free(item->username); mosquitto__free(item->password); #ifdef WITH_TLS mosquitto__free(item->salt); #endif HASH_DEL(*unpwd, item); mosquitto__free(item); } #ifdef WITH_TLS static int unpwd__decode_passwords(struct mosquitto__unpwd **unpwd) { struct mosquitto__unpwd *u, *tmp = NULL; char *token; unsigned char *salt; unsigned int salt_len; unsigned char *password; unsigned int password_len; int rc; enum mosquitto_pwhash_type hashtype; HASH_ITER(hh, *unpwd, u, tmp){ /* Need to decode password into hashed data + salt. */ if(u->password == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Missing password hash for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); continue; } token = strtok(u->password, "$"); if(token == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); continue; } if(!strcmp(token, "6")){ hashtype = pw_sha512; }else if(!strcmp(token, "7")){ hashtype = pw_sha512_pbkdf2; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash type for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); continue; } if(hashtype == pw_sha512_pbkdf2){ token = strtok(NULL, "$"); if(token == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); continue; } u->iterations = atoi(token); if(u->iterations < 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid hash iterations for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); continue; } } token = strtok(NULL, "$"); if(token == NULL){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); continue; } rc = base64__decode(token, &salt, &salt_len); if(rc == MOSQ_ERR_SUCCESS && salt_len == 12){ u->salt = salt; u->salt_len = salt_len; token = strtok(NULL, "$"); if(token){ rc = base64__decode(token, &password, &password_len); if(rc == MOSQ_ERR_SUCCESS && password_len == 64){ mosquitto__free(u->password); u->password = (char *)password; u->password_len = password_len; u->hashtype = hashtype; }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Invalid password hash for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); } }else{ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to decode password salt for user %s, removing entry.", u->username); unpwd__free_item(unpwd, u); } } return MOSQ_ERR_SUCCESS; } #endif static int unpwd__file_parse(struct mosquitto__unpwd **unpwd, const char *password_file) { int rc; if(!unpwd) return MOSQ_ERR_INVAL; if(!password_file) return MOSQ_ERR_SUCCESS; rc = pwfile__parse(password_file, unpwd); #ifdef WITH_TLS if(rc) return rc; rc = unpwd__decode_passwords(unpwd); #endif return rc; } static int psk__file_parse(struct mosquitto__unpwd **psk_id, const char *psk_file) { int rc; struct mosquitto__unpwd *u, *tmp = NULL; if(!db.config || !psk_id) return MOSQ_ERR_INVAL; /* We haven't been asked to parse a psk file. */ if(!psk_file) return MOSQ_ERR_SUCCESS; rc = pwfile__parse(psk_file, psk_id); if(rc) return rc; HASH_ITER(hh, (*psk_id), u, tmp){ /* Check for hex only digits */ if(!u->password){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Empty psk for identity \"%s\".", u->username); return MOSQ_ERR_INVAL; } if(strspn(u->password, "0123456789abcdefABCDEF") < strlen(u->password)){ log__printf(NULL, MOSQ_LOG_ERR, "Error: psk for identity \"%s\" contains non-hexadecimal characters.", u->username); return MOSQ_ERR_INVAL; } } return MOSQ_ERR_SUCCESS; } #ifdef WITH_TLS static int mosquitto__memcmp_const(const void *a, const void *b, size_t len) { size_t i; int rc = 0; if(!a || !b) return 1; for(i=0; iclient->username == NULL){ return MOSQ_ERR_PLUGIN_DEFER; } if(db.config->per_listener_settings){ if(ed->client->bridge) return MOSQ_ERR_SUCCESS; if(!ed->client->listener) return MOSQ_ERR_INVAL; unpwd_ref = ed->client->listener->security_options.unpwd; }else{ unpwd_ref = db.config->security_options.unpwd; } HASH_FIND(hh, unpwd_ref, ed->client->username, strlen(ed->client->username), u); if(u){ if(u->password){ if(ed->client->password){ #ifdef WITH_TLS rc = pw__digest(ed->client->password, u->salt, u->salt_len, hash, &hash_len, u->hashtype, u->iterations); if(rc == MOSQ_ERR_SUCCESS){ if(hash_len == u->password_len && !mosquitto__memcmp_const(u->password, hash, hash_len)){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } }else{ return rc; } #else if(!strcmp(u->password, ed->client->password)){ return MOSQ_ERR_SUCCESS; } #endif }else{ return MOSQ_ERR_AUTH; } }else{ return MOSQ_ERR_SUCCESS; } } return MOSQ_ERR_AUTH; } static int unpwd__cleanup(struct mosquitto__unpwd **root, bool reload) { struct mosquitto__unpwd *u, *tmp = NULL; UNUSED(reload); if(!root) return MOSQ_ERR_INVAL; HASH_ITER(hh, *root, u, tmp){ HASH_DEL(*root, u); mosquitto__free(u->password); mosquitto__free(u->username); #ifdef WITH_TLS mosquitto__free(u->salt); #endif mosquitto__free(u); } *root = NULL; return MOSQ_ERR_SUCCESS; } #ifdef WITH_TLS static void security__disconnect_auth(struct mosquitto *context) { if(context->protocol == mosq_p_mqtt5){ send__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL); } mosquitto__set_state(context, mosq_cs_disconnecting); do_disconnect(context, MOSQ_ERR_AUTH); } #endif /* Apply security settings after a reload. * Includes: * - Disconnecting anonymous users if appropriate * - Disconnecting users with invalid passwords * - Reapplying ACLs */ int mosquitto_security_apply_default(void) { struct mosquitto *context, *ctxt_tmp = NULL; struct mosquitto__acl_user *acl_user_tail; bool allow_anonymous; struct mosquitto__security_options *security_opts = NULL; #ifdef WITH_TLS int i; X509 *client_cert = NULL; X509_NAME *name; X509_NAME_ENTRY *name_entry; ASN1_STRING *name_asn1 = NULL; struct mosquitto__listener *listener; BIO *subject_bio; char *data_start; size_t name_length; char *subject; #endif #ifdef WITH_TLS for(i=0; ilistener_count; i++){ listener = &db.config->listeners[i]; if(listener && listener->ssl_ctx && listener->certfile && listener->keyfile && listener->crlfile && listener->require_certificate){ if(net__tls_server_ctx(listener)){ return MOSQ_ERR_TLS; } if(net__tls_load_verify(listener)){ return MOSQ_ERR_TLS; } } } #endif HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){ if(context->bridge){ continue; } /* Check for anonymous clients when allow_anonymous is false */ if(db.config->per_listener_settings){ if(context->listener){ allow_anonymous = context->listener->security_options.allow_anonymous; }else{ /* Client not currently connected, so defer judgement until it does connect */ allow_anonymous = true; } }else{ allow_anonymous = db.config->security_options.allow_anonymous; } if(!allow_anonymous && !context->username){ mosquitto__set_state(context, mosq_cs_disconnecting); do_disconnect(context, MOSQ_ERR_AUTH); continue; } /* Check for connected clients that are no longer authorised */ #ifdef WITH_TLS if(context->listener && context->listener->ssl_ctx && (context->listener->use_identity_as_username || context->listener->use_subject_as_username)){ /* Client must have either a valid certificate, or valid PSK used as a username. */ if(!context->ssl){ if(context->protocol == mosq_p_mqtt5){ send__disconnect(context, MQTT_RC_ADMINISTRATIVE_ACTION, NULL); } mosquitto__set_state(context, mosq_cs_disconnecting); do_disconnect(context, MOSQ_ERR_AUTH); continue; } #ifdef FINAL_WITH_TLS_PSK if(context->listener->psk_hint){ /* Client should have provided an identity to get this far. */ if(!context->username){ security__disconnect_auth(context); continue; } }else #endif /* FINAL_WITH_TLS_PSK */ { /* Free existing credentials and then recover them. */ mosquitto__free(context->username); context->username = NULL; mosquitto__free(context->password); context->password = NULL; client_cert = SSL_get_peer_certificate(context->ssl); if(!client_cert){ security__disconnect_auth(context); continue; } name = X509_get_subject_name(client_cert); if(!name){ X509_free(client_cert); client_cert = NULL; security__disconnect_auth(context); continue; } if (context->listener->use_identity_as_username) { /* use_identity_as_username */ i = X509_NAME_get_index_by_NID(name, NID_commonName, -1); if(i == -1){ X509_free(client_cert); client_cert = NULL; security__disconnect_auth(context); continue; } name_entry = X509_NAME_get_entry(name, i); if(name_entry){ name_asn1 = X509_NAME_ENTRY_get_data(name_entry); if (name_asn1 == NULL) { X509_free(client_cert); client_cert = NULL; security__disconnect_auth(context); continue; } #if OPENSSL_VERSION_NUMBER < 0x10100000L context->username = mosquitto__strdup((char *) ASN1_STRING_data(name_asn1)); #else context->username = mosquitto__strdup((char *) ASN1_STRING_get0_data(name_asn1)); #endif if(!context->username){ X509_free(client_cert); client_cert = NULL; security__disconnect_auth(context); continue; } /* Make sure there isn't an embedded NUL character in the CN */ if ((size_t)ASN1_STRING_length(name_asn1) != strlen(context->username)) { X509_free(client_cert); client_cert = NULL; security__disconnect_auth(context); continue; } } } else { /* use_subject_as_username */ subject_bio = BIO_new(BIO_s_mem()); X509_NAME_print_ex(subject_bio, X509_get_subject_name(client_cert), 0, XN_FLAG_RFC2253); data_start = NULL; name_length = (size_t)BIO_get_mem_data(subject_bio, &data_start); subject = mosquitto__malloc(sizeof(char)*name_length+1); if(!subject){ BIO_free(subject_bio); X509_free(client_cert); client_cert = NULL; security__disconnect_auth(context); continue; } memcpy(subject, data_start, name_length); subject[name_length] = '\0'; BIO_free(subject_bio); context->username = subject; } if(!context->username){ X509_free(client_cert); client_cert = NULL; security__disconnect_auth(context); continue; } X509_free(client_cert); client_cert = NULL; } }else #endif { /* Username/password check only if the identity/subject check not used */ if(mosquitto_unpwd_check(context) != MOSQ_ERR_SUCCESS){ mosquitto__set_state(context, mosq_cs_disconnecting); do_disconnect(context, MOSQ_ERR_AUTH); continue; } } /* Check for ACLs and apply to user. */ if(db.config->per_listener_settings){ if(context->listener){ security_opts = &context->listener->security_options; }else{ if(context->state != mosq_cs_active){ mosquitto__set_state(context, mosq_cs_disconnecting); do_disconnect(context, MOSQ_ERR_AUTH); continue; } } }else{ security_opts = &db.config->security_options; } if(security_opts && security_opts->acl_list){ acl_user_tail = security_opts->acl_list; while(acl_user_tail){ if(acl_user_tail->username){ if(context->username){ if(!strcmp(acl_user_tail->username, context->username)){ context->acl_list = acl_user_tail; break; } } }else{ if(!context->username){ context->acl_list = acl_user_tail; break; } } acl_user_tail = acl_user_tail->next; } } } return MOSQ_ERR_SUCCESS; } int mosquitto_psk_key_get_default(struct mosquitto *context, const char *hint, const char *identity, char *key, int max_key_len) { struct mosquitto__unpwd *u, *tmp = NULL; struct mosquitto__unpwd *psk_id_ref = NULL; if(!hint || !identity || !key) return MOSQ_ERR_INVAL; if(db.config->per_listener_settings){ if(!context->listener) return MOSQ_ERR_INVAL; psk_id_ref = context->listener->security_options.psk_id; }else{ psk_id_ref = db.config->security_options.psk_id; } if(!psk_id_ref) return MOSQ_ERR_PLUGIN_DEFER; HASH_ITER(hh, psk_id_ref, u, tmp){ if(!strcmp(u->username, identity)){ strncpy(key, u->password, (size_t)max_key_len); return MOSQ_ERR_SUCCESS; } } return MOSQ_ERR_AUTH; } #ifdef WITH_TLS int pw__digest(const char *password, const unsigned char *salt, unsigned int salt_len, unsigned char *hash, unsigned int *hash_len, enum mosquitto_pwhash_type hashtype, int iterations) { const EVP_MD *digest; #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX context; #else EVP_MD_CTX *context; #endif digest = EVP_get_digestbyname("sha512"); if(!digest){ /* FIXME fprintf(stderr, "Error: Unable to create openssl digest.\n"); */ return 1; } if(hashtype == pw_sha512){ #if OPENSSL_VERSION_NUMBER < 0x10100000L EVP_MD_CTX_init(&context); EVP_DigestInit_ex(&context, digest, NULL); EVP_DigestUpdate(&context, password, strlen(password)); EVP_DigestUpdate(&context, salt, salt_len); /* hash is assumed to be EVP_MAX_MD_SIZE bytes long. */ EVP_DigestFinal_ex(&context, hash, hash_len); EVP_MD_CTX_cleanup(&context); #else context = EVP_MD_CTX_new(); EVP_DigestInit_ex(context, digest, NULL); EVP_DigestUpdate(context, password, strlen(password)); EVP_DigestUpdate(context, salt, salt_len); /* hash is assumed to be EVP_MAX_MD_SIZE bytes long. */ EVP_DigestFinal_ex(context, hash, hash_len); EVP_MD_CTX_free(context); #endif }else{ *hash_len = EVP_MAX_MD_SIZE; PKCS5_PBKDF2_HMAC(password, (int)strlen(password), salt, (int)salt_len, iterations, digest, (int)(*hash_len), hash); } return MOSQ_ERR_SUCCESS; } #endif mosquitto-2.0.18/src/send_auth.c000066400000000000000000000045241450213760600165640ustar00rootroot00000000000000/* Copyright (c) 2019-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "util_mosq.h" int send__auth(struct mosquitto *context, uint8_t reason_code, const void *auth_data, uint16_t auth_data_len) { struct mosquitto__packet *packet = NULL; int rc; mosquitto_property *properties = NULL; uint32_t remaining_length; if(context->auth_method == NULL) return MOSQ_ERR_INVAL; if(context->protocol != mosq_p_mqtt5) return MOSQ_ERR_PROTOCOL; log__printf(NULL, MOSQ_LOG_DEBUG, "Sending AUTH to %s (rc%d, %s)", context->id, reason_code, context->auth_method); remaining_length = 1; rc = mosquitto_property_add_string(&properties, MQTT_PROP_AUTHENTICATION_METHOD, context->auth_method); if(rc){ mosquitto_property_free_all(&properties); return rc; } if(auth_data != NULL && auth_data_len > 0){ rc = mosquitto_property_add_binary(&properties, MQTT_PROP_AUTHENTICATION_DATA, auth_data, auth_data_len); if(rc){ mosquitto_property_free_all(&properties); return rc; } } remaining_length += property__get_remaining_length(properties); if(packet__check_oversize(context, remaining_length)){ mosquitto_property_free_all(&properties); mosquitto__free(packet); return MOSQ_ERR_OVERSIZE_PACKET; } packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->command = CMD_AUTH; packet->remaining_length = remaining_length; rc = packet__alloc(packet); if(rc){ mosquitto_property_free_all(&properties); mosquitto__free(packet); return rc; } packet__write_byte(packet, reason_code); property__write_all(packet, properties, true); mosquitto_property_free_all(&properties); return packet__queue(context, packet); } mosquitto-2.0.18/src/send_connack.c000066400000000000000000000062331450213760600172360ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "util_mosq.h" int send__connack(struct mosquitto *context, uint8_t ack, uint8_t reason_code, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int rc; mosquitto_property *connack_props = NULL; uint32_t remaining_length; rc = mosquitto_property_copy_all(&connack_props, properties); if(rc){ return rc; } if(context->id){ log__printf(NULL, MOSQ_LOG_DEBUG, "Sending CONNACK to %s (%d, %d)", context->id, ack, reason_code); }else{ log__printf(NULL, MOSQ_LOG_DEBUG, "Sending CONNACK to %s (%d, %d)", context->address, ack, reason_code); } remaining_length = 2; if(context->protocol == mosq_p_mqtt5){ if(reason_code < 128 && db.config->retain_available == false){ rc = mosquitto_property_add_byte(&connack_props, MQTT_PROP_RETAIN_AVAILABLE, 0); if(rc){ mosquitto_property_free_all(&connack_props); return rc; } } if(reason_code < 128 && db.config->max_packet_size > 0){ rc = mosquitto_property_add_int32(&connack_props, MQTT_PROP_MAXIMUM_PACKET_SIZE, db.config->max_packet_size); if(rc){ mosquitto_property_free_all(&connack_props); return rc; } } if(reason_code < 128 && db.config->max_inflight_messages > 0){ rc = mosquitto_property_add_int16(&connack_props, MQTT_PROP_RECEIVE_MAXIMUM, db.config->max_inflight_messages); if(rc){ mosquitto_property_free_all(&connack_props); return rc; } } if(context->listener->max_qos != 2){ rc = mosquitto_property_add_byte(&connack_props, MQTT_PROP_MAXIMUM_QOS, context->listener->max_qos); if(rc){ mosquitto_property_free_all(&connack_props); return rc; } } remaining_length += property__get_remaining_length(connack_props); } if(packet__check_oversize(context, remaining_length)){ mosquitto_property_free_all(&connack_props); return MOSQ_ERR_OVERSIZE_PACKET; } packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet){ mosquitto_property_free_all(&connack_props); return MOSQ_ERR_NOMEM; } packet->command = CMD_CONNACK; packet->remaining_length = remaining_length; rc = packet__alloc(packet); if(rc){ mosquitto_property_free_all(&connack_props); mosquitto__free(packet); return rc; } packet__write_byte(packet, ack); packet__write_byte(packet, reason_code); if(context->protocol == mosq_p_mqtt5){ property__write_all(packet, connack_props, true); } mosquitto_property_free_all(&connack_props); return packet__queue(context, packet); } mosquitto-2.0.18/src/send_suback.c000066400000000000000000000032661450213760600170750ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" #include "util_mosq.h" int send__suback(struct mosquitto *context, uint16_t mid, uint32_t payloadlen, const void *payload) { struct mosquitto__packet *packet = NULL; int rc; mosquitto_property *properties = NULL; log__printf(NULL, MOSQ_LOG_DEBUG, "Sending SUBACK to %s", context->id); packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->command = CMD_SUBACK; packet->remaining_length = 2+payloadlen; if(context->protocol == mosq_p_mqtt5){ packet->remaining_length += property__get_remaining_length(properties); } rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } packet__write_uint16(packet, mid); if(context->protocol == mosq_p_mqtt5){ /* We don't use Reason String or User Property yet. */ property__write_all(packet, properties, true); } if(payloadlen){ packet__write_bytes(packet, payload, payloadlen); } return packet__queue(context, packet); } mosquitto-2.0.18/src/send_unsuback.c000066400000000000000000000031641450213760600174350ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "property_mosq.h" int send__unsuback(struct mosquitto *mosq, uint16_t mid, int reason_code_count, uint8_t *reason_codes, const mosquitto_property *properties) { struct mosquitto__packet *packet = NULL; int rc; assert(mosq); packet = mosquitto__calloc(1, sizeof(struct mosquitto__packet)); if(!packet) return MOSQ_ERR_NOMEM; packet->command = CMD_UNSUBACK; packet->remaining_length = 2; if(mosq->protocol == mosq_p_mqtt5){ packet->remaining_length += property__get_remaining_length(properties); packet->remaining_length += (uint32_t)reason_code_count; } rc = packet__alloc(packet); if(rc){ mosquitto__free(packet); return rc; } packet__write_uint16(packet, mid); if(mosq->protocol == mosq_p_mqtt5){ property__write_all(packet, properties, true); packet__write_bytes(packet, reason_codes, (uint32_t)reason_code_count); } return packet__queue(mosq, packet); } mosquitto-2.0.18/src/service.c000066400000000000000000000107421450213760600162510ustar00rootroot00000000000000/* Copyright (c) 2011-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #if defined(WIN32) || defined(__CYGWIN__) #include "config.h" #include #include "memory_mosq.h" extern int run; SERVICE_STATUS_HANDLE service_handle = 0; static SERVICE_STATUS service_status; int main(int argc, char *argv[]); static void print_error(void) { char *buf = NULL; FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), LANG_NEUTRAL, (LPTSTR)&buf, 0, NULL); fprintf(stderr, "Error: %s\n", buf); LocalFree(buf); } /* Service control callback */ void __stdcall service_handler(DWORD fdwControl) { switch(fdwControl){ case SERVICE_CONTROL_CONTINUE: /* Continue from Paused state. */ break; case SERVICE_CONTROL_PAUSE: /* Pause service. */ break; case SERVICE_CONTROL_SHUTDOWN: /* System is shutting down. */ case SERVICE_CONTROL_STOP: /* Service should stop. */ service_status.dwCurrentState = SERVICE_STOP_PENDING; SetServiceStatus(service_handle, &service_status); run = 0; break; } } /* Function called when started as a service. */ void __stdcall service_main(DWORD dwArgc, LPTSTR *lpszArgv) { char **argv; int argc = 1; char conf_path[MAX_PATH + 20]; int rc; UNUSED(dwArgc); UNUSED(lpszArgv); service_handle = RegisterServiceCtrlHandler("mosquitto", service_handler); if(service_handle){ memset(conf_path, 0, sizeof(conf_path)); rc = GetEnvironmentVariable("MOSQUITTO_DIR", conf_path, MAX_PATH); if(!rc || rc == MAX_PATH){ service_status.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(service_handle, &service_status); return; } strcat(conf_path, "/mosquitto.conf"); argv = mosquitto__malloc(sizeof(char *)*3); argv[0] = "mosquitto"; argv[1] = "-c"; argv[2] = conf_path; argc = 3; service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_STOP; service_status.dwWin32ExitCode = NO_ERROR; service_status.dwCheckPoint = 0; SetServiceStatus(service_handle, &service_status); main(argc, argv); mosquitto__free(argv); service_status.dwCurrentState = SERVICE_STOPPED; SetServiceStatus(service_handle, &service_status); } } void service_install(void) { SC_HANDLE sc_manager, svc_handle; char service_string[MAX_PATH + 20]; char exe_path[MAX_PATH + 1]; SERVICE_DESCRIPTION svc_desc; memset(exe_path, 0, sizeof(exe_path)); if(GetModuleFileName(NULL, exe_path, MAX_PATH) == MAX_PATH){ fprintf(stderr, "Error: Path too long.\n"); return; } snprintf(service_string, sizeof(service_string), "\"%s\" run", exe_path); sc_manager = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE); if(sc_manager){ svc_handle = CreateService(sc_manager, "mosquitto", "Mosquitto Broker", SERVICE_START | SERVICE_STOP | SERVICE_CHANGE_CONFIG, SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, service_string, NULL, NULL, NULL, NULL, NULL); if(svc_handle){ svc_desc.lpDescription = "Eclipse Mosquitto MQTT v5/v3.1.1 broker"; ChangeServiceConfig2(svc_handle, SERVICE_CONFIG_DESCRIPTION, &svc_desc); CloseServiceHandle(svc_handle); }else{ print_error(); } CloseServiceHandle(sc_manager); } else { print_error(); } } void service_uninstall(void) { SC_HANDLE sc_manager, svc_handle; SERVICE_STATUS status; sc_manager = OpenSCManager(NULL, SERVICES_ACTIVE_DATABASE, SC_MANAGER_CONNECT); if(sc_manager){ svc_handle = OpenService(sc_manager, "mosquitto", SERVICE_QUERY_STATUS | DELETE); if(svc_handle){ if(QueryServiceStatus(svc_handle, &status)){ if(status.dwCurrentState == SERVICE_STOPPED){ DeleteService(svc_handle); } } CloseServiceHandle(svc_handle); }else{ print_error(); } CloseServiceHandle(sc_manager); }else{ print_error(); } } void service_run(void) { SERVICE_TABLE_ENTRY ste[] = { { "mosquitto", service_main }, { NULL, NULL } }; StartServiceCtrlDispatcher(ste); } #endif mosquitto-2.0.18/src/session_expiry.c000066400000000000000000000112351450213760600176720ustar00rootroot00000000000000/* Copyright (c) 2019-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "sys_tree.h" #include "time_mosq.h" static struct session_expiry_list *expiry_list = NULL; static time_t last_check = 0; static int session_expiry__cmp(struct session_expiry_list *i1, struct session_expiry_list *i2) { if(i1->context->session_expiry_time == i2->context->session_expiry_time){ return 0; }else if(i1->context->session_expiry_time > i2->context->session_expiry_time){ return 1; }else{ return -1; } } static void set_session_expiry_time(struct mosquitto *context) { context->session_expiry_time = db.now_real_s; if(db.config->persistent_client_expiration == 0){ /* No global expiry, so use the client expiration interval */ context->session_expiry_time += context->session_expiry_interval; }else{ /* We have a global expiry interval */ if(db.config->persistent_client_expiration < context->session_expiry_interval){ /* The client expiry is longer than the global expiry, so use the global */ context->session_expiry_time += db.config->persistent_client_expiration; }else{ /* The global expiry is longer than the client expiry, so use the client */ context->session_expiry_time += context->session_expiry_interval; } } } int session_expiry__add(struct mosquitto *context) { struct session_expiry_list *item; if(db.config->persistent_client_expiration == 0){ if(context->session_expiry_interval == UINT32_MAX){ /* There isn't a global expiry set, and the client has asked to * never expire, so we don't add it to the list. */ return MOSQ_ERR_SUCCESS; } } item = mosquitto__calloc(1, sizeof(struct session_expiry_list)); if(!item) return MOSQ_ERR_NOMEM; item->context = context; set_session_expiry_time(item->context); context->expiry_list_item = item; DL_INSERT_INORDER(expiry_list, item, session_expiry__cmp); return MOSQ_ERR_SUCCESS; } int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time) { struct session_expiry_list *item; if(db.config->persistent_client_expiration == 0){ if(context->session_expiry_interval == UINT32_MAX){ /* There isn't a global expiry set, and the client has asked to * never expire, so we don't add it to the list. */ return MOSQ_ERR_SUCCESS; } } item = mosquitto__calloc(1, sizeof(struct session_expiry_list)); if(!item) return MOSQ_ERR_NOMEM; item->context = context; if(expiry_time){ item->context->session_expiry_time = expiry_time; }else{ set_session_expiry_time(item->context); } context->expiry_list_item = item; DL_INSERT_INORDER(expiry_list, item, session_expiry__cmp); return MOSQ_ERR_SUCCESS; } void session_expiry__remove(struct mosquitto *context) { if(context->expiry_list_item){ DL_DELETE(expiry_list, context->expiry_list_item); mosquitto__free(context->expiry_list_item); context->expiry_list_item = NULL; } } /* Call on broker shutdown only */ void session_expiry__remove_all(void) { struct session_expiry_list *item, *tmp; struct mosquitto *context; DL_FOREACH_SAFE(expiry_list, item, tmp){ context = item->context; session_expiry__remove(context); context->session_expiry_interval = 0; context->will_delay_interval = 0; will_delay__remove(context); context__disconnect(context); } } void session_expiry__check(void) { struct session_expiry_list *item, *tmp; struct mosquitto *context; if(db.now_real_s <= last_check) return; last_check = db.now_real_s; DL_FOREACH_SAFE(expiry_list, item, tmp){ if(item->context->session_expiry_time < db.now_real_s){ context = item->context; session_expiry__remove(context); if(context->id){ log__printf(NULL, MOSQ_LOG_NOTICE, "Expiring client %s due to timeout.", context->id); } G_CLIENTS_EXPIRED_INC(); /* Session has now expired, so clear interval */ context->session_expiry_interval = 0; /* Session has expired, so will delay should be cleared. */ context->will_delay_interval = 0; will_delay__remove(context); context__send_will(context); context__add_to_disused(context); }else{ return; } } } mosquitto-2.0.18/src/signals.c000066400000000000000000000053341450213760600162520ustar00rootroot00000000000000/* Copyright (c) 2016-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. Dmitry Kaukov - windows named events implementation. */ #ifdef WIN32 # ifndef WIN32_LEAN_AND_MEAN # define WIN32_LEAN_AND_MEAN # endif # include #endif #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #ifdef WITH_PERSISTENCE extern bool flag_db_backup; #endif extern bool flag_reload; extern bool flag_tree_print; extern int run; #ifdef SIGHUP /* Signal handler for SIGHUP - flag a config reload. */ void handle_sighup(int signal) { UNUSED(signal); flag_reload = true; } #endif /* Signal handler for SIGINT and SIGTERM - just stop gracefully. */ void handle_sigint(int signal) { UNUSED(signal); run = 0; } /* Signal handler for SIGUSR1 - backup the db. */ void handle_sigusr1(int signal) { UNUSED(signal); #ifdef WITH_PERSISTENCE flag_db_backup = true; #endif } /* Signal handler for SIGUSR2 - print subscription / retained tree. */ void handle_sigusr2(int signal) { UNUSED(signal); flag_tree_print = true; } /* * * Signalling mosquitto process on Win32. * * On Windows we we can use named events to pass signals to the mosquitto process. * List of events : * * mosqPID_shutdown * mosqPID_reload * mosqPID_backup * * (where PID is the PID of the mosquitto process). */ #ifdef WIN32 DWORD WINAPI SigThreadProc(void* data) { TCHAR evt_name[MAX_PATH]; static HANDLE evt[3]; int pid = GetCurrentProcessId(); UNUSED(data); sprintf_s(evt_name, MAX_PATH, "mosq%d_shutdown", pid); evt[0] = CreateEvent(NULL, TRUE, FALSE, evt_name); sprintf_s(evt_name, MAX_PATH, "mosq%d_reload", pid); evt[1] = CreateEvent(NULL, FALSE, FALSE, evt_name); sprintf_s(evt_name, MAX_PATH, "mosq%d_backup", pid); evt[2] = CreateEvent(NULL, FALSE, FALSE, evt_name); while (true) { int wr = WaitForMultipleObjects(sizeof(evt) / sizeof(HANDLE), evt, FALSE, INFINITE); switch (wr) { case WAIT_OBJECT_0 + 0: handle_sigint(SIGINT); break; case WAIT_OBJECT_0 + 1: flag_reload = true; continue; case WAIT_OBJECT_0 + 2: handle_sigusr1(0); continue; break; } } CloseHandle(evt[0]); CloseHandle(evt[1]); CloseHandle(evt[2]); return 0; } #endif mosquitto-2.0.18/src/subs.c000066400000000000000000000506751450213760600155760ustar00rootroot00000000000000/* Copyright (c) 2010-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ /* A note on matching topic subscriptions. * * Topics can be up to 32767 characters in length. The / character is used as a * hierarchy delimiter. Messages are published to a particular topic. * Clients may subscribe to particular topics directly, but may also use * wildcards in subscriptions. The + and # characters are used as wildcards. * The # wildcard can be used at the end of a subscription only, and is a * wildcard for the level of hierarchy at which it is placed and all subsequent * levels. * The + wildcard may be used at any point within the subscription and is a * wildcard for only the level of hierarchy at which it is placed. * Neither wildcard may be used as part of a substring. * Valid: * a/b/+ * a/+/c * a/# * a/b/# * # * +/b/c * +/+/+ * Invalid: * a/#/c * a+/b/c * Valid but non-matching: * a/b * a/+ * +/b * b/c/a * a/b/d */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "util_mosq.h" #include "utlist.h" static int subs__send(struct mosquitto__subleaf *leaf, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store *stored) { bool client_retain; uint16_t mid; uint8_t client_qos, msg_qos; mosquitto_property *properties = NULL; int rc2; /* Check for ACL topic access. */ rc2 = mosquitto_acl_check(leaf->context, topic, stored->payloadlen, stored->payload, stored->qos, stored->retain, MOSQ_ACL_READ); if(rc2 == MOSQ_ERR_ACL_DENIED){ return MOSQ_ERR_SUCCESS; }else if(rc2 == MOSQ_ERR_SUCCESS){ client_qos = leaf->qos; if(db.config->upgrade_outgoing_qos){ msg_qos = client_qos; }else{ if(qos > client_qos){ msg_qos = client_qos; }else{ msg_qos = qos; } } if(msg_qos){ mid = mosquitto__mid_generate(leaf->context); }else{ mid = 0; } if(leaf->retain_as_published){ client_retain = retain; }else{ client_retain = false; } if(leaf->identifier){ mosquitto_property_add_varint(&properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, leaf->identifier); } if(db__message_insert(leaf->context, mid, mosq_md_out, msg_qos, client_retain, stored, properties, true) == 1){ return 1; } }else{ return 1; /* Application error */ } return 0; } static int subs__shared_process(struct mosquitto__subhier *hier, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store *stored) { int rc = 0, rc2; struct mosquitto__subshared *shared, *shared_tmp; struct mosquitto__subleaf *leaf; HASH_ITER(hh, hier->shared, shared, shared_tmp){ leaf = shared->subs; rc2 = subs__send(leaf, topic, qos, retain, stored); /* Remove current from the top, add back to the bottom */ DL_DELETE(shared->subs, leaf); DL_APPEND(shared->subs, leaf); if(rc2) rc = 1; } return rc; } static int subs__process(struct mosquitto__subhier *hier, const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store *stored) { int rc = 0; int rc2; struct mosquitto__subleaf *leaf; rc = subs__shared_process(hier, topic, qos, retain, stored); leaf = hier->subs; while(source_id && leaf){ if(!leaf->context->id || (leaf->no_local && !strcmp(leaf->context->id, source_id))){ leaf = leaf->next; continue; } rc2 = subs__send(leaf, topic, qos, retain, stored); if(rc2){ rc = 1; } leaf = leaf->next; } if(hier->subs || hier->shared){ return rc; }else{ return MOSQ_ERR_NO_SUBSCRIBERS; } } static int sub__add_leaf(struct mosquitto *context, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subleaf **head, struct mosquitto__subleaf **newleaf) { struct mosquitto__subleaf *leaf; *newleaf = NULL; leaf = *head; while(leaf){ if(leaf->context && leaf->context->id && !strcmp(leaf->context->id, context->id)){ /* Client making a second subscription to same topic. Only * need to update QoS. Return MOSQ_ERR_SUB_EXISTS to * indicate this to the calling function. */ leaf->qos = qos; leaf->identifier = identifier; return MOSQ_ERR_SUB_EXISTS; } leaf = leaf->next; } leaf = mosquitto__calloc(1, sizeof(struct mosquitto__subleaf)); if(!leaf) return MOSQ_ERR_NOMEM; leaf->context = context; leaf->qos = qos; leaf->identifier = identifier; leaf->no_local = ((options & MQTT_SUB_OPT_NO_LOCAL) != 0); leaf->retain_as_published = ((options & MQTT_SUB_OPT_RETAIN_AS_PUBLISHED) != 0); DL_APPEND(*head, leaf); *newleaf = leaf; return MOSQ_ERR_SUCCESS; } static void sub__remove_shared_leaf(struct mosquitto__subhier *subhier, struct mosquitto__subshared *shared, struct mosquitto__subleaf *leaf) { DL_DELETE(shared->subs, leaf); if(shared->subs == NULL){ HASH_DELETE(hh, subhier->shared, shared); mosquitto__free(shared->name); mosquitto__free(shared); } mosquitto__free(leaf); } static int sub__add_shared(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier *subhier, const char *sharename) { struct mosquitto__subleaf *newleaf; struct mosquitto__subshared *shared = NULL; struct mosquitto__client_sub **subs; struct mosquitto__client_sub *csub; int i; size_t slen; int rc; slen = strlen(sharename); HASH_FIND(hh, subhier->shared, sharename, slen, shared); if(shared == NULL){ shared = mosquitto__calloc(1, sizeof(struct mosquitto__subshared)); if(!shared){ return MOSQ_ERR_NOMEM; } shared->name = mosquitto__strdup(sharename); if(shared->name == NULL){ mosquitto__free(shared); return MOSQ_ERR_NOMEM; } HASH_ADD_KEYPTR(hh, subhier->shared, shared->name, slen, shared); } rc = sub__add_leaf(context, qos, identifier, options, &shared->subs, &newleaf); if(rc > 0){ if(shared->subs == NULL){ HASH_DELETE(hh, subhier->shared, shared); mosquitto__free(shared->name); mosquitto__free(shared); } return rc; } if(rc != MOSQ_ERR_SUB_EXISTS){ slen = strlen(sub); csub = mosquitto__calloc(1, sizeof(struct mosquitto__client_sub) + slen + 1); if(csub == NULL) return MOSQ_ERR_NOMEM; memcpy(csub->topic_filter, sub, slen); csub->hier = subhier; csub->shared = shared; for(i=0; isub_count; i++){ if(!context->subs[i]){ context->subs[i] = csub; break; } } if(i == context->sub_count){ subs = mosquitto__realloc(context->subs, sizeof(struct mosquitto__client_sub *)*(size_t)(context->sub_count + 1)); if(!subs){ sub__remove_shared_leaf(subhier, shared, newleaf); mosquitto__free(newleaf); mosquitto__free(csub); return MOSQ_ERR_NOMEM; } context->subs = subs; context->sub_count++; context->subs[context->sub_count-1] = csub; } #ifdef WITH_SYS_TREE db.shared_subscription_count++; #endif } if(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt5){ return rc; }else{ /* mqttv311/mqttv5 requires retained messages are resent on * resubscribe. */ return MOSQ_ERR_SUCCESS; } } static int sub__add_normal(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier *subhier) { struct mosquitto__subleaf *newleaf = NULL; struct mosquitto__client_sub **subs; struct mosquitto__client_sub *csub; int i; int rc; size_t slen; rc = sub__add_leaf(context, qos, identifier, options, &subhier->subs, &newleaf); if(rc > 0){ return rc; } if(rc != MOSQ_ERR_SUB_EXISTS){ slen = strlen(sub); csub = mosquitto__calloc(1, sizeof(struct mosquitto__client_sub) + slen + 1); if(csub == NULL) return MOSQ_ERR_NOMEM; memcpy(csub->topic_filter, sub, slen); csub->hier = subhier; csub->shared = NULL; for(i=0; isub_count; i++){ if(!context->subs[i]){ context->subs[i] = csub; break; } } if(i == context->sub_count){ subs = mosquitto__realloc(context->subs, sizeof(struct mosquitto__client_sub *)*(size_t)(context->sub_count + 1)); if(!subs){ DL_DELETE(subhier->subs, newleaf); mosquitto__free(newleaf); mosquitto__free(csub); return MOSQ_ERR_NOMEM; } context->subs = subs; context->sub_count++; context->subs[context->sub_count-1] = csub; } #ifdef WITH_SYS_TREE db.subscription_count++; #endif } if(context->protocol == mosq_p_mqtt31 || context->protocol == mosq_p_mqtt5){ return rc; }else{ /* mqttv311/mqttv5 requires retained messages are resent on * resubscribe. */ return MOSQ_ERR_SUCCESS; } } static int sub__add_context(struct mosquitto *context, const char *topic_filter, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier *subhier, char *const *const topics, const char *sharename) { struct mosquitto__subhier *branch; int topic_index = 0; size_t topiclen; /* Find leaf node */ while(topics && topics[topic_index] != NULL){ topiclen = strlen(topics[topic_index]); if(topiclen > UINT16_MAX){ return MOSQ_ERR_INVAL; } HASH_FIND(hh, subhier->children, topics[topic_index], topiclen, branch); if(!branch){ /* Not found */ branch = sub__add_hier_entry(subhier, &subhier->children, topics[topic_index], (uint16_t)topiclen); if(!branch) return MOSQ_ERR_NOMEM; } subhier = branch; topic_index++; } /* Add add our context */ if(context && context->id){ if(sharename){ return sub__add_shared(context, topic_filter, qos, identifier, options, subhier, sharename); }else{ return sub__add_normal(context, topic_filter, qos, identifier, options, subhier); } }else{ return MOSQ_ERR_SUCCESS; } } static int sub__remove_normal(struct mosquitto *context, struct mosquitto__subhier *subhier, uint8_t *reason) { struct mosquitto__subleaf *leaf; int i; leaf = subhier->subs; while(leaf){ if(leaf->context==context){ #ifdef WITH_SYS_TREE db.subscription_count--; #endif DL_DELETE(subhier->subs, leaf); mosquitto__free(leaf); /* Remove the reference to the sub that the client is keeping. * It would be nice to be able to use the reference directly, * but that would involve keeping a copy of the topic string in * each subleaf. Might be worth considering though. */ for(i=0; isub_count; i++){ if(context->subs[i] && context->subs[i]->hier == subhier){ mosquitto__free(context->subs[i]); context->subs[i] = NULL; break; } } *reason = 0; return MOSQ_ERR_SUCCESS; } leaf = leaf->next; } return MOSQ_ERR_NO_SUBSCRIBERS; } static int sub__remove_shared(struct mosquitto *context, struct mosquitto__subhier *subhier, uint8_t *reason, const char *sharename) { struct mosquitto__subshared *shared; struct mosquitto__subleaf *leaf; int i; HASH_FIND(hh, subhier->shared, sharename, strlen(sharename), shared); if(shared){ leaf = shared->subs; while(leaf){ if(leaf->context==context){ #ifdef WITH_SYS_TREE db.shared_subscription_count--; #endif DL_DELETE(shared->subs, leaf); mosquitto__free(leaf); /* Remove the reference to the sub that the client is keeping. * It would be nice to be able to use the reference directly, * but that would involve keeping a copy of the topic string in * each subleaf. Might be worth considering though. */ for(i=0; isub_count; i++){ if(context->subs[i] && context->subs[i]->hier == subhier && context->subs[i]->shared == shared){ mosquitto__free(context->subs[i]); context->subs[i] = NULL; break; } } if(shared->subs == NULL){ HASH_DELETE(hh, subhier->shared, shared); mosquitto__free(shared->name); mosquitto__free(shared); } *reason = 0; return MOSQ_ERR_SUCCESS; } leaf = leaf->next; } return MOSQ_ERR_NO_SUBSCRIBERS; }else{ return MOSQ_ERR_NO_SUBSCRIBERS; } } static int sub__remove_recurse(struct mosquitto *context, struct mosquitto__subhier *subhier, char **topics, uint8_t *reason, const char *sharename) { struct mosquitto__subhier *branch; if(topics == NULL || topics[0] == NULL){ if(sharename){ return sub__remove_shared(context, subhier, reason, sharename); }else{ return sub__remove_normal(context, subhier, reason); } } HASH_FIND(hh, subhier->children, topics[0], strlen(topics[0]), branch); if(branch){ sub__remove_recurse(context, branch, &(topics[1]), reason, sharename); if(!branch->children && !branch->subs && !branch->shared){ HASH_DELETE(hh, subhier->children, branch); mosquitto__free(branch->topic); mosquitto__free(branch); } } return MOSQ_ERR_SUCCESS; } static int sub__search(struct mosquitto__subhier *subhier, char **split_topics, const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store *stored) { /* FIXME - need to take into account source_id if the client is a bridge */ struct mosquitto__subhier *branch; int rc; bool have_subscribers = false; if(split_topics && split_topics[0]){ /* Check for literal match */ HASH_FIND(hh, subhier->children, split_topics[0], strlen(split_topics[0]), branch); if(branch){ rc = sub__search(branch, &(split_topics[1]), source_id, topic, qos, retain, stored); if(rc == MOSQ_ERR_SUCCESS){ have_subscribers = true; }else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){ return rc; } if(split_topics[1] == NULL){ /* End of list */ rc = subs__process(branch, source_id, topic, qos, retain, stored); if(rc == MOSQ_ERR_SUCCESS){ have_subscribers = true; }else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){ return rc; } } } /* Check for + match */ HASH_FIND(hh, subhier->children, "+", 1, branch); if(branch){ rc = sub__search(branch, &(split_topics[1]), source_id, topic, qos, retain, stored); if(rc == MOSQ_ERR_SUCCESS){ have_subscribers = true; }else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){ return rc; } if(split_topics[1] == NULL){ /* End of list */ rc = subs__process(branch, source_id, topic, qos, retain, stored); if(rc == MOSQ_ERR_SUCCESS){ have_subscribers = true; }else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){ return rc; } } } } /* Check for # match */ HASH_FIND(hh, subhier->children, "#", 1, branch); if(branch && !branch->children){ /* The topic matches due to a # wildcard - process the * subscriptions but *don't* return. Although this branch has ended * there may still be other subscriptions to deal with. */ rc = subs__process(branch, source_id, topic, qos, retain, stored); if(rc == MOSQ_ERR_SUCCESS){ have_subscribers = true; }else if(rc != MOSQ_ERR_NO_SUBSCRIBERS){ return rc; } } if(have_subscribers){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_NO_SUBSCRIBERS; } } struct mosquitto__subhier *sub__add_hier_entry(struct mosquitto__subhier *parent, struct mosquitto__subhier **sibling, const char *topic, uint16_t len) { struct mosquitto__subhier *child; assert(sibling); child = mosquitto__calloc(1, sizeof(struct mosquitto__subhier)); if(!child){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return NULL; } child->parent = parent; child->topic_len = len; child->topic = mosquitto__strdup(topic); if(!child->topic){ child->topic_len = 0; mosquitto__free(child); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return NULL; } HASH_ADD_KEYPTR(hh, *sibling, child->topic, child->topic_len, child); return child; } int sub__add(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier **root) { int rc = 0; struct mosquitto__subhier *subhier; const char *sharename = NULL; char *local_sub; char **topics; size_t topiclen; assert(root); assert(*root); assert(sub); rc = sub__topic_tokenise(sub, &local_sub, &topics, &sharename); if(rc) return rc; topiclen = strlen(topics[0]); if(topiclen > UINT16_MAX){ mosquitto__free(local_sub); mosquitto__free(topics); return MOSQ_ERR_INVAL; } HASH_FIND(hh, *root, topics[0], topiclen, subhier); if(!subhier){ subhier = sub__add_hier_entry(NULL, root, topics[0], (uint16_t)topiclen); if(!subhier){ mosquitto__free(local_sub); mosquitto__free(topics); log__printf(NULL, MOSQ_LOG_ERR, "Error: Out of memory."); return MOSQ_ERR_NOMEM; } } rc = sub__add_context(context, sub, qos, identifier, options, subhier, topics, sharename); mosquitto__free(local_sub); mosquitto__free(topics); return rc; } int sub__remove(struct mosquitto *context, const char *sub, struct mosquitto__subhier *root, uint8_t *reason) { int rc = 0; struct mosquitto__subhier *subhier; const char *sharename = NULL; char *local_sub = NULL; char **topics = NULL; assert(root); assert(sub); rc = sub__topic_tokenise(sub, &local_sub, &topics, &sharename); if(rc) return rc; HASH_FIND(hh, root, topics[0], strlen(topics[0]), subhier); if(subhier){ *reason = MQTT_RC_NO_SUBSCRIPTION_EXISTED; rc = sub__remove_recurse(context, subhier, topics, reason, sharename); } mosquitto__free(local_sub); mosquitto__free(topics); return rc; } int sub__messages_queue(const char *source_id, const char *topic, uint8_t qos, int retain, struct mosquitto_msg_store **stored) { int rc = MOSQ_ERR_SUCCESS, rc2; struct mosquitto__subhier *subhier; char **split_topics = NULL; char *local_topic = NULL; assert(topic); if(sub__topic_tokenise(topic, &local_topic, &split_topics, NULL)) return 1; /* Protect this message until we have sent it to all clients - this is required because websockets client calls db__message_write(), which could remove the message if ref_count==0. */ db__msg_store_ref_inc(*stored); HASH_FIND(hh, db.subs, split_topics[0], strlen(split_topics[0]), subhier); if(subhier){ rc = sub__search(subhier, split_topics, source_id, topic, qos, retain, *stored); } if(retain){ rc2 = retain__store(topic, *stored, split_topics); if(rc2) rc = rc2; } mosquitto__free(split_topics); mosquitto__free(local_topic); /* Remove our reference and free if needed. */ db__msg_store_ref_dec(stored); return rc; } /* Remove a subhier element, and return its parent if that needs freeing as well. */ static struct mosquitto__subhier *tmp_remove_subs(struct mosquitto__subhier *sub) { struct mosquitto__subhier *parent; if(!sub || !sub->parent){ return NULL; } if(sub->children || sub->subs){ return NULL; } parent = sub->parent; HASH_DELETE(hh, parent->children, sub); mosquitto__free(sub->topic); mosquitto__free(sub); if(parent->subs == NULL && parent->children == NULL && parent->shared == NULL && parent->parent){ return parent; }else{ return NULL; } } /* Remove all subscriptions for a client. */ int sub__clean_session(struct mosquitto *context) { int i; struct mosquitto__subleaf *leaf; struct mosquitto__subhier *hier; for(i=0; isub_count; i++){ if(context->subs[i] == NULL){ continue; } hier = context->subs[i]->hier; if(context->subs[i]->shared){ leaf = context->subs[i]->shared->subs; while(leaf){ if(leaf->context==context){ #ifdef WITH_SYS_TREE db.shared_subscription_count--; #endif sub__remove_shared_leaf(context->subs[i]->hier, context->subs[i]->shared, leaf); break; } leaf = leaf->next; } }else{ leaf = hier->subs; while(leaf){ if(leaf->context==context){ #ifdef WITH_SYS_TREE db.subscription_count--; #endif DL_DELETE(hier->subs, leaf); mosquitto__free(leaf); break; } leaf = leaf->next; } } mosquitto__free(context->subs[i]); context->subs[i] = NULL; if(hier->subs == NULL && hier->children == NULL && hier->shared == NULL && hier->parent){ do{ hier = tmp_remove_subs(hier); }while(hier); } } mosquitto__free(context->subs); context->subs = NULL; context->sub_count = 0; return MOSQ_ERR_SUCCESS; } void sub__tree_print(struct mosquitto__subhier *root, int level) { int i; struct mosquitto__subhier *branch, *branch_tmp; struct mosquitto__subleaf *leaf; HASH_ITER(hh, root, branch, branch_tmp){ if(level > -1){ for(i=0; i<(level+2)*2; i++){ printf(" "); } printf("%s", branch->topic); leaf = branch->subs; while(leaf){ if(leaf->context){ printf(" (%s, %d)", leaf->context->id, leaf->qos); }else{ printf(" (%s, %d)", "", leaf->qos); } leaf = leaf->next; } printf("\n"); } sub__tree_print(branch->children, level+1); } } mosquitto-2.0.18/src/sys_tree.c000066400000000000000000000372641450213760600164560ustar00rootroot00000000000000/* Copyright (c) 2009-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifdef WITH_SYS_TREE #include "config.h" #include #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "time_mosq.h" #define BUFLEN 100 #define SYS_TREE_QOS 2 uint64_t g_bytes_received = 0; uint64_t g_bytes_sent = 0; uint64_t g_pub_bytes_received = 0; uint64_t g_pub_bytes_sent = 0; unsigned long g_msgs_received = 0; unsigned long g_msgs_sent = 0; unsigned long g_pub_msgs_received = 0; unsigned long g_pub_msgs_sent = 0; unsigned long g_msgs_dropped = 0; unsigned int g_clients_expired = 0; unsigned int g_socket_connections = 0; unsigned int g_connection_count = 0; void sys_tree__init(void) { char buf[64]; uint32_t len; if(db.config->sys_interval == 0){ return; } /* Set static $SYS messages */ len = (uint32_t)snprintf(buf, 64, "mosquitto version %s", VERSION); db__messages_easy_queue(NULL, "$SYS/broker/version", SYS_TREE_QOS, len, buf, 1, 0, NULL); } static void sys_tree__update_clients(char *buf) { static unsigned int client_count = UINT_MAX; static unsigned int clients_expired = UINT_MAX; static unsigned int client_max = 0; static unsigned int disconnected_count = UINT_MAX; static unsigned int connected_count = UINT_MAX; uint32_t len; unsigned int count_total, count_by_sock; count_total = HASH_CNT(hh_id, db.contexts_by_id); count_by_sock = HASH_CNT(hh_sock, db.contexts_by_sock); if(client_count != count_total){ client_count = count_total; len = (uint32_t)snprintf(buf, BUFLEN, "%d", client_count); db__messages_easy_queue(NULL, "$SYS/broker/clients/total", SYS_TREE_QOS, len, buf, 1, 0, NULL); if(client_count > client_max){ client_max = client_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", client_max); db__messages_easy_queue(NULL, "$SYS/broker/clients/maximum", SYS_TREE_QOS, len, buf, 1, 0, NULL); } } if(disconnected_count != count_total-count_by_sock){ disconnected_count = count_total-count_by_sock; len = (uint32_t)snprintf(buf, BUFLEN, "%d", disconnected_count); db__messages_easy_queue(NULL, "$SYS/broker/clients/inactive", SYS_TREE_QOS, len, buf, 1, 0, NULL); db__messages_easy_queue(NULL, "$SYS/broker/clients/disconnected", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(connected_count != count_by_sock){ connected_count = count_by_sock; len = (uint32_t)snprintf(buf, BUFLEN, "%d", connected_count); db__messages_easy_queue(NULL, "$SYS/broker/clients/active", SYS_TREE_QOS, len, buf, 1, 0, NULL); db__messages_easy_queue(NULL, "$SYS/broker/clients/connected", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(g_clients_expired != clients_expired){ clients_expired = g_clients_expired; len = (uint32_t)snprintf(buf, BUFLEN, "%d", clients_expired); db__messages_easy_queue(NULL, "$SYS/broker/clients/expired", SYS_TREE_QOS, len, buf, 1, 0, NULL); } } #ifdef REAL_WITH_MEMORY_TRACKING static void sys_tree__update_memory(char *buf) { static unsigned long current_heap = ULONG_MAX; static unsigned long max_heap = ULONG_MAX; unsigned long value_ul; uint32_t len; value_ul = mosquitto__memory_used(); if(current_heap != value_ul){ current_heap = value_ul; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", current_heap); db__messages_easy_queue(NULL, "$SYS/broker/heap/current", SYS_TREE_QOS, len, buf, 1, 0, NULL); } value_ul =mosquitto__max_memory_used(); if(max_heap != value_ul){ max_heap = value_ul; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", max_heap); db__messages_easy_queue(NULL, "$SYS/broker/heap/maximum", SYS_TREE_QOS, len, buf, 1, 0, NULL); } } #endif static void calc_load(char *buf, const char *topic, bool initial, double exponent, double interval, double *current) { double new_value; uint32_t len; if (initial) { new_value = *current; len = (uint32_t)snprintf(buf, BUFLEN, "%.2f", new_value); db__messages_easy_queue(NULL, topic, SYS_TREE_QOS, len, buf, 1, 0, NULL); } else { new_value = interval + exponent*((*current) - interval); if(fabs(new_value - (*current)) >= 0.01){ len = (uint32_t)snprintf(buf, BUFLEN, "%.2f", new_value); db__messages_easy_queue(NULL, topic, SYS_TREE_QOS, len, buf, 1, 0, NULL); } } (*current) = new_value; } /* Send messages for the $SYS hierarchy if the last update is longer than * 'interval' seconds ago. * 'interval' is the amount of seconds between updates. If 0, then no periodic * messages are sent for the $SYS hierarchy. * 'start_time' is the result of time() that the broker was started at. */ void sys_tree__update(int interval, time_t start_time) { static time_t last_update = 0; time_t uptime; char buf[BUFLEN]; static int msg_store_count = INT_MAX; static unsigned long msg_store_bytes = ULONG_MAX; static unsigned long msgs_received = ULONG_MAX; static unsigned long msgs_sent = ULONG_MAX; static unsigned long publish_dropped = ULONG_MAX; static unsigned long pub_msgs_received = ULONG_MAX; static unsigned long pub_msgs_sent = ULONG_MAX; static unsigned long long bytes_received = ULLONG_MAX; static unsigned long long bytes_sent = ULLONG_MAX; static unsigned long long pub_bytes_received = ULLONG_MAX; static unsigned long long pub_bytes_sent = ULLONG_MAX; static int subscription_count = INT_MAX; static int shared_subscription_count = INT_MAX; static int retained_count = INT_MAX; static double msgs_received_load1 = 0; static double msgs_received_load5 = 0; static double msgs_received_load15 = 0; static double msgs_sent_load1 = 0; static double msgs_sent_load5 = 0; static double msgs_sent_load15 = 0; static double publish_dropped_load1 = 0; static double publish_dropped_load5 = 0; static double publish_dropped_load15 = 0; double msgs_received_interval, msgs_sent_interval, publish_dropped_interval; static double publish_received_load1 = 0; static double publish_received_load5 = 0; static double publish_received_load15 = 0; static double publish_sent_load1 = 0; static double publish_sent_load5 = 0; static double publish_sent_load15 = 0; double publish_received_interval, publish_sent_interval; static double bytes_received_load1 = 0; static double bytes_received_load5 = 0; static double bytes_received_load15 = 0; static double bytes_sent_load1 = 0; static double bytes_sent_load5 = 0; static double bytes_sent_load15 = 0; double bytes_received_interval, bytes_sent_interval; static double socket_load1 = 0; static double socket_load5 = 0; static double socket_load15 = 0; double socket_interval; static double connection_load1 = 0; static double connection_load5 = 0; static double connection_load15 = 0; double connection_interval; double exponent; double i_mult; uint32_t len; bool initial_publish; if(interval && db.now_s - interval > last_update){ uptime = db.now_s - start_time; len = (uint32_t)snprintf(buf, BUFLEN, "%" PRIu64 " seconds", (uint64_t)uptime); db__messages_easy_queue(NULL, "$SYS/broker/uptime", SYS_TREE_QOS, len, buf, 1, 0, NULL); sys_tree__update_clients(buf); initial_publish = false; if(last_update == 0){ initial_publish = true; last_update = 1; } if(last_update > 0){ i_mult = 60.0/(double)(db.now_s-last_update); msgs_received_interval = (double)(g_msgs_received - msgs_received)*i_mult; msgs_sent_interval = (double)(g_msgs_sent - msgs_sent)*i_mult; publish_dropped_interval = (double)(g_msgs_dropped - publish_dropped)*i_mult; publish_received_interval = (double)(g_pub_msgs_received - pub_msgs_received)*i_mult; publish_sent_interval = (double)(g_pub_msgs_sent - pub_msgs_sent)*i_mult; bytes_received_interval = (double)(g_bytes_received - bytes_received)*i_mult; bytes_sent_interval = (double)(g_bytes_sent - bytes_sent)*i_mult; socket_interval = g_socket_connections*i_mult; g_socket_connections = 0; connection_interval = g_connection_count*i_mult; g_connection_count = 0; /* 1 minute load */ exponent = exp(-1.0*(double)(db.now_s-last_update)/60.0); calc_load(buf, "$SYS/broker/load/messages/received/1min", initial_publish, exponent, msgs_received_interval, &msgs_received_load1); calc_load(buf, "$SYS/broker/load/messages/sent/1min", initial_publish, exponent, msgs_sent_interval, &msgs_sent_load1); calc_load(buf, "$SYS/broker/load/publish/dropped/1min", initial_publish, exponent, publish_dropped_interval, &publish_dropped_load1); calc_load(buf, "$SYS/broker/load/publish/received/1min", initial_publish, exponent, publish_received_interval, &publish_received_load1); calc_load(buf, "$SYS/broker/load/publish/sent/1min", initial_publish, exponent, publish_sent_interval, &publish_sent_load1); calc_load(buf, "$SYS/broker/load/bytes/received/1min", initial_publish, exponent, bytes_received_interval, &bytes_received_load1); calc_load(buf, "$SYS/broker/load/bytes/sent/1min", initial_publish, exponent, bytes_sent_interval, &bytes_sent_load1); calc_load(buf, "$SYS/broker/load/sockets/1min", initial_publish, exponent, socket_interval, &socket_load1); calc_load(buf, "$SYS/broker/load/connections/1min", initial_publish, exponent, connection_interval, &connection_load1); /* 5 minute load */ exponent = exp(-1.0*(double)(db.now_s-last_update)/300.0); calc_load(buf, "$SYS/broker/load/messages/received/5min", initial_publish, exponent, msgs_received_interval, &msgs_received_load5); calc_load(buf, "$SYS/broker/load/messages/sent/5min", initial_publish, exponent, msgs_sent_interval, &msgs_sent_load5); calc_load(buf, "$SYS/broker/load/publish/dropped/5min", initial_publish, exponent, publish_dropped_interval, &publish_dropped_load5); calc_load(buf, "$SYS/broker/load/publish/received/5min", initial_publish, exponent, publish_received_interval, &publish_received_load5); calc_load(buf, "$SYS/broker/load/publish/sent/5min", initial_publish, exponent, publish_sent_interval, &publish_sent_load5); calc_load(buf, "$SYS/broker/load/bytes/received/5min", initial_publish, exponent, bytes_received_interval, &bytes_received_load5); calc_load(buf, "$SYS/broker/load/bytes/sent/5min", initial_publish, exponent, bytes_sent_interval, &bytes_sent_load5); calc_load(buf, "$SYS/broker/load/sockets/5min", initial_publish, exponent, socket_interval, &socket_load5); calc_load(buf, "$SYS/broker/load/connections/5min", initial_publish, exponent, connection_interval, &connection_load5); /* 15 minute load */ exponent = exp(-1.0*(double)(db.now_s-last_update)/900.0); calc_load(buf, "$SYS/broker/load/messages/received/15min", initial_publish, exponent, msgs_received_interval, &msgs_received_load15); calc_load(buf, "$SYS/broker/load/messages/sent/15min", initial_publish, exponent, msgs_sent_interval, &msgs_sent_load15); calc_load(buf, "$SYS/broker/load/publish/dropped/15min", initial_publish, exponent, publish_dropped_interval, &publish_dropped_load15); calc_load(buf, "$SYS/broker/load/publish/received/15min", initial_publish, exponent, publish_received_interval, &publish_received_load15); calc_load(buf, "$SYS/broker/load/publish/sent/15min", initial_publish, exponent, publish_sent_interval, &publish_sent_load15); calc_load(buf, "$SYS/broker/load/bytes/received/15min", initial_publish, exponent, bytes_received_interval, &bytes_received_load15); calc_load(buf, "$SYS/broker/load/bytes/sent/15min", initial_publish, exponent, bytes_sent_interval, &bytes_sent_load15); calc_load(buf, "$SYS/broker/load/sockets/15min", initial_publish, exponent, socket_interval, &socket_load15); calc_load(buf, "$SYS/broker/load/connections/15min", initial_publish, exponent, connection_interval, &connection_load15); } if(db.msg_store_count != msg_store_count){ msg_store_count = db.msg_store_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", msg_store_count); db__messages_easy_queue(NULL, "$SYS/broker/messages/stored", SYS_TREE_QOS, len, buf, 1, 0, NULL); db__messages_easy_queue(NULL, "$SYS/broker/store/messages/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if (db.msg_store_bytes != msg_store_bytes){ msg_store_bytes = db.msg_store_bytes; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", msg_store_bytes); db__messages_easy_queue(NULL, "$SYS/broker/store/messages/bytes", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(db.subscription_count != subscription_count){ subscription_count = db.subscription_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", subscription_count); db__messages_easy_queue(NULL, "$SYS/broker/subscriptions/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(db.shared_subscription_count != shared_subscription_count){ shared_subscription_count = db.shared_subscription_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", shared_subscription_count); db__messages_easy_queue(NULL, "$SYS/broker/shared_subscriptions/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(db.retained_count != retained_count){ retained_count = db.retained_count; len = (uint32_t)snprintf(buf, BUFLEN, "%d", retained_count); db__messages_easy_queue(NULL, "$SYS/broker/retained messages/count", SYS_TREE_QOS, len, buf, 1, 0, NULL); } #ifdef REAL_WITH_MEMORY_TRACKING sys_tree__update_memory(buf); #endif if(msgs_received != g_msgs_received){ msgs_received = g_msgs_received; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", msgs_received); db__messages_easy_queue(NULL, "$SYS/broker/messages/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(msgs_sent != g_msgs_sent){ msgs_sent = g_msgs_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", msgs_sent); db__messages_easy_queue(NULL, "$SYS/broker/messages/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(publish_dropped != g_msgs_dropped){ publish_dropped = g_msgs_dropped; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", publish_dropped); db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/dropped", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_msgs_received != g_pub_msgs_received){ pub_msgs_received = g_pub_msgs_received; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", pub_msgs_received); db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_msgs_sent != g_pub_msgs_sent){ pub_msgs_sent = g_pub_msgs_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%lu", pub_msgs_sent); db__messages_easy_queue(NULL, "$SYS/broker/publish/messages/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(bytes_received != g_bytes_received){ bytes_received = g_bytes_received; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", bytes_received); db__messages_easy_queue(NULL, "$SYS/broker/bytes/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(bytes_sent != g_bytes_sent){ bytes_sent = g_bytes_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", bytes_sent); db__messages_easy_queue(NULL, "$SYS/broker/bytes/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_bytes_received != g_pub_bytes_received){ pub_bytes_received = g_pub_bytes_received; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", pub_bytes_received); db__messages_easy_queue(NULL, "$SYS/broker/publish/bytes/received", SYS_TREE_QOS, len, buf, 1, 0, NULL); } if(pub_bytes_sent != g_pub_bytes_sent){ pub_bytes_sent = g_pub_bytes_sent; len = (uint32_t)snprintf(buf, BUFLEN, "%llu", pub_bytes_sent); db__messages_easy_queue(NULL, "$SYS/broker/publish/bytes/sent", SYS_TREE_QOS, len, buf, 1, 0, NULL); } last_update = db.now_s; } } #endif mosquitto-2.0.18/src/sys_tree.h000066400000000000000000000042241450213760600164510ustar00rootroot00000000000000/* Copyright (c) 2015-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifndef SYS_TREE_H #define SYS_TREE_H #if defined(WITH_SYS_TREE) && defined(WITH_BROKER) extern uint64_t g_bytes_received; extern uint64_t g_bytes_sent; extern uint64_t g_pub_bytes_received; extern uint64_t g_pub_bytes_sent; extern unsigned long g_msgs_received; extern unsigned long g_msgs_sent; extern unsigned long g_pub_msgs_received; extern unsigned long g_pub_msgs_sent; extern unsigned long g_msgs_dropped; extern int g_clients_expired; extern unsigned int g_socket_connections; extern unsigned int g_connection_count; #define G_BYTES_RECEIVED_INC(A) (g_bytes_received+=(uint64_t)(A)) #define G_BYTES_SENT_INC(A) (g_bytes_sent+=(uint64_t)(A)) #define G_PUB_BYTES_RECEIVED_INC(A) (g_pub_bytes_received+=(A)) #define G_PUB_BYTES_SENT_INC(A) (g_pub_bytes_sent+=(A)) #define G_MSGS_RECEIVED_INC(A) (g_msgs_received+=(A)) #define G_MSGS_SENT_INC(A) (g_msgs_sent+=(A)) #define G_PUB_MSGS_RECEIVED_INC(A) (g_pub_msgs_received+=(A)) #define G_PUB_MSGS_SENT_INC(A) (g_pub_msgs_sent+=(A)) #define G_MSGS_DROPPED_INC() (g_msgs_dropped++) #define G_CLIENTS_EXPIRED_INC() (g_clients_expired++) #define G_SOCKET_CONNECTIONS_INC() (g_socket_connections++) #define G_CONNECTION_COUNT_INC() (g_connection_count++) #else #define G_BYTES_RECEIVED_INC(A) #define G_BYTES_SENT_INC(A) #define G_PUB_BYTES_RECEIVED_INC(A) #define G_PUB_BYTES_SENT_INC(A) #define G_MSGS_RECEIVED_INC(A) #define G_MSGS_SENT_INC(A) #define G_PUB_MSGS_RECEIVED_INC(A) #define G_PUB_MSGS_SENT_INC(A) #define G_MSGS_DROPPED_INC() #define G_CLIENTS_EXPIRED_INC() #define G_SOCKET_CONNECTIONS_INC() #define G_CONNECTION_COUNT_INC() #endif #endif mosquitto-2.0.18/src/topic_tok.c000066400000000000000000000045201450213760600166010ustar00rootroot00000000000000/* Copyright (c) 2010-2019 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "mqtt_protocol.h" #include "util_mosq.h" #include "utlist.h" static char *strtok_hier(char *str, char **saveptr) { char *c; if(str != NULL){ *saveptr = str; } if(*saveptr == NULL){ return NULL; } c = strchr(*saveptr, '/'); if(c){ str = *saveptr; *saveptr = c+1; c[0] = '\0'; }else if(*saveptr){ /* No match, but surplus string */ str = *saveptr; *saveptr = NULL; } return str; } int sub__topic_tokenise(const char *subtopic, char **local_sub, char ***topics, const char **sharename) { char *saveptr = NULL; char *token; int count; int topic_index = 0; int i; size_t len; len = strlen(subtopic); if(len == 0){ return MOSQ_ERR_INVAL; } *local_sub = mosquitto__strdup(subtopic); if((*local_sub) == NULL) return MOSQ_ERR_NOMEM; count = 0; saveptr = *local_sub; while(saveptr){ saveptr = strchr(&saveptr[1], '/'); count++; } *topics = mosquitto__calloc((size_t)(count+3) /* 3=$shared,sharename,NULL */, sizeof(char *)); if((*topics) == NULL){ mosquitto__free(*local_sub); return MOSQ_ERR_NOMEM; } if((*local_sub)[0] != '$'){ (*topics)[topic_index] = ""; topic_index++; } token = strtok_hier((*local_sub), &saveptr); while(token){ (*topics)[topic_index] = token; topic_index++; token = strtok_hier(NULL, &saveptr); } if(!strcmp((*topics)[0], "$share")){ if(count < 2){ mosquitto__free(*local_sub); mosquitto__free(*topics); return MOSQ_ERR_PROTOCOL; } if(sharename){ (*sharename) = (*topics)[1]; } for(i=1; i All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifdef WITH_WEBSOCKETS #include "config.h" #include #include "mosquitto_internal.h" #include "mosquitto_broker_internal.h" #include "mqtt_protocol.h" #include "memory_mosq.h" #include "packet_mosq.h" #include "sys_tree.h" #include "util_mosq.h" #include #include #include #ifndef WIN32 # include #endif /* Be careful if changing these, if TX is not bigger than SERV then there can * be very large write performance penalties. */ #define WS_SERV_BUF_SIZE 4096 #define WS_TX_BUF_SIZE (WS_SERV_BUF_SIZE*2) static int callback_mqtt( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); static int callback_http( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len); enum mosq_ws_protocols { PROTOCOL_HTTP = 0, PROTOCOL_MQTT, DEMO_PROTOCOL_COUNT }; struct libws_http_data { FILE *fptr; }; static struct lws_protocols protocols[] = { /* first protocol must always be HTTP handler */ { "http-only", /* name */ callback_http, /* lws_callback_function */ sizeof (struct libws_http_data), /* per_session_data_size */ 0, /* rx_buffer_size */ 0, /* id */ NULL, /* user v1.4 on */ WS_TX_BUF_SIZE /* tx_packet_size v2.3.0 */ }, { "mqtt", callback_mqtt, sizeof(struct libws_mqtt_data), 0, /* rx_buffer_size */ 1, /* id */ NULL, /* user v1.4 on */ WS_TX_BUF_SIZE /* tx_packet_size v2.3.0 */ }, { "mqttv3.1", callback_mqtt, sizeof(struct libws_mqtt_data), 0, /* rx_buffer_size */ 2, /* id */ NULL, /* user v1.4 on */ WS_TX_BUF_SIZE /* tx_packet_size v2.3.0 */ }, { NULL, NULL, 0, 0, /* rx_buffer_size */ 0, /* id */ NULL, /* user v1.4 on */ 0 /* tx_packet_size v2.3.0 */ } }; static void easy_address(int sock, struct mosquitto *mosq) { char address[1024]; if(!net__socket_get_address(sock, address, 1024, &mosq->remote_port)){ mosq->address = mosquitto__strdup(address); } } static int callback_mqtt( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct mosquitto *mosq = NULL; struct mosquitto__packet *packet; int count; unsigned int ucount; const struct lws_protocols *p; struct libws_mqtt_data *u = (struct libws_mqtt_data *)user; size_t pos; uint8_t *buf; int rc; uint8_t byte; char ip_addr_buff[1024]; switch (reason) { case LWS_CALLBACK_ESTABLISHED: mosq = context__init(WEBSOCKET_CLIENT); if(mosq){ p = lws_get_protocol(wsi); mosq->listener = p->user; if(!mosq->listener){ mosquitto__free(mosq); return -1; } mosq->wsi = wsi; #ifdef WITH_TLS if(in){ mosq->ssl = (SSL *)in; if(!mosq->listener->ssl_ctx){ mosq->listener->ssl_ctx = SSL_get_SSL_CTX(mosq->ssl); } } #endif u->mosq = mosq; }else{ return -1; } if (lws_hdr_copy(wsi, ip_addr_buff, sizeof(ip_addr_buff), WSI_TOKEN_X_FORWARDED_FOR) > 0) { mosq->address = mosquitto__strdup(ip_addr_buff); } else { easy_address(lws_get_socket_fd(wsi), mosq); } if(!mosq->address){ /* getpeername and inet_ntop failed and not a bridge */ mosquitto__free(mosq); u->mosq = NULL; return -1; } if(mosq->listener->max_connections > 0 && mosq->listener->client_count > mosq->listener->max_connections){ if(db.config->connection_messages == true){ log__printf(NULL, MOSQ_LOG_NOTICE, "Client connection from %s denied: max_connections exceeded.", mosq->address); } mosquitto__free(mosq->address); mosquitto__free(mosq); u->mosq = NULL; return -1; } mosq->sock = lws_get_socket_fd(wsi); HASH_ADD(hh_sock, db.contexts_by_sock, sock, sizeof(mosq->sock), mosq); mux__add_in(mosq); break; case LWS_CALLBACK_CLOSED: if(!u){ return -1; } mosq = u->mosq; if(mosq){ if(mosq->sock != INVALID_SOCKET){ HASH_DELETE(hh_sock, db.contexts_by_sock, mosq); mosq->sock = INVALID_SOCKET; mux__delete(mosq); } mosq->wsi = NULL; #ifdef WITH_TLS mosq->ssl = NULL; #endif do_disconnect(mosq, MOSQ_ERR_CONN_LOST); } break; case LWS_CALLBACK_SERVER_WRITEABLE: if(!u){ return -1; } mosq = u->mosq; if(!mosq){ return -1; } rc = db__message_write_inflight_out_latest(mosq); if(rc) return -1; rc = db__message_write_queued_out(mosq); if(rc) return -1; if(mosq->out_packet && !mosq->current_out_packet){ mosq->current_out_packet = mosq->out_packet; mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } mosq->out_packet_count--; } while(mosq->current_out_packet && !lws_send_pipe_choked(mosq->wsi)){ packet = mosq->current_out_packet; if(packet->pos == 0 && packet->to_process == packet->packet_length){ /* First time this packet has been dealt with. * libwebsockets requires that the payload has * LWS_PRE space available before the * actual data. * We've already made the payload big enough to allow this, * but need to move it into position here. */ memmove(&packet->payload[LWS_PRE], packet->payload, packet->packet_length); packet->pos += LWS_PRE; } count = lws_write(wsi, &packet->payload[packet->pos], packet->to_process, LWS_WRITE_BINARY); if(count < 0){ if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting || mosq->state == mosq_cs_disused){ return -1; } return 0; } ucount = (unsigned int)count; #ifdef WITH_SYS_TREE g_bytes_sent += ucount; #endif packet->to_process -= ucount; packet->pos += ucount; if(packet->to_process > 0){ if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting || mosq->state == mosq_cs_disused){ return -1; } break; } #ifdef WITH_SYS_TREE g_msgs_sent++; if(((packet->command)&0xF0) == CMD_PUBLISH){ g_pub_msgs_sent++; } #endif /* Free data and reset values */ mosq->current_out_packet = mosq->out_packet; if(mosq->out_packet){ mosq->out_packet = mosq->out_packet->next; if(!mosq->out_packet){ mosq->out_packet_last = NULL; } mosq->out_packet_count--; } packet__cleanup(packet); mosquitto__free(packet); mosq->next_msg_out = db.now_s + mosq->keepalive; } if (mosq->state == mosq_cs_disconnect_ws || mosq->state == mosq_cs_disconnecting || mosq->state == mosq_cs_disused){ return -1; } if(mosq->current_out_packet){ lws_callback_on_writable(mosq->wsi); } break; case LWS_CALLBACK_RECEIVE: if(!u || !u->mosq){ return -1; } mosq = u->mosq; pos = 0; buf = (uint8_t *)in; G_BYTES_RECEIVED_INC(len); while(pos < len){ if(!mosq->in_packet.command){ mosq->in_packet.command = buf[pos]; pos++; /* Clients must send CONNECT as their first command. */ if(mosq->state == mosq_cs_new && (mosq->in_packet.command&0xF0) != CMD_CONNECT){ return -1; } } if(mosq->in_packet.remaining_count <= 0){ do{ if(pos == len){ return 0; } byte = buf[pos]; pos++; mosq->in_packet.remaining_count--; /* Max 4 bytes length for remaining length as defined by protocol. * Anything more likely means a broken/malicious client. */ if(mosq->in_packet.remaining_count < -4){ return -1; } mosq->in_packet.remaining_length += (byte & 127) * mosq->in_packet.remaining_mult; mosq->in_packet.remaining_mult *= 128; }while((byte & 128) != 0); mosq->in_packet.remaining_count = (int8_t)(mosq->in_packet.remaining_count * -1); if(mosq->in_packet.remaining_length > 0){ mosq->in_packet.payload = mosquitto__malloc(mosq->in_packet.remaining_length*sizeof(uint8_t)); if(!mosq->in_packet.payload){ return -1; } mosq->in_packet.to_process = mosq->in_packet.remaining_length; } } if(mosq->in_packet.to_process>0){ if((uint32_t)len - pos >= mosq->in_packet.to_process){ memcpy(&mosq->in_packet.payload[mosq->in_packet.pos], &buf[pos], mosq->in_packet.to_process); mosq->in_packet.pos += mosq->in_packet.to_process; pos += mosq->in_packet.to_process; mosq->in_packet.to_process = 0; }else{ memcpy(&mosq->in_packet.payload[mosq->in_packet.pos], &buf[pos], len-pos); mosq->in_packet.pos += (uint32_t)(len-pos); mosq->in_packet.to_process -= (uint32_t)(len-pos); return 0; } } /* All data for this packet is read. */ mosq->in_packet.pos = 0; #ifdef WITH_SYS_TREE G_MSGS_RECEIVED_INC(1); if(((mosq->in_packet.command)&0xF0) == CMD_PUBLISH){ G_PUB_MSGS_RECEIVED_INC(1); } #endif rc = handle__packet(mosq); /* Free data and reset values */ packet__cleanup(&mosq->in_packet); keepalive__update(mosq); if(rc && (mosq->out_packet || mosq->current_out_packet)) { if(mosq->state != mosq_cs_disconnecting){ mosquitto__set_state(mosq, mosq_cs_disconnect_ws); } lws_callback_on_writable(mosq->wsi); } else if (rc) { do_disconnect(mosq, MOSQ_ERR_CONN_LOST); return -1; } } break; default: break; } return 0; } static char *http__canonical_filename( struct lws *wsi, const char *in, const char *http_dir) { size_t inlen, slen; char *filename, *filename_canonical; inlen = strlen(in); if(in[inlen-1] == '/'){ slen = strlen(http_dir) + inlen + strlen("/index.html") + 2; }else{ slen = strlen(http_dir) + inlen + 2; } filename = mosquitto__malloc(slen); if(!filename){ lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); return NULL; } if(((char *)in)[inlen-1] == '/'){ snprintf(filename, slen, "%s%sindex.html", http_dir, (char *)in); }else{ snprintf(filename, slen, "%s%s", http_dir, (char *)in); } /* Get canonical path and check it is within our http_dir */ #ifdef WIN32 filename_canonical = _fullpath(NULL, filename, 0); mosquitto__free(filename); if(!filename_canonical){ lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); return NULL; } #else filename_canonical = realpath(filename, NULL); mosquitto__free(filename); if(!filename_canonical){ if(errno == EACCES){ lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); }else if(errno == EINVAL || errno == EIO || errno == ELOOP){ lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); }else if(errno == ENAMETOOLONG){ lws_return_http_status(wsi, HTTP_STATUS_REQ_URI_TOO_LONG, NULL); }else if(errno == ENOENT || errno == ENOTDIR){ lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); } return NULL; } #endif if(strncmp(http_dir, filename_canonical, strlen(http_dir))){ /* Requested file isn't within http_dir, deny access. */ free(filename_canonical); lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); return NULL; } return filename_canonical; } static int callback_http( struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { struct libws_http_data *u = (struct libws_http_data *)user; struct libws_mqtt_hack *hack; char *http_dir; size_t buflen; size_t wlen; int rc; char *filename_canonical; unsigned char buf[4096]; struct stat filestat; struct mosquitto *mosq; struct lws_pollargs *pollargs = (struct lws_pollargs *)in; /* FIXME - ssl cert verification is done here. */ switch (reason) { case LWS_CALLBACK_HTTP: if(!u){ return -1; } hack = (struct libws_mqtt_hack *)lws_context_user(lws_get_context(wsi)); if(!hack){ return -1; } http_dir = hack->http_dir; if(!http_dir){ /* http disabled */ return -1; } /* Forbid POST */ if(lws_hdr_total_length(wsi, WSI_TOKEN_POST_URI)){ lws_return_http_status(wsi, HTTP_STATUS_METHOD_NOT_ALLOWED, NULL); return -1; } filename_canonical = http__canonical_filename(wsi, (char *)in, http_dir); if(!filename_canonical) return -1; u->fptr = fopen(filename_canonical, "rb"); if(!u->fptr){ free(filename_canonical); lws_return_http_status(wsi, HTTP_STATUS_NOT_FOUND, NULL); return -1; } if(fstat(fileno(u->fptr), &filestat) < 0){ free(filename_canonical); lws_return_http_status(wsi, HTTP_STATUS_INTERNAL_SERVER_ERROR, NULL); fclose(u->fptr); u->fptr = NULL; return -1; } if((filestat.st_mode & S_IFDIR) == S_IFDIR){ fclose(u->fptr); u->fptr = NULL; free(filename_canonical); /* FIXME - use header functions from lws 2.x */ buflen = (size_t)snprintf((char *)buf, 4096, "HTTP/1.0 302 OK\r\n" "Location: %s/\r\n\r\n", (char *)in); return lws_write(wsi, buf, buflen, LWS_WRITE_HTTP); } if((filestat.st_mode & S_IFREG) != S_IFREG){ lws_return_http_status(wsi, HTTP_STATUS_FORBIDDEN, NULL); fclose(u->fptr); u->fptr = NULL; free(filename_canonical); return -1; } log__printf(NULL, MOSQ_LOG_DEBUG, "http serving file \"%s\".", filename_canonical); free(filename_canonical); /* FIXME - use header functions from lws 2.x */ buflen = (size_t)snprintf((char *)buf, 4096, "HTTP/1.0 200 OK\r\n" "Server: mosquitto\r\n" "Content-Length: %u\r\n\r\n", (unsigned int)filestat.st_size); if(lws_write(wsi, buf, buflen, LWS_WRITE_HTTP) < 0){ fclose(u->fptr); u->fptr = NULL; return -1; } lws_callback_on_writable(wsi); break; case LWS_CALLBACK_HTTP_BODY: /* For extra POST data? */ return -1; case LWS_CALLBACK_HTTP_BODY_COMPLETION: /* For end of extra POST data? */ return -1; case LWS_CALLBACK_FILTER_HTTP_CONNECTION: /* Access control here */ return 0; case LWS_CALLBACK_HTTP_WRITEABLE: /* Send our data here */ if(u && u->fptr){ do{ buflen = fread(buf, 1, sizeof(buf), u->fptr); if(buflen < 1){ fclose(u->fptr); u->fptr = NULL; return -1; } rc = lws_write(wsi, buf, buflen, LWS_WRITE_HTTP); if(rc < 0){ return -1; } wlen = (size_t)rc; if(wlen < buflen){ if(fseek(u->fptr, (long)(buflen-wlen), SEEK_CUR) < 0){ fclose(u->fptr); u->fptr = NULL; return -1; } }else{ if(buflen < sizeof(buf)){ fclose(u->fptr); u->fptr = NULL; } } }while(u->fptr && !lws_send_pipe_choked(wsi)); lws_callback_on_writable(wsi); }else{ return -1; } break; case LWS_CALLBACK_CLOSED: case LWS_CALLBACK_CLOSED_HTTP: case LWS_CALLBACK_HTTP_FILE_COMPLETION: if(u && u->fptr){ fclose(u->fptr); u->fptr = NULL; } break; case LWS_CALLBACK_ADD_POLL_FD: HASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq); if(mosq){ if(pollargs->events & LWS_POLLOUT){ mux__add_out(mosq); mosq->ws_want_write = true; }else{ mux__remove_out(mosq); } }else{ if(pollargs->events & POLLIN){ /* Assume this is a new listener */ listeners__add_websockets(lws_get_context(wsi), pollargs->fd); } } break; case LWS_CALLBACK_DEL_POLL_FD: HASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq); if(mosq){ mux__delete(mosq); } break; case LWS_CALLBACK_CHANGE_MODE_POLL_FD: HASH_FIND(hh_sock, db.contexts_by_sock, &pollargs->fd, sizeof(pollargs->fd), mosq); if(mosq){ if(pollargs->events & LWS_POLLHUP){ return 1; }else if(pollargs->events & LWS_POLLOUT){ mux__add_out(mosq); mosq->ws_want_write = true; }else{ mux__remove_out(mosq); } } break; #ifdef WITH_TLS case LWS_CALLBACK_OPENSSL_PERFORM_CLIENT_CERT_VERIFICATION: if(!len || (SSL_get_verify_result((SSL*)in) != X509_V_OK)){ return 1; } break; #endif default: return 0; } return 0; } static void log_wrap(int level, const char *line) { char *l = (char *)line; UNUSED(level); l[strlen(line)-1] = '\0'; /* Remove \n */ log__printf(NULL, MOSQ_LOG_WEBSOCKETS, "%s", l); } void mosq_websockets_init(struct mosquitto__listener *listener, const struct mosquitto__config *conf) { struct lws_context_creation_info info; struct lws_protocols *p; size_t protocol_count; int i; struct libws_mqtt_hack *user; /* Count valid protocols */ for(protocol_count=0; protocols[protocol_count].name; protocol_count++); p = mosquitto__calloc(protocol_count+1, sizeof(struct lws_protocols)); if(!p){ log__printf(NULL, MOSQ_LOG_ERR, "Out of memory."); return; } for(i=0; protocols[i].name; i++){ p[i].name = protocols[i].name; p[i].callback = protocols[i].callback; p[i].per_session_data_size = protocols[i].per_session_data_size; p[i].rx_buffer_size = protocols[i].rx_buffer_size; p[i].user = listener; } memset(&info, 0, sizeof(info)); info.iface = listener->host; info.port = listener->port; info.protocols = p; info.gid = -1; info.uid = -1; #ifdef WITH_TLS info.ssl_ca_filepath = listener->cafile; info.ssl_cert_filepath = listener->certfile; info.ssl_private_key_filepath = listener->keyfile; info.ssl_cipher_list = listener->ciphers; #if defined(WITH_WEBSOCKETS) && LWS_LIBRARY_VERSION_NUMBER>=3001000 info.tls1_3_plus_cipher_list = listener->ciphers_tls13; #endif if(listener->require_certificate){ info.options |= LWS_SERVER_OPTION_REQUIRE_VALID_OPENSSL_CLIENT_CERT; } #endif info.options |= LWS_SERVER_OPTION_DO_SSL_GLOBAL_INIT; if(listener->socket_domain == AF_INET){ info.options |= LWS_SERVER_OPTION_DISABLE_IPV6; } info.max_http_header_data = conf->websockets_headers_size; user = mosquitto__calloc(1, sizeof(struct libws_mqtt_hack)); if(!user){ mosquitto__free(p); log__printf(NULL, MOSQ_LOG_ERR, "Out of memory."); return; } if(listener->http_dir){ #ifdef WIN32 user->http_dir = _fullpath(NULL, listener->http_dir, 0); #else user->http_dir = realpath(listener->http_dir, NULL); #endif if(!user->http_dir){ mosquitto__free(user); mosquitto__free(p); log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open http dir \"%s\".", listener->http_dir); return; } } user->listener = listener; info.user = user; info.pt_serv_buf_size = WS_SERV_BUF_SIZE; listener->ws_protocol = p; lws_set_log_level(conf->websockets_log_level, log_wrap); log__printf(NULL, MOSQ_LOG_INFO, "Opening websockets listen socket on port %d.", listener->port); listener->ws_in_init = true; listener->ws_context = lws_create_context(&info); listener->ws_in_init = false; } #endif mosquitto-2.0.18/src/will_delay.c000066400000000000000000000050251450213760600167340ustar00rootroot00000000000000/* Copyright (c) 2019-2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #include "config.h" #include #include #include #include "mosquitto_broker_internal.h" #include "memory_mosq.h" #include "time_mosq.h" static struct will_delay_list *delay_list = NULL; static time_t last_check = 0; static int will_delay__cmp(struct will_delay_list *i1, struct will_delay_list *i2) { return (int)(i1->context->will_delay_interval - i2->context->will_delay_interval); } int will_delay__add(struct mosquitto *context) { struct will_delay_list *item; if(context->will_delay_entry){ return MOSQ_ERR_SUCCESS; } item = mosquitto__calloc(1, sizeof(struct will_delay_list)); if(!item) return MOSQ_ERR_NOMEM; item->context = context; context->will_delay_entry = item; item->context->will_delay_time = db.now_real_s + item->context->will_delay_interval; DL_INSERT_INORDER(delay_list, item, will_delay__cmp); return MOSQ_ERR_SUCCESS; } /* Call on broker shutdown only */ void will_delay__send_all(void) { struct will_delay_list *item, *tmp; DL_FOREACH_SAFE(delay_list, item, tmp){ DL_DELETE(delay_list, item); item->context->will_delay_interval = 0; item->context->will_delay_entry = NULL; context__send_will(item->context); mosquitto__free(item); } } void will_delay__check(void) { struct will_delay_list *item, *tmp; if(db.now_real_s <= last_check) return; last_check = db.now_real_s; DL_FOREACH_SAFE(delay_list, item, tmp){ if(item->context->will_delay_time < db.now_real_s){ DL_DELETE(delay_list, item); item->context->will_delay_interval = 0; item->context->will_delay_entry = NULL; context__send_will(item->context); if(item->context->session_expiry_interval == 0){ context__add_to_disused(item->context); } mosquitto__free(item); }else{ return; } } } void will_delay__remove(struct mosquitto *mosq) { if(mosq->will_delay_entry != NULL){ DL_DELETE(delay_list, mosq->will_delay_entry); mosquitto__free(mosq->will_delay_entry); mosq->will_delay_entry = NULL; } } mosquitto-2.0.18/src/xtreport.c000066400000000000000000000066651450213760600165110ustar00rootroot00000000000000/* Copyright (c) 2020 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause Contributors: Roger Light - initial implementation and documentation. */ #ifdef WITH_XTREPORT /* This file allows reporting of internal parameters to a kcachegrind * compatible output file. It is for debugging purposes only and is most likely * of no interest to end users. */ #include "config.h" #include #include #include "mosquitto_broker_internal.h" #include "mosquitto_internal.h" #include "net_mosq.h" static void client_cost(FILE *fptr, struct mosquitto *context, int fn_index) { long pkt_count, pkt_bytes; long cmsg_count; long cmsg_bytes; struct mosquitto__packet *pkt_tmp; long tBytes; pkt_count = 1; pkt_bytes = context->in_packet.packet_length; if(context->current_out_packet){ pkt_count++; pkt_bytes += context->current_out_packet->packet_length; } pkt_tmp = context->out_packet; while(pkt_tmp){ pkt_count++; pkt_bytes += pkt_tmp->packet_length; pkt_tmp = pkt_tmp->next; } cmsg_count = context->msgs_in.inflight_count + context->msgs_in.queued_count; cmsg_bytes = context->msgs_in.inflight_bytes + context->msgs_in.queued_bytes; cmsg_count += context->msgs_out.inflight_count + context->msgs_out.queued_count; cmsg_bytes += context->msgs_out.inflight_bytes + context->msgs_out.queued_bytes; tBytes = pkt_bytes + cmsg_bytes; if(context->id){ tBytes += (long)strlen(context->id); } fprintf(fptr, "%d %ld %lu %lu %lu %lu %d\n", fn_index, tBytes, pkt_count, cmsg_count, pkt_bytes, cmsg_bytes, context->sock == INVALID_SOCKET?0:context->sock); } void xtreport(void) { pid_t pid; char filename[40]; FILE *fptr; struct mosquitto *context, *ctxt_tmp; int fn_index = 2; static int iter = 1; pid = getpid(); snprintf(filename, 40, "/tmp/xtmosquitto.kcg.%d.%d", pid, iter); iter++; fptr = fopen(filename, "wt"); if(fptr == NULL) return; fprintf(fptr, "# callgrind format\n"); fprintf(fptr, "version: 1\n"); fprintf(fptr, "creator: mosquitto\n"); fprintf(fptr, "pid: %d\n", pid); fprintf(fptr, "cmd: mosquitto\n\n"); fprintf(fptr, "positions: line\n"); fprintf(fptr, "event: tB : total bytes\n"); fprintf(fptr, "event: pkt : currently queued packets\n"); fprintf(fptr, "event: cmsg : currently pending client messages\n"); fprintf(fptr, "event: pktB : currently queued packet bytes\n"); fprintf(fptr, "event: cmsgB : currently pending client message bytes\n"); fprintf(fptr, "events: tB pkt cmsg pktB cmsgB sock\n"); fprintf(fptr, "fn=(1) clients\n"); fprintf(fptr, "1 0 0 0 0 0 0\n"); fn_index = 2; HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){ if(context->id){ fprintf(fptr, "cfn=(%d) %s\n", fn_index, context->id); }else{ fprintf(fptr, "cfn=(%d) unknown\n", fn_index); } fprintf(fptr, "calls=1 %d\n", fn_index); client_cost(fptr, context, fn_index); fn_index++; } fn_index = 2; HASH_ITER(hh_id, db.contexts_by_id, context, ctxt_tmp){ fprintf(fptr, "fn=(%d)\n", fn_index); client_cost(fptr, context, fn_index); fn_index++; } fclose(fptr); } #endif mosquitto-2.0.18/test/000077500000000000000000000000001450213760600146315ustar00rootroot00000000000000mosquitto-2.0.18/test/Makefile000066400000000000000000000005711450213760600162740ustar00rootroot00000000000000include ../config.mk .PHONY: all check test ptest clean all : check : test test : utest $(MAKE) -C broker test $(MAKE) -C lib test $(MAKE) -C client test ptest : utest $(MAKE) -C broker ptest $(MAKE) -C lib ptest $(MAKE) -C client test utest : $(MAKE) -C unit test reallyclean : clean clean : $(MAKE) -C lib clean $(MAKE) -C broker clean $(MAKE) -C unit clean mosquitto-2.0.18/test/broker/000077500000000000000000000000001450213760600161155ustar00rootroot00000000000000mosquitto-2.0.18/test/broker/01-bad-initial-packets.py000077500000000000000000000052711450213760600225220ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether non-CONNECT packets as an initial packet can cause excess memory use from mosq_test_helper import * import psutil def write_config(filename, port): with open(filename, 'w') as f: f.write(f"listener {port}\n") f.write("allow_anonymous true\n") f.write("sys_interval 1\n") def do_send(port, socks, payload): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) socks.append(sock) sock.connect(("127.0.0.1", port)) try: sock.send(payload) except ConnectionResetError: pass def do_test(port): rc = 1 conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) try: socks = [] do_send(port, socks, b"\x20\x80\x80\x80t" + b"\01"*100000000) # CONNACK do_send(port, socks, b"\x30\x80\x80\x80t" + b"\01"*100000000) # PUBLISH do_send(port, socks, b"\x40\x80\x80\x80t" + b"\01"*100000000) # PUBACK do_send(port, socks, b"\x50\x80\x80\x80t" + b"\01"*100000000) # PUBREC do_send(port, socks, b"\x60\x80\x80\x80t" + b"\01"*100000000) # PUBREL do_send(port, socks, b"\x70\x80\x80\x80t" + b"\01"*100000000) # PUBCOMP do_send(port, socks, b"\x80\x80\x80\x80t" + b"\01"*100000000) # SUBSCRIBE do_send(port, socks, b"\x90\x80\x80\x80t" + b"\01"*100000000) # SUBACK do_send(port, socks, b"\xA0\x80\x80\x80t" + b"\01"*100000000) # UNSUBSCRIBE do_send(port, socks, b"\xB0\x80\x80\x80t" + b"\01"*100000000) # UNSUBACK do_send(port, socks, b"\xC0\x80\x80\x80t" + b"\01"*100000000) # PINGREQ do_send(port, socks, b"\xD0\x80\x80\x80t" + b"\01"*100000000) # PINGRESP do_send(port, socks, b"\xE0\x80\x80\x80t" + b"\01"*100000000) # DISCONNECT do_send(port, socks, b"\xF0\x80\x80\x80t" + b"\01"*100000000) # AUTH mem = psutil.Process(broker.pid).memory_info().vms for s in socks: s.close() if os.environ.get('MOSQ_USE_VALGRIND') is None: limit = 25000000 else: limit = 120000000 if mem > limit: raise mosq_test.TestError(f"Process memory {mem} greater than limit of {limit}") rc = 0 except MemoryError: print("Memory error!") except Exception as e: print(e) except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) port = mosq_test.get_port() do_test(port) exit(0) mosquitto-2.0.18/test/broker/01-connect-575314.py000077500000000000000000000031561450213760600211140ustar00rootroot00000000000000#!/usr/bin/env python3 # Check for performance of processing user-property on CONNECT from mosq_test_helper import * def do_test(): rc = 1 props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") for i in range(0, 5000): props += mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") connect_packet_slow = mosq_test.gen_connect("connect-user-property", proto_ver=5, properties=props) connect_packet_fast = mosq_test.gen_connect("a"*65000, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: t_start = time.monotonic() sock = mosq_test.do_client_connect(connect_packet_slow, connack_packet, port=port) t_stop = time.monotonic() sock.close() t_diff_slow = t_stop - t_start t_start = time.monotonic() sock = mosq_test.do_client_connect(connect_packet_fast, connack_packet, port=port) t_stop = time.monotonic() sock.close() t_diff_fast = t_stop - t_start # 20 is chosen as a factor that works in plain mode and running under # valgrind. The slow performance manifests as a factor of >100. Fast is <10. if t_diff_slow / t_diff_fast < 20: rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/01-connect-allow-anonymous.py000077500000000000000000000063511450213760600235100ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether an anonymous connection is correctly denied. from mosq_test_helper import * def write_config1(filename, port): with open(filename, 'w') as f: f.write("max_connections 10\n") # So the file isn't completely empty def write_config2(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) def write_config3(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) def write_config4(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") def write_config5(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") def do_test(use_conf, write_config, expect_success): port = mosq_test.get_port() if write_config is not None: conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=use_conf, port=port) try: for proto_ver in [4, 5]: rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-anon-test-%d" % (proto_ver), keepalive=keepalive, proto_ver=proto_ver) if proto_ver == 5: if expect_success == True: connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) else: connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=proto_ver, properties=None) else: if expect_success == True: connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) else: connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: if write_config is not None: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) # No config file - allow_anonymous should be true do_test(use_conf=False, write_config=None, expect_success=True) # Config file but no listener - allow_anonymous should be true # Not possible right now because the test doesn't allow us to use a config file and -p at the same time. #do_test(use_conf=True, write_config=write_config1, expect_success=True) # Config file with "port" - allow_anonymous should be false do_test(use_conf=True, write_config=write_config2, expect_success=False) # Config file with "listener" - allow_anonymous should be false do_test(use_conf=True, write_config=write_config3, expect_success=False) # Config file with "port" - allow_anonymous explicitly true do_test(use_conf=True, write_config=write_config4, expect_success=True) # Config file with "listener" - allow_anonymous explicitly true do_test(use_conf=True, write_config=write_config5, expect_success=True) exit(0) mosquitto-2.0.18/test/broker/01-connect-disconnect-v5.py000077500000000000000000000042331450213760600230220ustar00rootroot00000000000000#!/usr/bin/env python3 # loop through the different v5 DISCONNECT reason_code/properties options. from mosq_test_helper import * def disco_test(test, disconnect_packet): global rc sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, port=port) mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, "suback1") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port) sock2.send(disconnect_packet) sock2.close() # If this fails then we probably received the will mosq_test.do_ping(sock1) rc -= 1 rc = 4 keepalive = 10 connect1_packet = mosq_test.gen_connect("sub", proto_ver=5, keepalive=keepalive) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "#", 0, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) connect2_packet = mosq_test.gen_connect("connect-disconnect-test", proto_ver=5, keepalive=keepalive, will_topic="failure", will_payload=b"failure") connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # No reason code, no properties, len=0 disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) disco_test("disco len=0", disconnect_packet) # Reason code, no properties, len=1 disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0) disco_test("disco len=1", disconnect_packet) # Reason code, empty properties, len=2 disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0, properties="") disco_test("disco len=2", disconnect_packet) # Reason code, one property, len>2 props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=0, properties=props) disco_test("disco len>2", disconnect_packet) except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if rc != 0: exit(rc) mosquitto-2.0.18/test/broker/01-connect-max-connections.py000077500000000000000000000045671450213760600234600ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether max_connections works with repeated connections from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_connections 10\n") def do_test(): rc = 1 connect_packets_ok = [] connack_packets_ok = [] for i in range(0, 10): connect_packets_ok.append(mosq_test.gen_connect("max-conn-%d"%i, proto_ver=5)) connack_packets_ok.append(mosq_test.gen_connack(rc=0, proto_ver=5)) connect_packet_bad = mosq_test.gen_connect("max-conn-bad", proto_ver=5) connack_packet_bad = b"" port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) socks = [] try: # Open all allowed connections, a limit of 10 for i in range(0, 10): socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port)) # Try to open an 11th connection try: sock_bad = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port) except ConnectionResetError: # Expected behaviour pass # Close all allowed connections for i in range(0, 10): socks[i].close() ## Now repeat - check it works as before # Open all allowed connections, a limit of 10 for i in range(0, 10): socks.append(mosq_test.do_client_connect(connect_packets_ok[i], connack_packets_ok[i], port=port)) # Try to open an 11th connection try: sock_bad = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port) except ConnectionResetError: # Expected behaviour pass # Close all allowed connections for i in range(0, 10): socks[i].close() rc = 0 except mosq_test.TestError: pass except Exception as err: print(err) finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/01-connect-max-keepalive.py000077500000000000000000000023241450213760600230700ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether max_keepalive violations are rejected for MQTT < 5.0. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_keepalive 100\n") def do_test(proto_ver): rc = 1 connect_packet = mosq_test.gen_connect("max-keepalive", keepalive=101, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=2, proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) socks = [] try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass except Exception as err: print(err) finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(3) do_test(4) exit(0) mosquitto-2.0.18/test/broker/01-connect-take-over.py000077500000000000000000000017431450213760600222410ustar00rootroot00000000000000#!/usr/bin/env python3 # MQTT v5 session takeover test from mosq_test_helper import * port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: rc = 1 connect_packet = mosq_test.gen_connect("take-over", proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_SESSION_TAKEN_OVER, proto_ver=5) sock1 = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock2 = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.expect_packet(sock1, "disconnect", disconnect_packet) mosq_test.do_ping(sock2) sock2.close() sock1.close() rc = 0 except mosq_test.TestError: pass except Exception as e: print(e) finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/01-connect-uname-no-password-denied.pwfile000066400000000000000000000001621450213760600257760ustar00rootroot00000000000000user:$6$Ut1cUS9PG8+gC3vn$tOjCfSJJDe1Alu9HktxxyyzwN4+6mAMSWGRAF9gmMN8pzcGTPVEYYMAZpCEp96Oz2ZRRz5YKM6lPMf1tUbb6zA== mosquitto-2.0.18/test/broker/01-connect-uname-no-password-denied.py000077500000000000000000000027661450213760600251570ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is denied if it provides just a username when it # needs a username and password. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("password_file %s\n" % (filename.replace('.conf', '.pwfile'))) f.write("allow_anonymous false\n") def do_test(proto_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-test", keepalive=keepalive, username="user", proto_ver=proto_ver) if proto_ver == 5: connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=proto_ver, properties=None) else: connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/01-connect-uname-or-anon.pwfile000066400000000000000000000001621450213760600236450ustar00rootroot00000000000000user:$6$Ut1cUS9PG8+gC3vn$tOjCfSJJDe1Alu9HktxxyyzwN4+6mAMSWGRAF9gmMN8pzcGTPVEYYMAZpCEp96Oz2ZRRz5YKM6lPMf1tUbb6zA== mosquitto-2.0.18/test/broker/01-connect-uname-or-anon.py000077500000000000000000000060351450213760600230170ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether an anonymous connection is correctly denied. from mosq_test_helper import * def write_config(filename, port, allow_anonymous, password_file): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) if allow_anonymous: f.write("allow_anonymous true\n") else: f.write("allow_anonymous false\n") if password_file: f.write("password_file %s\n" % (filename.replace('.conf', '.pwfile'))) def do_test(allow_anonymous, password_file, username, expect_success): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, allow_anonymous, password_file) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: for proto_ver in [4, 5]: rc = 1 keepalive = 10 if username: connect_packet = mosq_test.gen_connect("connect-test-%d" % (proto_ver), keepalive=keepalive, proto_ver=proto_ver, username="user", password="password") else: connect_packet = mosq_test.gen_connect("connect-test-%d" % (proto_ver), keepalive=keepalive, proto_ver=proto_ver) if proto_ver == 5: if expect_success == True: connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) else: connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=proto_ver, properties=None) else: if expect_success == True: connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) else: connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d, allow_anonymous=%d, password_file=%d, username=%d" % (proto_ver, allow_anonymous, password_file, username)) exit(rc) do_test(allow_anonymous=True, password_file=True, username=True, expect_success=True) do_test(allow_anonymous=True, password_file=True, username=False, expect_success=True) do_test(allow_anonymous=True, password_file=False, username=True, expect_success=True) do_test(allow_anonymous=True, password_file=False, username=False, expect_success=True) do_test(allow_anonymous=False, password_file=True, username=True, expect_success=True) do_test(allow_anonymous=False, password_file=True, username=False, expect_success=False) do_test(allow_anonymous=False, password_file=False, username=True, expect_success=False) do_test(allow_anonymous=False, password_file=False, username=False, expect_success=False) exit(0) mosquitto-2.0.18/test/broker/01-connect-uname-password-denied-no-will.py000077500000000000000000000052011450213760600261070ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is denied if it provides a correct username but # incorrect password. The client has a will, but it should not be sent. Check that. from mosq_test_helper import * def write_config(filename, port, pw_file): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("password_file %s\n" % (pw_file)) f.write("allow_anonymous false\n") def write_pwfile(filename): with open(filename, 'w') as f: # Username user, password password f.write('user:$6$vZY4TS+/HBxHw38S$vvjVFECzb8dyuu/mruD2QKTfdFn0WmKxbc+1TsdB0L8EdHk3v9JRmfjHd56+VaTnUcSZOZ/hzkdvWCtxlX7AUQ==\n') def do_test(proto_ver): pw_file = os.path.basename(__file__).replace('.py', '.pwfile') port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, pw_file) write_pwfile(pw_file) rc = 1 keepalive = 10 connect1_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="user", password="password", will_topic="will/test", will_payload=b"will msg", proto_ver=proto_ver) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, topic="will/test", qos=0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) connect2_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="user", password="password9", proto_ver=proto_ver) if proto_ver == 5: connack2_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=proto_ver, properties=None) else: connack2_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, port=port) mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet) sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port) sock2.close() # If we receive a will here, this is an error mosq_test.do_ping(sock1) sock1.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) os.remove(pw_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/01-connect-uname-password-denied.pwfile000066400000000000000000000001621450213760600253640ustar00rootroot00000000000000user:$6$vZY4TS+/HBxHw38S$vvjVFECzb8dyuu/mruD2QKTfdFn0WmKxbc+1TsdB0L8EdHk3v9JRmfjHd56+VaTnUcSZOZ/hzkdvWCtxlX7AUQ== mosquitto-2.0.18/test/broker/01-connect-uname-password-denied.py000077500000000000000000000030061450213760600245310ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is denied if it provides a correct username but # incorrect password. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("password_file %s\n" % (filename.replace('.conf', '.pwfile'))) f.write("allow_anonymous false\n") def do_test(proto_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="user", password="password9", proto_ver=proto_ver) if proto_ver == 5: connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=proto_ver, properties=None) else: connack_packet = mosq_test.gen_connack(rc=5, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/01-connect-uname-password-success-no-tls.pwfile000066400000000000000000000000161450213760600270140ustar00rootroot00000000000000user:password mosquitto-2.0.18/test/broker/01-connect-uname-password-success-no-tls.py000077500000000000000000000025461450213760600261730ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is denied if it provides a correct username but # incorrect password. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("password_file %s\n" % (filename.replace('.conf', '.pwfile'))) f.write("allow_anonymous false\n") def do_test(proto_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="user", password="password", proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/01-connect-windows-line-endings.py000077500000000000000000000030171450213760600244040ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether config files with windows line endings are accepted. # This just connects anonymously - if the config file causes a failure, the # broker won't start so the connection would fail. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\r\n" % (port)) f.write("allow_anonymous true\r\n") def do_test(): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: for proto_ver in [4, 5]: rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-anon-test-%d" % (proto_ver), keepalive=keepalive, proto_ver=proto_ver) if proto_ver == 5: connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) else: connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/01-connect-zero-length-id.py000077500000000000000000000210511450213760600231660ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a CONNECT with a zero length client id results in the correct behaviour. # MQTT v3.1.1 - zero length is allowed, unless allow_zero_length_clientid is false, and unless clean_start is False. # MQTT v5.0 - zero length is allowed, unless allow_zero_length_clientid is false from mosq_test_helper import * def write_config(filename, port1, port2, per_listener, allow_zero): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("listener %d\n" % (port2)) f.write("allow_anonymous true\n") if allow_zero != "": f.write("allow_zero_length_clientid %s\n" % (allow_zero)) f.write("listener %d\n" % (port1)) f.write("allow_anonymous true\n") if allow_zero != "": f.write("allow_zero_length_clientid %s\n" % (allow_zero)) def do_test(per_listener, proto_ver, clean_start, allow_zero, client_port, expect_fail): conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, per_listener, allow_zero) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("", keepalive=keepalive, proto_ver=proto_ver, clean_session=clean_start) if proto_ver == 4: if expect_fail == True: connack_packet = mosq_test.gen_connack(rc=2, proto_ver=proto_ver) else: connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) else: if expect_fail == True: connack_packet = mosq_test.gen_connack(rc=128, proto_ver=proto_ver, properties=None) else: props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_ASSIGNED_CLIENT_IDENTIFIER, "auto-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx") connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=props) # Remove the "xxxx" part - this means the front part of the packet # is correct (so remaining length etc. is correct), but we don't # need to match against the random id. connack_packet = connack_packet[:-39] broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port1, use_conf=True) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=client_port) sock.close() rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() os.remove(conf_file) if rc: print(stde.decode('utf-8')) 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)) print("port1:%d port2:%d" % (port1, port2)) exit(rc) (port1, port2) = mosq_test.get_port(2) test_v4 = True test_v5 = True if test_v4 == True: do_test(per_listener="false", proto_ver=4, client_port=port1, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="false", proto_ver=4, client_port=port1, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=4, client_port=port1, clean_start=False, allow_zero="true", expect_fail=True) do_test(per_listener="false", proto_ver=4, client_port=port1, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port1, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="true", proto_ver=4, client_port=port1, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port1, clean_start=False, allow_zero="true", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port1, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=4, client_port=port2, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="false", proto_ver=4, client_port=port2, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=4, client_port=port2, clean_start=False, allow_zero="true", expect_fail=True) do_test(per_listener="false", proto_ver=4, client_port=port2, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port2, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="true", proto_ver=4, client_port=port2, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port2, clean_start=False, allow_zero="true", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port2, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=4, client_port=port1, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="false", proto_ver=4, client_port=port1, clean_start=False, allow_zero="", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port1, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="true", proto_ver=4, client_port=port1, clean_start=False, allow_zero="", expect_fail=True) do_test(per_listener="false", proto_ver=4, client_port=port2, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="false", proto_ver=4, client_port=port2, clean_start=False, allow_zero="", expect_fail=True) do_test(per_listener="true", proto_ver=4, client_port=port2, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="true", proto_ver=4, client_port=port2, clean_start=False, allow_zero="", expect_fail=True) if test_v5 == True: do_test(per_listener="false", proto_ver=5, client_port=port1, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="false", proto_ver=5, client_port=port1, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=5, client_port=port1, clean_start=False, allow_zero="true", expect_fail=False) do_test(per_listener="false", proto_ver=5, client_port=port1, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=5, client_port=port1, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port1, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=5, client_port=port1, clean_start=False, allow_zero="true", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port1, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=5, client_port=port2, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="false", proto_ver=5, client_port=port2, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=5, client_port=port2, clean_start=False, allow_zero="true", expect_fail=False) do_test(per_listener="false", proto_ver=5, client_port=port2, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=5, client_port=port2, clean_start=True, allow_zero="true", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port2, clean_start=True, allow_zero="false", expect_fail=True) do_test(per_listener="true", proto_ver=5, client_port=port2, clean_start=False, allow_zero="true", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port2, clean_start=False, allow_zero="false", expect_fail=True) do_test(per_listener="false", proto_ver=5, client_port=port1, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="false", proto_ver=5, client_port=port1, clean_start=False, allow_zero="", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port1, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port1, clean_start=False, allow_zero="", expect_fail=False) do_test(per_listener="false", proto_ver=5, client_port=port2, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="false", proto_ver=5, client_port=port2, clean_start=False, allow_zero="", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port2, clean_start=True, allow_zero="", expect_fail=False) do_test(per_listener="true", proto_ver=5, client_port=port2, clean_start=False, allow_zero="", expect_fail=False) exit(0) mosquitto-2.0.18/test/broker/02-shared-qos0-v5.py000077500000000000000000000140011450213760600213630ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether shared subscriptions work # Client 1 subscribes to #, non shared. Should receive everything. # Client 2 subscribes to $share/one/share-test # Client 3 subscribes to $share/one/share-test and $share/two/share-test # Client 4 subscribes to $share/two/share-test # Client 5 subscribes to $share/one/share-test # A publish to "share-test" should always go to client 1. # The first publish should also go to client 2 (share one) and client 3 (share two) # The second publish should also go to client 3 (share one) and client 4 (share two) # The third publish should also go to client 3 (share two) and client 5 (share one) from mosq_test_helper import * rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("client1", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect2_packet = mosq_test.gen_connect("client2", keepalive=keepalive, proto_ver=5) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect3_packet = mosq_test.gen_connect("client3", keepalive=keepalive, proto_ver=5) connack3_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect4_packet = mosq_test.gen_connect("client4", keepalive=keepalive, proto_ver=5) connack4_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect5_packet = mosq_test.gen_connect("client5", keepalive=keepalive, proto_ver=5) connack5_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe1_packet = mosq_test.gen_subscribe(mid, "#", 0, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) subscribe2_packet = mosq_test.gen_subscribe(mid, "$share/one/share-test", 0, proto_ver=5) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) subscribe3a_packet = mosq_test.gen_subscribe(mid, "$share/one/share-test", 0, proto_ver=5) suback3a_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) subscribe3b_packet = mosq_test.gen_subscribe(mid, "$share/two/share-test", 0, proto_ver=5) suback3b_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) subscribe4_packet = mosq_test.gen_subscribe(mid, "$share/two/share-test", 0, proto_ver=5) suback4_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) subscribe5_packet = mosq_test.gen_subscribe(mid, "$share/one/share-test", 0, proto_ver=5) suback5_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish1_packet = mosq_test.gen_publish("share-test", qos=0, payload="message1", proto_ver=5) publish2_packet = mosq_test.gen_publish("share-test", qos=0, payload="message2", proto_ver=5) publish3_packet = mosq_test.gen_publish("share-test", qos=0, payload="message3", proto_ver=5) mid = 2 unsubscribe1_packet = mosq_test.gen_unsubscribe(mid, "#", proto_ver=5) unsuback1_packet = mosq_test.gen_unsuback(mid, proto_ver=5) unsubscribe2_packet = mosq_test.gen_unsubscribe(mid, "$share/one/share-test", proto_ver=5) unsuback2_packet = mosq_test.gen_unsuback(mid, proto_ver=5) unsubscribe3a_packet = mosq_test.gen_unsubscribe(mid, "$share/one/share-test", proto_ver=5) unsuback3a_packet = mosq_test.gen_unsuback(mid, proto_ver=5) unsubscribe3b_packet = mosq_test.gen_unsubscribe(mid, "$share/two/share-test", proto_ver=5) unsuback3b_packet = mosq_test.gen_unsuback(mid, proto_ver=5) unsubscribe4_packet = mosq_test.gen_unsubscribe(mid, "$share/two/share-test", proto_ver=5) unsuback4_packet = mosq_test.gen_unsuback(mid, proto_ver=5) unsubscribe5_packet = mosq_test.gen_unsubscribe(mid, "$share/one/share-test", proto_ver=5) unsuback5_packet = mosq_test.gen_unsuback(mid, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port) sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) sock3 = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=20, port=port) sock4 = mosq_test.do_client_connect(connect4_packet, connack4_packet, timeout=20, port=port) sock5 = mosq_test.do_client_connect(connect5_packet, connack5_packet, timeout=20, port=port) mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, "suback1") mosq_test.do_send_receive(sock2, subscribe2_packet, suback2_packet, "suback2") mosq_test.do_send_receive(sock3, subscribe3a_packet, suback3a_packet, "suback3a") mosq_test.do_send_receive(sock3, subscribe3b_packet, suback3b_packet, "suback3b") mosq_test.do_send_receive(sock4, subscribe4_packet, suback4_packet, "suback4") mosq_test.do_send_receive(sock5, subscribe5_packet, suback5_packet, "suback5") sock1.send(publish1_packet) mosq_test.expect_packet(sock1, "publish1 1", publish1_packet) mosq_test.expect_packet(sock2, "publish1 2", publish1_packet) mosq_test.expect_packet(sock3, "publish1 3", publish1_packet) sock1.send(publish2_packet) mosq_test.expect_packet(sock1, "publish2 1", publish2_packet) mosq_test.expect_packet(sock3, "publish2 3", publish2_packet) mosq_test.expect_packet(sock4, "publish2 4", publish2_packet) sock1.send(publish3_packet) mosq_test.expect_packet(sock1, "publish3 1", publish3_packet) mosq_test.expect_packet(sock3, "publish3 3", publish3_packet) mosq_test.expect_packet(sock5, "publish3 5", publish3_packet) mosq_test.do_send_receive(sock1, unsubscribe1_packet, unsuback1_packet, "unsuback1") mosq_test.do_send_receive(sock2, unsubscribe2_packet, unsuback2_packet, "unsuback2") mosq_test.do_send_receive(sock3, unsubscribe3a_packet, unsuback3a_packet, "unsuback3a") mosq_test.do_send_receive(sock3, unsubscribe3b_packet, unsuback3b_packet, "unsuback3b") mosq_test.do_send_receive(sock4, unsubscribe4_packet, unsuback4_packet, "unsuback4") mosq_test.do_send_receive(sock5, unsubscribe5_packet, unsuback5_packet, "unsuback5") rc = 0 sock1.close() sock2.close() sock3.close() sock4.close() sock5.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/02-subhier-crash.py000077500000000000000000000027051450213760600214540ustar00rootroot00000000000000#!/usr/bin/env python3 # Test related to https://github.com/eclipse/mosquitto/issues/505 from mosq_test_helper import * rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subhier-crash", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "topic/a", 0) suback1_packet = mosq_test.gen_suback(mid, 0) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid, "topic/b", 0) suback2_packet = mosq_test.gen_suback(mid, 0) mid = 3 unsubscribe1_packet = mosq_test.gen_unsubscribe(mid, "topic/a") unsuback1_packet = mosq_test.gen_unsuback(mid) disconnect_packet = mosq_test.gen_disconnect() port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) def test(): sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback 1") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback 2") mosq_test.do_send_receive(sock, unsubscribe1_packet, unsuback1_packet, "unsuback") sock.send(disconnect_packet) sock.close() try: time.sleep(0.5) test() # Repeat test to check broker is still there test() rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/02-subpub-qos0-long-topic.py000077500000000000000000000031621450213760600231440ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic, for long topics. from mosq_test_helper import * def do_test(topic, succeeds): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) subscribe_packet = mosq_test.gen_subscribe(mid, topic, 0) suback_packet = mosq_test.gen_suback(mid, 0) publish_packet = mosq_test.gen_publish(topic, qos=0, payload="message") port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) if succeeds == True: mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, publish_packet, publish_packet, "publish") else: mosq_test.do_send_receive(sock, subscribe_packet, b"", "suback") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test("/"*200, True) # 200 max hierarchy limit do_test("abc/"*199+"d", True) # 200 max hierarchy limit, longer overall string than 200 do_test("/"*201, False) # Exceeds 200 max hierarchy limit do_test("abc/"*201+"d", False) # Exceeds 200 max hierarchy limit, longer overall string than 200 exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos0-oversize-payload.py000077500000000000000000000047221450213760600243710ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether message size limits apply. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("message_size_limit 1\n") def do_test(proto_ver): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos0", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) connect2_packet = mosq_test.gen_connect("subpub-qos0-helper", keepalive=keepalive, proto_ver=proto_ver) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet_ok = mosq_test.gen_publish("subpub/qos0", qos=0, payload="A", proto_ver=proto_ver) publish_packet_bad = mosq_test.gen_publish("subpub/qos0", qos=0, payload="AB", proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) sock2.send(publish_packet_ok) mosq_test.expect_packet(sock, "publish 1", publish_packet_ok) # Check all is still well on the publishing client mosq_test.do_ping(sock2) sock2.send(publish_packet_bad) # Check all is still well on the publishing client mosq_test.do_ping(sock2) # The subscribing client shouldn't have received a PUBLISH mosq_test.do_ping(sock) rc = 0 sock.close() except SyntaxError: raise except TypeError: raise except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos0-queued-bytes.py000077500000000000000000000041261450213760600235060ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_inflight_messages 20\n") f.write("max_inflight_bytes 1000000\n") f.write("max_queued_messages 20\n") f.write("max_queued_bytes 1000000\n") def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos0-bytes", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connect_packet_helper = mosq_test.gen_connect("qos0-bytes-helper", keepalive=keepalive, proto_ver=proto_ver) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos0/queued/bytes", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) publish_packet0 = mosq_test.gen_publish("subpub/qos0/queued/bytes", qos=0, payload="message", proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=4, port=port, connack_error="connack 1") mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper = mosq_test.do_client_connect(connect_packet_helper, connack_packet, timeout=4, port=port, connack_error="connack helper") helper.send(publish_packet0) mosq_test.expect_packet(sock, "publish0", publish_packet0) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos0-retain-as-publish.py000077500000000000000000000041701450213760600244200ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic with retain-as-published set works as expected. # MQTT v5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 530 subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/normal", 0, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) mid = 531 subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/rap", 0 | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED, proto_ver=5) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish1_packet = mosq_test.gen_publish("subpub/normal", qos=0, retain=True, payload="message", proto_ver=5) publish2_packet = mosq_test.gen_publish("subpub/rap", qos=0, retain=True, payload="message", proto_ver=5) publish1r_packet = mosq_test.gen_publish("subpub/normal", qos=0, retain=False, payload="message", proto_ver=5) publish2r_packet = mosq_test.gen_publish("subpub/rap", qos=0, retain=True, payload="message", proto_ver=5) mid = 1 publish3_packet = mosq_test.gen_publish("subpub/receive", qos=1, mid=mid, payload="success", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") mosq_test.do_send_receive(sock, publish1_packet, publish1r_packet, "publish1") mosq_test.do_send_receive(sock, publish2_packet, publish2r_packet, "publish2") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos0-send-retain.py000077500000000000000000000070561450213760600233100ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether "send retain" subscribe options work # MQTT v5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 530 subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/always", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_ALWAYS, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) mid = 531 subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/new", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_NEW, proto_ver=5) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) mid = 532 subscribe3_packet = mosq_test.gen_subscribe(mid, "subpub/never", 0 | mqtt5_opts.MQTT_SUB_OPT_SEND_RETAIN_NEVER, proto_ver=5) suback3_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish1_packet = mosq_test.gen_publish("subpub/always", qos=0, retain=True, payload="message", proto_ver=5) publish2_packet = mosq_test.gen_publish("subpub/new", qos=0, retain=True, payload="message", proto_ver=5) publish3_packet = mosq_test.gen_publish("subpub/never", qos=0, retain=True, payload="message", proto_ver=5) publish1r1_packet = mosq_test.gen_publish("subpub/always", qos=0, retain=True, payload="message", proto_ver=5) publish1r2_packet = mosq_test.gen_publish("subpub/always", qos=0, retain=True, payload="message", proto_ver=5) publish2r1_packet = mosq_test.gen_publish("subpub/new", qos=0, retain=True, payload="message", proto_ver=5) publish2r2_packet = mosq_test.gen_publish("subpub/new", qos=0, retain=False, payload="message", proto_ver=5) publish3r1_packet = mosq_test.gen_publish("subpub/never", qos=0, retain=False, payload="message", proto_ver=5) publish3r2_packet = mosq_test.gen_publish("subpub/never", qos=0, retain=False, payload="message", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) sock.send(publish1_packet) sock.send(publish2_packet) sock.send(publish3_packet) # Don't expect a message after this mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, "suback3") # Don't expect a message after this mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, "suback3") # Expect a message after this, because it is the first subscribe mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") mosq_test.expect_packet(sock, "publish2r1", publish2r1_packet) # Don't expect a message after this, it is the second subscribe mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") # Always expect a message after this mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.expect_packet(sock, "publish1r1", publish1r1_packet) # Always expect a message after this mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.expect_packet(sock, "publish1r1", publish1r2_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos0-subscription-id.py000077500000000000000000000115411450213760600242070ustar00rootroot00000000000000#!/usr/bin/env python3 # Does setting and updating subscription identifiers work as expected? # MQTT v5 from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 1) subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/id1", 0, proto_ver=5, properties=props) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) mid = 2 props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 14) subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/+/id2", 0, proto_ver=5, properties=props) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) mid = 3 subscribe3_packet = mosq_test.gen_subscribe(mid, "subpub/noid", 0, proto_ver=5) suback3_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) # Updated version of subscribe1, now without a subscription identifier mid = 4 subscribe1u_packet = mosq_test.gen_subscribe(mid, "subpub/id1", 0, proto_ver=5) suback1u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) # Updated version of subscribe2, with a new subscription identifier mid = 5 props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 19) subscribe2u_packet = mosq_test.gen_subscribe(mid, "subpub/+/id2", 0, proto_ver=5, properties=props) suback2u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) # Updated version of subscribe3, now with a subscription identifier mid = 6 props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 21) subscribe3u_packet = mosq_test.gen_subscribe(mid, "subpub/noid", 0, proto_ver=5, properties=props) suback3u_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish1_packet = mosq_test.gen_publish("subpub/id1", qos=0, payload="message1", proto_ver=5) props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 1) publish1r_packet = mosq_test.gen_publish("subpub/id1", qos=0, payload="message1", proto_ver=5, properties=props) publish2_packet = mosq_test.gen_publish("subpub/test/id2", qos=0, payload="message2", proto_ver=5) props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 14) publish2r_packet = mosq_test.gen_publish("subpub/test/id2", qos=0, payload="message2", proto_ver=5, properties=props) publish3_packet = mosq_test.gen_publish("subpub/noid", qos=0, payload="message3", proto_ver=5) # Updated version of publish1r, now with no id publish1ru_packet = mosq_test.gen_publish("subpub/id1", qos=0, payload="message1", proto_ver=5) # Updated verison of publish2r, with updated id props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 19) publish2ru_packet = mosq_test.gen_publish("subpub/test/id2", qos=0, payload="message2", proto_ver=5, properties=props) # Updated version of publish3r, now with an id props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 21) publish3ru_packet = mosq_test.gen_publish("subpub/noid", qos=0, payload="message3", proto_ver=5, properties=props) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") mosq_test.do_send_receive(sock, subscribe3_packet, suback3_packet, "suback3") mosq_test.do_send_receive(sock, publish3_packet, publish3_packet, "publish3") mosq_test.do_send_receive(sock, publish2_packet, publish2r_packet, "publish2") mosq_test.do_send_receive(sock, publish1_packet, publish1r_packet, "publish1") # Now update the subscription identifiers mosq_test.do_send_receive(sock, subscribe1u_packet, suback1u_packet, "suback1u") mosq_test.do_send_receive(sock, subscribe2u_packet, suback2u_packet, "suback2u") mosq_test.do_send_receive(sock, subscribe3u_packet, suback3u_packet, "suback3u") mosq_test.do_send_receive(sock, publish2_packet, publish2ru_packet, "publish2u") mosq_test.do_send_receive(sock, publish3_packet, publish3ru_packet, "publish3u") mosq_test.do_send_receive(sock, publish1_packet, publish1ru_packet, "publish1u") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos0-topic-alias-unknown.py000077500000000000000000000022761450213760600250000ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether "topic alias" works to the broker # MQTT v5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("sub-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 3) publish1_packet = mosq_test.gen_publish("", qos=0, payload="message", proto_ver=5, properties=props) disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_PROTOCOL_ERROR, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) sock.send(publish1_packet) mosq_test.expect_packet(sock, "disconnect", disconnect_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos0-topic-alias.py000077500000000000000000000036531450213760600233030ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether "topic alias" works to the broker # MQTT v5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 connect1_packet = mosq_test.gen_connect("sub-test", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect2_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, proto_ver=5) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/alias", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 3) publish1_packet = mosq_test.gen_publish("subpub/alias", qos=0, payload="message", proto_ver=5, properties=props) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 3) publish2s_packet = mosq_test.gen_publish("", qos=0, payload="message", proto_ver=5, properties=props) publish2r_packet = mosq_test.gen_publish("subpub/alias", qos=0, payload="message", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port) sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port) sock1.send(publish1_packet) mosq_test.do_send_receive(sock2, subscribe_packet, suback_packet, "suback") sock1.send(publish2s_packet) mosq_test.expect_packet(sock2, "publish2r", publish2r_packet) rc = 0 sock1.close() sock2.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit() mosquitto-2.0.18/test/broker/02-subpub-qos1-message-expiry-retain.py000077500000000000000000000073241450213760600253200ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether the broker reduces the message expiry interval when republishing # a retained message, and eventually removes it. # MQTT v5 # Helper publishes a message, with a medium length expiry with retained set. It # publishes a second message with retained set but no expiry. # Client connects, subscribes, gets messages, disconnects. # We wait until the expiry will have expired. # Client connects, subscribes, doesn't get expired message, does get # non-expired message. from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/expired", 1, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/kept", 1, proto_ver=5) suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) helper_connect = mosq_test.gen_connect("helper", proto_ver=5) helper_connack = mosq_test.gen_connack(rc=0, proto_ver=5) mid=1 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 4) publish1_packet = mosq_test.gen_publish("subpub/expired", mid=mid, qos=1, retain=True, payload="message1", proto_ver=5, properties=props) puback1_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid=2 publish2s_packet = mosq_test.gen_publish("subpub/kept", mid=mid, qos=1, retain=True, payload="message2", proto_ver=5) puback2s_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid=1 publish2r_packet = mosq_test.gen_publish("subpub/kept", mid=mid, qos=1, retain=True, payload="message2", proto_ver=5) puback2r_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) mosq_test.do_send_receive(helper, publish1_packet, puback1_packet, "puback 1") mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, "puback 2") helper.close() sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback 1-1") mosq_test.expect_packet(sock, "publish 1", publish1_packet) sock.send(puback1_packet) mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback 2-1") mosq_test.expect_packet(sock, "publish 2", publish2s_packet) sock.send(puback2s_packet) sock.close() time.sleep(5) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback 1-2") # We shouldn't receive a publish here # This will fail if we do receive a publish mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback 2-2") mosq_test.expect_packet(sock, "publish 2", publish2r_packet) sock.send(puback2r_packet) sock.close() rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos1-message-expiry-will.py000077500000000000000000000055601450213760600250050ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether the broker reduces the message expiry interval when republishing a will. # MQTT v5 # Client connects with clean session set false, subscribes with qos=1, then disconnects # Helper publishes two messages, one with a short expiry and one with a long expiry # We wait until the short expiry will have expired but the long one not. # Client reconnects, expects delivery of the long expiry message with a reduced # expiry interval property. from mosq_test_helper import * def do_test(): rc = 1 mid = 53 keepalive = 60 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 60) connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5, clean_session=False, properties=props) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 10) helper_connect = mosq_test.gen_connect("helper", proto_ver=5, will_topic="subpub/qos1", will_qos=1, will_payload=b"message", will_properties=props, keepalive=2) helper_connack = mosq_test.gen_connack(rc=0, proto_ver=5) #mid=2 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 10) publish2s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message2", proto_ver=5, properties=props) puback2s_packet = mosq_test.gen_puback(mid) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.close() helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) time.sleep(2) sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port) packet = sock.recv(len(publish2s_packet)) for i in range(10, 5, -1): props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, i) publish2r_packet = mosq_test.gen_publish("subpub/qos1", mid=1, qos=1, payload="message", proto_ver=5, properties=props) if packet == publish2r_packet: rc = 0 break sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos1-message-expiry.py000077500000000000000000000061611450213760600240360ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether the broker reduces the message expiry interval when republishing. # MQTT v5 # Client connects with clean session set false, subscribes with qos=1, then disconnects # Helper publishes two messages, one with a short expiry and one with a long expiry # We wait until the short expiry will have expired but the long one not. # Client reconnects, expects delivery of the long expiry message with a reduced # expiry interval property. from mosq_test_helper import * def do_test(): rc = 1 mid = 53 keepalive = 60 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 60) connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5, clean_session=False, properties=props) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) helper_connect = mosq_test.gen_connect("helper", proto_ver=5) helper_connack = mosq_test.gen_connack(rc=0, proto_ver=5) mid=1 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 1) publish1s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message1", proto_ver=5, properties=props) puback1s_packet = mosq_test.gen_puback(mid) mid=2 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 10) publish2s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message2", proto_ver=5, properties=props) puback2s_packet = mosq_test.gen_puback(mid) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.close() helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) mosq_test.do_send_receive(helper, publish1s_packet, puback1s_packet, "puback 1") mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, "puback 2") time.sleep(2) sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port) packet = sock.recv(len(publish2s_packet)) for i in range(9, 5, -1): props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, i) publish2r_packet = mosq_test.gen_publish("subpub/qos1", mid=2, qos=1, payload="message2", proto_ver=5, properties=props) if packet == publish2r_packet: rc = 0 break sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos1-nolocal.py000077500000000000000000000041241450213760600225200ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic does not receive its own message # sent to that topic if no local is set. # MQTT v5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 530 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1 | mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 531 subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/receive", 1, proto_ver=5) suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 300 publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) puback_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 301 publish2_packet = mosq_test.gen_publish("subpub/receive", qos=1, mid=mid, payload="success", proto_ver=5) puback2_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 1 publish3_packet = mosq_test.gen_publish("subpub/receive", qos=1, mid=mid, payload="success", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") sock.send(publish2_packet) mosq_test.receive_unordered(sock, puback2_packet, publish3_packet, "puback2/publish3") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos1-oversize-payload.py000077500000000000000000000055451450213760600243760ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether message size limits apply. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("message_size_limit 1\n") def do_test(proto_ver): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) connect2_packet = mosq_test.gen_connect("subpub-qos1-helper", keepalive=keepalive, proto_ver=proto_ver) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 publish_packet_ok = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="A", proto_ver=proto_ver) puback_packet_ok = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver) mid = 2 publish_packet_bad = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="AB", proto_ver=proto_ver) if proto_ver == 5: puback_packet_bad = mosq_test.gen_puback(reason_code=mqtt5_rc.MQTT_RC_PACKET_TOO_LARGE, mid=mid, proto_ver=proto_ver) else: puback_packet_bad = mosq_test.gen_puback(mid=mid, proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) mosq_test.do_send_receive(sock2, publish_packet_ok, puback_packet_ok, "puback 1") mosq_test.expect_packet(sock, "publish 1", publish_packet_ok) sock.send(puback_packet_ok) # Check all is still well on the publishing client mosq_test.do_ping(sock2) mosq_test.do_send_receive(sock2, publish_packet_bad, puback_packet_bad, "puback 2") # The subscribing client shouldn't have received a PUBLISH mosq_test.do_ping(sock) rc = 0 sock.close() except SyntaxError: raise except TypeError: raise except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos1.py000077500000000000000000000032171450213760600210750ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 530 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos1-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 300 publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 1 publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.send(publish_packet) mosq_test.receive_unordered(sock, puback_packet, publish_packet2, "puback/publish2") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos2-1322.py000077500000000000000000000143611450213760600214650ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for issue 1322: ## restart mosquitto #sudo systemctl restart mosquitto.service # ## listen on topic1 #mosquitto_sub -t "topic1" # ## publish to topic1 without clean session #mosquitto_pub -t "topic1" -q 2 -c --id "foobar" -m "message1" ## message1 on topic1 is received as expected # ## publish to topic2 without clean session ## IMPORTANT: no subscription to this topic is present on broker! #mosquitto_pub -t "topic2" -q 2 -c --id "foobar" -m "message2" ## this goes nowhere, as no subscriber present # ## publish to topic1 without clean session #mosquitto_pub -t "topic1" -q 2 -c --id "foobar" -m "message3" ## message3 on topic1 IS NOT RECEIVED # ## listen on topic2 #mosquitto_sub -t "topic2" # ## publish to topic1 without clean session #mosquitto_pub -t "topic1" -q 2 -c --id "foobar" -m "message4" ## message2 on topic2 is received incorrectly # ## publish to topic1 without clean session #mosquitto_pub -t "topic1" -q 2 -c --id "foobar" -m "message5" ## message5 on topic1 is received as expected (message4 was dropped) from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 pub_connect_packet = mosq_test.gen_connect("pub", keepalive=keepalive, clean_session=False, proto_ver=proto_ver, session_expiry=60) pub_connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) pub_connack2_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver) sub1_connect_packet = mosq_test.gen_connect("sub1", keepalive=keepalive, proto_ver=proto_ver) sub1_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) sub2_connect_packet = mosq_test.gen_connect("sub2", keepalive=keepalive, proto_ver=proto_ver) sub2_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "topic1", 0, proto_ver=proto_ver) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) mid = 1 subscribe2_packet = mosq_test.gen_subscribe(mid, "topic2", 0, proto_ver=proto_ver) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) # All publishes have the same mid mid = 1 pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) publish1s_packet = mosq_test.gen_publish("topic1", qos=2, mid=mid, payload="message1", proto_ver=proto_ver) publish2s_packet = mosq_test.gen_publish("topic2", qos=2, mid=mid, payload="message2", proto_ver=proto_ver) publish3s_packet = mosq_test.gen_publish("topic1", qos=2, mid=mid, payload="message3", proto_ver=proto_ver) publish4s_packet = mosq_test.gen_publish("topic1", qos=2, mid=mid, payload="message4", proto_ver=proto_ver) publish5s_packet = mosq_test.gen_publish("topic1", qos=2, mid=mid, payload="message5", proto_ver=proto_ver) publish1r_packet = mosq_test.gen_publish("topic1", qos=0, payload="message1", proto_ver=proto_ver) publish2r_packet = mosq_test.gen_publish("topic2", qos=0, payload="message2", proto_ver=proto_ver) publish3r_packet = mosq_test.gen_publish("topic1", qos=0, payload="message3", proto_ver=proto_ver) publish4r_packet = mosq_test.gen_publish("topic1", qos=0, payload="message4", proto_ver=proto_ver) publish5r_packet = mosq_test.gen_publish("topic1", qos=0, payload="message5", proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sub1 = mosq_test.do_client_connect(sub1_connect_packet, sub1_connack_packet, timeout=10, port=port) mosq_test.do_send_receive(sub1, subscribe1_packet, suback1_packet, "suback1") pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack1_packet, timeout=10, port=port) mosq_test.do_send_receive(pub, publish1s_packet, pubrec_packet, "pubrec1") mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, "pubcomp1") pub.close() mosq_test.expect_packet(sub1, "publish1", publish1r_packet) pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port) mosq_test.do_send_receive(pub, publish2s_packet, pubrec_packet, "pubrec2") mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, "pubcomp2") pub.close() # We expect nothing on sub1 mosq_test.do_ping(sub1, error_string="pingresp1") pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port) mosq_test.do_send_receive(pub, publish3s_packet, pubrec_packet, "pubrec3") mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, "pubcomp3") pub.close() mosq_test.expect_packet(sub1, "publish3", publish3r_packet) sub2 = mosq_test.do_client_connect(sub2_connect_packet, sub2_connack_packet, timeout=10, port=port) mosq_test.do_send_receive(sub2, subscribe2_packet, suback2_packet, "suback2") pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port) mosq_test.do_send_receive(pub, publish4s_packet, pubrec_packet, "pubrec4") mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, "pubcomp4") pub.close() # We expect nothing on sub2 mosq_test.do_ping(sub2, error_string="pingresp2") mosq_test.expect_packet(sub1, "publish4", publish4r_packet) pub = mosq_test.do_client_connect(pub_connect_packet, pub_connack2_packet, timeout=10, port=port) mosq_test.do_send_receive(pub, publish5s_packet, pubrec_packet, "pubrec5") mosq_test.do_send_receive(pub, pubrel_packet, pubcomp_packet, "pubcomp5") pub.close() # We expect nothing on sub2 mosq_test.do_ping(sub2, error_string="pingresp2") mosq_test.expect_packet(sub1, "publish5", publish5r_packet) rc = 0 sub2.close() sub1.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos2-max-inflight-bytes.py000077500000000000000000000154201450213760600246060ustar00rootroot00000000000000#!/usr/bin/env python3 # Does the broker respect max_inflight_bytes? # Also check whether the send quota is dealt with properly when both # RECEIVE-MAXIMUM and max_inflight_bytes are set. # MQTT v5 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_inflight_bytes 16\n") def send_small(port): rc = 1 connect_packet = mosq_test.gen_connect("subpub-qos2-test-helper") connack_packet = mosq_test.gen_connack(rc=0) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) for i in range(0, 10): mid = 1+i publish_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload=str(i+1)) pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") def do_test(proto_ver): if proto_ver == 4: exit(0) rc = 1 keepalive = 60 props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 5) connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive, proto_ver=5, properties=props) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Repeat many times to stress the send quota mid = 0 for i in range(0, 12): pub = subprocess.Popen(['./02-subpub-qos2-receive-maximum-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) pub.wait() (stdo, stde) = pub.communicate() mid += 1 publish_packet1 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message1", proto_ver=5) pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid += 1 publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message2", proto_ver=5) pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid += 1 publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message3", proto_ver=5) pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5) mosq_test.expect_packet(sock, "publish1", publish_packet1) mosq_test.expect_packet(sock, "publish2", publish_packet2) mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, "pubrel1") sock.send(pubcomp_packet1) mosq_test.expect_packet(sock, "publish3", publish_packet3) mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2") sock.send(pubcomp_packet2) mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, "pubrel3") sock.send(pubcomp_packet3) # send messages where count will exceed max_inflight_messages, but the # payload bytes won't exceed max_inflight_bytes send_small(port) mid += 1 publish_packet1 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="1", proto_ver=5) pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid += 1 publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="2", proto_ver=5) pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid += 1 publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="3", proto_ver=5) pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid += 1 publish_packet4 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="4", proto_ver=5) pubrec_packet4 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet4 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet4 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid += 1 publish_packet5 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="5", proto_ver=5) pubrec_packet5 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet5 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet5 = mosq_test.gen_pubcomp(mid, proto_ver=5) mosq_test.expect_packet(sock, "publish1s", publish_packet1) mosq_test.expect_packet(sock, "publish2s", publish_packet2) mosq_test.expect_packet(sock, "publish3s", publish_packet3) mosq_test.expect_packet(sock, "publish4s", publish_packet4) mosq_test.expect_packet(sock, "publish5s", publish_packet5) mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, "pubrel1s") mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2s") mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, "pubrel3s") mosq_test.do_send_receive(sock, pubrec_packet4, pubrel_packet4, "pubrel4s") mosq_test.do_send_receive(sock, pubrec_packet5, pubrel_packet5, "pubrel5s") rc = 0 sock.close() except mosq_test.TestError: pass except Exception as e: print(e) finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: #print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos2-pubrec-error.py000077500000000000000000000062121450213760600235010ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBREC with reason code >= 0x80 is handled correctly from mosq_test_helper import * def helper(port): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 publish_1_packet = mosq_test.gen_publish("qos2/pubrec/rejected", qos=2, mid=mid, payload="rejected-message") pubrec_1_packet = mosq_test.gen_pubrec(mid) pubrel_1_packet = mosq_test.gen_pubrel(mid) pubcomp_1_packet = mosq_test.gen_pubcomp(mid) mid = 2 publish_2_packet = mosq_test.gen_publish("qos2/pubrec/accepted", qos=2, mid=mid, payload="accepted-message") pubrec_2_packet = mosq_test.gen_pubrec(mid) pubrel_2_packet = mosq_test.gen_pubrel(mid) pubcomp_2_packet = mosq_test.gen_pubcomp(mid) sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) mosq_test.do_send_receive(sock, publish_1_packet, pubrec_1_packet, "helper pubrec") mosq_test.do_send_receive(sock, pubrel_1_packet, pubcomp_1_packet, "helper pubcomp") mosq_test.do_send_receive(sock, publish_2_packet, pubrec_2_packet, "helper pubrec") mosq_test.do_send_receive(sock, pubrel_2_packet, pubcomp_2_packet, "helper pubcomp") sock.close() def do_test(): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qo2-timeout-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/pubrec/+", 2, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) mid = 1 publish_1_packet = mosq_test.gen_publish("qos2/pubrec/rejected", qos=2, mid=mid, payload="rejected-message", proto_ver=5) pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5, reason_code=0x80) mid = 2 publish_2_packet = mosq_test.gen_publish("qos2/pubrec/accepted", qos=2, mid=mid, payload="accepted-message", proto_ver=5) pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper(port) # Should have now received a publish command mosq_test.expect_packet(sock, "publish 1", publish_1_packet) sock.send(pubrec_1_packet) mosq_test.expect_packet(sock, "publish 2", publish_2_packet) mosq_test.do_send_receive(sock, pubrec_2_packet, pubrel_2_packet, "pubrel 2") sock.send(pubcomp_2_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos2-receive-maximum-1.py000077500000000000000000000054171450213760600243330ustar00rootroot00000000000000#!/usr/bin/env python3 # Does the broker respect receive maximum==1? # MQTT v5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive, proto_ver=5, properties=props) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) mid = 1 publish_packet1 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message1", proto_ver=5) pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 2 publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message2", proto_ver=5) pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 3 publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message3", proto_ver=5) pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") pub = subprocess.Popen(['./02-subpub-qos2-receive-maximum-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) pub.wait() (stdo, stde) = pub.communicate() mosq_test.expect_packet(sock, "publish1", publish_packet1) mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, "pubrel1") sock.send(pubcomp_packet1) mosq_test.expect_packet(sock, "publish2", publish_packet2) mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2") sock.send(pubcomp_packet2) mosq_test.expect_packet(sock, "publish3", publish_packet3) mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, "pubrel3") sock.send(pubcomp_packet3) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos2-receive-maximum-2.py000077500000000000000000000055731450213760600243370ustar00rootroot00000000000000#!/usr/bin/env python3 # Does the broker respect receive maximum==2? # MQTT v5 from mosq_test_helper import * def do_test(proto_ver): if proto_ver == 4: exit(0) rc = 1 keepalive = 60 props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 2) connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive, proto_ver=5, properties=props) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) mid = 1 publish_packet1 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message1", proto_ver=5) pubrec_packet1 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet1 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet1 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 2 publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message2", proto_ver=5) pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 3 publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message3", proto_ver=5) pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") pub = subprocess.Popen(['./02-subpub-qos2-receive-maximum-helper.py', str(port)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) pub.wait() (stdo, stde) = pub.communicate() mosq_test.expect_packet(sock, "publish1", publish_packet1) mosq_test.expect_packet(sock, "publish2", publish_packet2) mosq_test.do_send_receive(sock, pubrec_packet1, pubrel_packet1, "pubrel1") sock.send(pubcomp_packet1) mosq_test.expect_packet(sock, "publish3", publish_packet3) mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2") sock.send(pubcomp_packet2) mosq_test.do_send_receive(sock, pubrec_packet3, pubrel_packet3, "pubrel3") sock.send(pubcomp_packet3) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-qos2-receive-maximum-helper.py000077500000000000000000000042541450213760600254500ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. # MQTT v5 from mosq_test_helper import * def do_test(proto_ver): if proto_ver == 4: exit(0) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos2-test-helper", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 publish_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message1", proto_ver=5) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 2 publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message2", proto_ver=5) pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 3 publish_packet3 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message3", proto_ver=5) pubrec_packet3 = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_packet3 = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_packet3 = mosq_test.gen_pubcomp(mid, proto_ver=5) port = mosq_test.get_port() try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") mosq_test.do_send_receive(sock, publish_packet2, pubrec_packet2, "pubrec2") mosq_test.do_send_receive(sock, pubrel_packet2, pubcomp_packet2, "pubcomp2") mosq_test.do_send_receive(sock, publish_packet3, pubrec_packet3, "pubrec3") mosq_test.do_send_receive(sock, pubrel_packet3, pubcomp_packet3, "pubcomp3") rc = 0 sock.close() except mosq_test.TestError: pass finally: if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=5) exit(rc) mosquitto-2.0.18/test/broker/02-subpub-qos2.py000077500000000000000000000043471450213760600211030ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 530 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos2-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos2", 2, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 301 publish_packet = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message", proto_ver=proto_ver) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) mid = 1 publish_packet2 = mosq_test.gen_publish("subpub/qos2", qos=2, mid=mid, payload="message", proto_ver=proto_ver) pubrec_packet2 = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet2 = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet2 = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") sock.send(pubrel_packet) mosq_test.receive_unordered(sock, pubcomp_packet, publish_packet2, "pubcomp/publish2") mosq_test.do_send_receive(sock, pubrec_packet2, pubrel_packet2, "pubrel2") sock.send(pubcomp_packet2) # Broker side of flow complete so can quit here. rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subpub-recover-subscriptions.py000077500000000000000000000061001450213760600245560ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether a durable client keeps its subscriptions on reconnecting. from mosq_test_helper import * def publish_helper(port): connect_packet = mosq_test.gen_connect("subpub-sub-helper") connack_packet = mosq_test.gen_connack(rc=0) publish1_packet = mosq_test.gen_publish("not-shared/sub", qos=0, payload="message1") publish2_packet = mosq_test.gen_publish("shared/sub", qos=0, payload="message2") sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) sock.send(publish1_packet) sock.send(publish2_packet) sock.close() def do_test(proto_ver): rc = 1 if proto_ver == 5: props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 60) connect_packet = mosq_test.gen_connect("subpub-sub-test", proto_ver=proto_ver, clean_session=False, properties=props) else: connect_packet = mosq_test.gen_connect("subpub-sub-test", proto_ver=proto_ver, clean_session=False) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, flags=1) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "not-shared/sub", 0, proto_ver=proto_ver) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid, "$share/name/shared/sub", 0, proto_ver=proto_ver) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish1_packet = mosq_test.gen_publish("not-shared/sub", qos=0, payload="message1", proto_ver=proto_ver) publish2_packet = mosq_test.gen_publish("shared/sub", qos=0, payload="message2", proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=2, port=port, connack_error="connack 1") mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") publish_helper(port) mosq_test.expect_packet(sock, "publish1", publish1_packet) if proto_ver == 5: mosq_test.expect_packet(sock, "publish2", publish2_packet) sock.close() # Reconnect, but don't resubscribe sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=2, port=port, connack_error="connack 2") publish_helper(port) mosq_test.expect_packet(sock, "publish1", publish1_packet) if proto_ver == 5: mosq_test.expect_packet(sock, "publish2", publish2_packet) sock.close() rc = 0 sock.close() except mosq_test.TestError: pass except Exception as err: print(err) finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subscribe-dollar-v5.py000077500000000000000000000025461450213760600225040ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a SUBSCRIBE to $SYS or $share succeeds from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subscribe-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "$SYS/broker/missing", 0, proto_ver=proto_ver) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid, "$share/share/#", 0, proto_ver=proto_ver) suback2_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) exit(0) mosquitto-2.0.18/test/broker/02-subscribe-invalid-utf8.py000077500000000000000000000031551450213760600232060ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a SUBSCRIBE to a topic with an invalid UTF-8 topic fails from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("subscribe-invalid-utf8", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "invalid/utf8", 0, proto_ver=proto_ver) b = list(struct.unpack("B"*len(subscribe_packet), subscribe_packet)) b[13] = 0 # Topic should never have a 0x0000 subscribe_packet = struct.pack("B"*len(b), *b) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: time.sleep(0.5) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) if proto_ver == 4: mosq_test.do_send_receive(sock, subscribe_packet, b"", "suback") else: disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code = mqtt5_rc.MQTT_RC_MALFORMED_PACKET) mosq_test.do_send_receive(sock, subscribe_packet, disconnect_packet, "suback") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subscribe-long-topic.py000077500000000000000000000030351450213760600227440ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a SUBSCRIBE to a topic with 65535 hierarchy characters fails # This needs checking with MOSQ_USE_VALGRIND=1 to detect memory failures # https://github.com/eclipse/mosquitto/issues/1412 from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subscribe-long-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "/"*65535, 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) if proto_ver == 4: mosq_test.do_send_receive(sock, subscribe_packet, b"", "suback") else: disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code = mqtt5_rc.MQTT_RC_MALFORMED_PACKET) mosq_test.do_send_receive(sock, subscribe_packet, disconnect_packet, "suback") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/02-subscribe-persistence-flipflop.py000077500000000000000000000062111450213760600250250ustar00rootroot00000000000000#!/usr/bin/env python3 # Test switching between persistence and a clean session. # # Bug #874: # # # mosquitto_sub -i sub -t 'topic' -v -p 29883 -q 1 -d -c # ^C # mosquitto_sub -i sub -t 'topic' -v -p 29883 -q 1 -d # ^C # # SUBSCRIBE to topic is no longer respected by mosquitto # # run: # # mosquitto_sub -i sub -t 'topic' -v -p 29883 -q 1 -d -c # # and in a separate shell # # mosquitto_pub -i pub -t topic -m 'hello' -p 29883 -q 1 # # sub does not receive the message from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet_sub_persistent = mosq_test.gen_connect("flipflop-test", keepalive=keepalive, clean_session=False, proto_ver=proto_ver) connect_packet_sub_clean = mosq_test.gen_connect("flipflop-test", keepalive=keepalive, clean_session=True, proto_ver=proto_ver) connack_packet_sub = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connect_packet_pub = mosq_test.gen_connect("flipflop-test-pub", keepalive=keepalive, proto_ver=proto_ver) connack_packet_pub = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid=1 subscribe_packet = mosq_test.gen_subscribe(mid, "flipflop/test", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid=1 publish_packet = mosq_test.gen_publish("flipflop/test", qos=1, mid=mid, payload="message", proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # mosquitto_sub -i sub -t 'topic' -q 1 -d -c sub_sock = mosq_test.do_client_connect(connect_packet_sub_persistent, connack_packet_sub, port=port) mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, "subscribe persistent 1") # And disconnect sub_sock.close() # mosquitto_sub -i sub -t 'topic' -q 1 -d sub_sock = mosq_test.do_client_connect(connect_packet_sub_clean, connack_packet_sub, port=port) mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, "subscribe clean") # And disconnect sub_sock.close() # mosquitto_sub -i sub -t 'topic' -v -q 1 -d -c sub_sock = mosq_test.do_client_connect(connect_packet_sub_persistent, connack_packet_sub, port=port) mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, "subscribe persistent 2") # and in a separate shell # # mosquitto_pub -i pub -t topic -m 'hello' -p 29883 -q 1 pub_sock = mosq_test.do_client_connect(connect_packet_pub, connack_packet_pub, port=port) mosq_test.do_send_receive(pub_sock, publish_packet, puback_packet, "publish") mosq_test.expect_packet(sub_sock, "publish receive", publish_packet) rc = 0 sub_sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-pattern-matching.py000077500000000000000000000060001450213760600221530ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def helper(port, pub_topic): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60) connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish(pub_topic, qos=0, retain=True, payload="message") sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) sock.send(publish_packet) sock.close() def pattern_test(sub_topic, pub_topic): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pattern-sub-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish(pub_topic, qos=0, payload="message") publish_retained_packet = mosq_test.gen_publish(pub_topic, qos=0, retain=True, payload="message") mid = 312 subscribe_packet = mosq_test.gen_subscribe(mid, sub_topic, 0) suback_packet = mosq_test.gen_suback(mid, 0) mid = 234; unsubscribe_packet = mosq_test.gen_unsubscribe(mid, sub_topic) unsuback_packet = mosq_test.gen_unsuback(mid) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper(port, pub_topic) mosq_test.expect_packet(sock, "publish", publish_packet) mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, "unsuback") mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish retained", publish_retained_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print(stdo.decode('utf-8')) sys.exit(rc) return rc pattern_test("#", "test/topic") pattern_test("#", "/test/topic") pattern_test("foo/#", "foo/bar/baz") pattern_test("foo/+/baz", "foo/bar/baz") pattern_test("foo/+/baz/#", "foo/bar/baz") pattern_test("foo/+/baz/#", "foo/bar/baz/bar") pattern_test("foo/foo/baz/#", "foo/foo/baz/bar") pattern_test("foo/#", "foo") pattern_test("foo/#", "foo/") pattern_test("/#", "/foo") pattern_test("test/topic/", "test/topic/") pattern_test("test/topic/+", "test/topic/") pattern_test("+/+/+/+/+/+/+/+/+/+/test", "one/two/three/four/five/six/seven/eight/nine/ten/test") pattern_test("#", "test////a//topic") pattern_test("#", "/test////a//topic") pattern_test("foo/#", "foo//bar///baz") pattern_test("foo/+/baz", "foo//baz") pattern_test("foo/+/baz//", "foo//baz//") pattern_test("foo/+/baz/#", "foo//baz") pattern_test("foo/+/baz/#", "foo//baz/bar") pattern_test("foo//baz/#", "foo//baz/bar") pattern_test("foo/foo/baz/#", "foo/foo/baz/bar") pattern_test("/#", "////foo///bar") exit(0) mosquitto-2.0.18/test/broker/03-publish-b2c-disconnect-qos1.py000077500000000000000000000061441450213760600240410ustar00rootroot00000000000000#!/usr/bin/env python3 # Does an interrupted QoS 1 flow from broker to client get handled correctly? from mosq_test_helper import * def helper(port): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60) connack_packet = mosq_test.gen_connack(rc=0) mid = 128 publish_packet = mosq_test.gen_publish("qos1/disconnect/test", qos=1, mid=mid, payload="disconnect-message") puback_packet = mosq_test.gen_puback(mid) sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "helper puback") sock.close() def do_test(proto_ver): port = mosq_test.get_port() rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-disco-test", keepalive=keepalive, clean_session=False, proto_ver=proto_ver, session_expiry=60) connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver) connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/disconnect/test", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("qos1/disconnect/test", qos=1, mid=mid, payload="disconnect-message", proto_ver=proto_ver) publish_dup_packet = mosq_test.gen_publish("qos1/disconnect/test", qos=1, mid=mid, payload="disconnect-message", dup=True, proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 3266 publish2_packet = mosq_test.gen_publish("qos1/outgoing", qos=1, mid=mid, payload="outgoing-message", proto_ver=proto_ver) puback2_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper(port) # Should have now received a publish command mosq_test.expect_packet(sock, "publish", publish_packet) # Send our outgoing message. When we disconnect the broker # should get rid of it and assume we're going to retry. sock.send(publish2_packet) sock.close() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(60) # 60 seconds timeout is much longer than 5 seconds message retry. sock.connect(("localhost", port)) mosq_test.do_send_receive(sock, connect_packet, connack2_packet, "connack") mosq_test.expect_packet(sock, "dup publish", publish_dup_packet) sock.send(puback_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-b2c-disconnect-qos2.py000077500000000000000000000067331450213760600240460ustar00rootroot00000000000000#!/usr/bin/env python3 # Does an interrupted QoS 1 flow from broker to client get handled correctly? from mosq_test_helper import * def helper(port): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60) connack_packet = mosq_test.gen_connack(rc=0) mid = 312 publish_packet = mosq_test.gen_publish("qos2/disconnect/test", qos=2, mid=mid, payload="disconnect-message") pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "helper pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "helper pubcomp") sock.close() def do_test(proto_ver): rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos2-disco-test", keepalive=keepalive, clean_session=False, proto_ver=proto_ver, session_expiry=60) connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver) connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/disconnect/test", 2, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("qos2/disconnect/test", qos=2, mid=mid, payload="disconnect-message", proto_ver=proto_ver) publish_dup_packet = mosq_test.gen_publish("qos2/disconnect/test", qos=2, mid=mid, payload="disconnect-message", dup=True, proto_ver=proto_ver) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) mid = 3266 publish2_packet = mosq_test.gen_publish("qos1/outgoing", qos=1, mid=mid, payload="outgoing-message", proto_ver=proto_ver) puback2_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper(port) # Should have now received a publish command mosq_test.expect_packet(sock, "publish", publish_packet) # Send our outgoing message. When we disconnect the broker # should get rid of it and assume we're going to retry. sock.send(publish2_packet) sock.close() sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port) mosq_test.expect_packet(sock, "dup publish", publish_dup_packet) mosq_test.do_send_receive(sock, pubrec_packet, pubrel_packet, "pubrel") sock.close() sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port) mosq_test.expect_packet(sock, "dup pubrel", pubrel_packet) sock.send(pubcomp_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-b2c-qos1-len.py000077500000000000000000000051671450213760600224720ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether the broker handles a v5 PUBACK with all combinations # of with/without reason code and properties. from mosq_test_helper import * def helper(port): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 publish_packet = mosq_test.gen_publish("qos1/len/test", qos=1, mid=mid, payload="len-message") puback_packet = mosq_test.gen_puback(mid) sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "helper puback") sock.close() def len_test(test, puback_packet): rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, clean_session=False, proto_ver=5) connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/len/test", 1, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 1 publish_packet = mosq_test.gen_publish("qos1/len/test", qos=1, mid=mid, payload="len-message", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper(port) # Should have now received a publish command mosq_test.expect_packet(sock, "publish", publish_packet) sock.send(puback_packet) mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if rc != 0: print(test) exit(rc) # No reason code, no properties puback_packet = mosq_test.gen_puback(1) len_test("qos1 len 2", puback_packet) # Reason code, no properties puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00) len_test("qos1 len 3", puback_packet) # Reason code, empty properties puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties="") len_test("qos1 len 4", puback_packet) # Reason code, one property props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties=props) len_test("qos1 len >5", puback_packet) mosquitto-2.0.18/test/broker/03-publish-b2c-qos2-len.py000077500000000000000000000065771450213760600225010ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether the broker handles a v5 PUBREC, PUBCOMP with all combinations # of with/without reason code and properties. from mosq_test_helper import * def helper(port): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 publish_packet = mosq_test.gen_publish("qos2/len/test", qos=2, mid=mid, payload="len-message") pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) sock = mosq_test.do_client_connect(connect_packet, connack_packet, connack_error="helper connack", port=port) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "helper pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "helper pubcomp") sock.close() def len_test(test, pubrec_packet, pubcomp_packet): rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, clean_session=False, proto_ver=5) connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/len/test", 2, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) mid = 1 publish_packet = mosq_test.gen_publish("qos2/len/test", qos=2, mid=mid, payload="len-message", proto_ver=5) pubrel_packet = mosq_test.gen_pubrel(mid) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper(port) # Should have now received a publish command mosq_test.expect_packet(sock, "publish", publish_packet) mosq_test.do_send_receive(sock, pubrec_packet, pubrel_packet, "pubrel") sock.send(pubcomp_packet) mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if rc != 0: print(test) exit(rc) # No reason code, no properties pubrec_packet = mosq_test.gen_pubrec(1) pubcomp_packet = mosq_test.gen_pubcomp(1) len_test("qos2 len 2", pubrec_packet, pubcomp_packet) # Reason code, no properties pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00) pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00) len_test("qos2 len 3", pubrec_packet, pubcomp_packet) # Reason code, empty properties pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties="") pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties="") len_test("qos2 len 4", pubrec_packet, pubcomp_packet) # Reason code, one property props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties=props) props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties=props) len_test("qos2 len >5", pubrec_packet, pubcomp_packet) mosquitto-2.0.18/test/broker/03-publish-c2b-disconnect-qos2.py000077500000000000000000000063061450213760600240420ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos2-disco-test", keepalive=keepalive, clean_session=False, proto_ver=proto_ver, session_expiry=60) connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver) if proto_ver == 3: connack2_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver) else: connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver) helper_connect_packet = mosq_test.gen_connect("pub-qos2-disco-helper", keepalive=keepalive, clean_session=True, proto_ver=proto_ver) helper_connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/disconnect/test", 2, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("qos2/disconnect/test", qos=2, mid=mid, payload="disconnect-message", proto_ver=proto_ver) publish_dup_packet = mosq_test.gen_publish("qos2/disconnect/test", qos=2, mid=mid, payload="disconnect-message", dup=True, proto_ver=proto_ver) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) if proto_ver == 3: pubrel_dup_packet = mosq_test.gen_pubrel(mid, dup=True, proto_ver=proto_ver) else: pubrel_dup_packet = mosq_test.gen_pubrel(mid, dup=False, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # Add a subscriber, so we ensure that the QoS 2 flow must be completed helper = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port) mosq_test.do_send_receive(helper, subscribe_packet, suback_packet) sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") # We're now going to disconnect and pretend we didn't receive the pubrec. sock.close() sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error="connack 2") sock.send(publish_dup_packet) mosq_test.expect_packet(sock, "pubrec", pubrec_packet) mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") # Again, pretend we didn't receive this pubcomp sock.close() sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port) mosq_test.do_send_receive(sock, pubrel_dup_packet, pubcomp_packet, "pubcomp") rc = 0 sock.close() helper.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=3) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-c2b-qos2-len.py000077500000000000000000000037351450213760600224720ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether the broker handles a v5 PUBREL with all combinations # of with/without reason code and properties. from mosq_test_helper import * def len_test(test, pubrel_packet): rc = 1 mid = 3265 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, clean_session=False, proto_ver=5) connack_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5) mid = 1 publish_packet = mosq_test.gen_publish("qos2/len/test", qos=2, mid=mid, payload="len-message", proto_ver=5) pubrec_packet = mosq_test.gen_pubrec(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if rc != 0: print(test) exit(rc) # No reason code, no properties pubrel_packet = mosq_test.gen_pubrel(1) len_test("qos2 len 2", pubrel_packet) # Reason code, no properties pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00) len_test("qos2 len 3", pubrel_packet) # Reason code, empty properties pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00, properties="") len_test("qos2 len 4", pubrel_packet) # Reason code, one property props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") pubrel_packet = mosq_test.gen_pubrel(1, proto_ver=5, reason_code=0x00, properties=props) len_test("qos2 len >5", pubrel_packet) mosquitto-2.0.18/test/broker/03-publish-dollar-v5.py000077500000000000000000000027561450213760600221750ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to $ topics QoS 1 results in the expected PUBACK packet. from mosq_test_helper import * mid = 1 def helper(topic, reason_code): global mid publish_packet = mosq_test.gen_publish(topic, qos=1, mid=mid, payload="message", proto_ver=5) if reason_code == 0: puback_packet = mosq_test.gen_puback(mid, proto_ver=5) else: puback_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=reason_code) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback%d"%(mid)) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) helper("$SYS/broker/uptime", mqtt5_rc.MQTT_RC_NOT_AUTHORIZED) helper("$SYS/broker/connection/me", mqtt5_rc.MQTT_RC_NOT_AUTHORIZED) helper("$SYS/broker/connection/me/state", mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) helper("$share/share/topic", mqtt5_rc.MQTT_RC_NOT_AUTHORIZED) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/03-publish-dollar.py000077500000000000000000000015751450213760600216430ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic starting with $ succeeds from mosq_test_helper import * rc = 1 mid = 19 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-dollar-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish("$test/test", qos=1, mid=mid, payload="message") puback_packet = mosq_test.gen_puback(mid) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/03-publish-invalid-utf8.py000077500000000000000000000031331450213760600226700ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with an invalid UTF-8 topic fails from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-invalid-utf8", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("invalid/utf8", 1, mid=mid, proto_ver=proto_ver) b = list(struct.unpack("B"*len(publish_packet), publish_packet)) b[11] = 0 # Topic should never have a 0x0000 publish_packet = struct.pack("B"*len(b), *b) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: time.sleep(0.5) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) if proto_ver == 4: mosq_test.do_send_receive(sock, publish_packet, b"", "puback") else: disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_MALFORMED_PACKET) mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "puback") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-long-topic.py000077500000000000000000000030461450213760600224340ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with 65535 hierarchy characters fails # This needs checking with MOSQ_USE_VALGRIND=1 to detect memory failures # https://github.com/eclipse/mosquitto/issues/1412 from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 19 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("/"*65535, qos=1, mid=mid, payload="message", proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) if proto_ver == 4: mosq_test.do_send_receive(sock, publish_packet, b"", "puback") else: disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_MALFORMED_PACKET) mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "puback") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-qos1-max-inflight-expire.py000077500000000000000000000100311450213760600251130ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with QoS 1 results in the correct packet flow for a subscriber. # With max_inflight_messages set to 1 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_inflight_messages 1\n") def do_test(proto_ver): rc = 1 keepalive = 60 properties = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 1000) sub_connect_packet = mosq_test.gen_connect("sub", keepalive=keepalive, properties=properties, proto_ver=proto_ver, clean_session=False) properties = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) \ + mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) sub_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False) sub_connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver, properties=properties, property_helper=False) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "pub/qos1/test", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, proto_ver=proto_ver) properties = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) \ + mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False) mid = 311 properties = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 1) publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=proto_ver, properties=properties) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 1 r_publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=proto_ver) r_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sub_sock = mosq_test.do_client_connect(sub_connect_packet, sub_connack_packet, port=port, timeout=10) mosq_test.do_send_receive(sub_sock, subscribe_packet, suback_packet, "suback") sub_sock.close() sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, timeout=10) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") time.sleep(2) sub_sock = mosq_test.do_client_connect(sub_connect_packet, sub_connack_packet2, port=port, timeout=10) # This message has expired, so we don't expect it #mosq_test.expect_packet(sub_sock, "publish 2", r_publish_packet) #sub_sock.send(r_puback_packet) # mid = 1 s_publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message2", proto_ver=proto_ver) s_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mosq_test.do_send_receive(sock, s_publish_packet, s_puback_packet, "puback") mid = 2 r_publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message2", proto_ver=proto_ver) r_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mosq_test.expect_packet(sub_sock, "publish 3", r_publish_packet) sub_sock.send(r_puback_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-qos1-max-inflight.py000077500000000000000000000035261450213760600236340ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with QoS 1 results in the correct packet flow. # With max_inflight_messages set to 1 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_inflight_messages 1\n") def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, proto_ver=proto_ver) properties = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) \ + mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False) mid = 311 publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS, proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, timeout=10) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-qos1-no-subscribers-v5.py000077500000000000000000000057461450213760600245430ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with QoS 1 results in the correct PUBACK # packet when there are no subscribers. from mosq_test_helper import * rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 publish1_packet = mosq_test.gen_publish("pub", qos=1, mid=mid, payload="message", proto_ver=5) puback1_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid = 2 publish2_packet = mosq_test.gen_publish("pub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) puback2_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid = 3 publish3_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=5) puback3_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid = 4 publish4_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=5, retain=True) puback4_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid = 5 publish1b_packet = mosq_test.gen_publish("pub", qos=1, mid=mid, payload="message", proto_ver=5) puback1b_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid = 6 publish2b_packet = mosq_test.gen_publish("pub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) puback2b_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) mid = 7 publish3b_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=5) puback3b_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) # None of the pub/qos1/test topic tree exists here mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback1a") mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, "puback2a") mosq_test.do_send_receive(sock, publish3_packet, puback3_packet, "puback3a") # This publish sets a retained message, which means the topic tree exists mosq_test.do_send_receive(sock, publish4_packet, puback4_packet, "puback4") # So now test again mosq_test.do_send_receive(sock, publish1b_packet, puback1b_packet, "puback1b") mosq_test.do_send_receive(sock, publish2b_packet, puback2b_packet, "puback2b") mosq_test.do_send_receive(sock, publish3b_packet, puback3b_packet, "puback3b") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/03-publish-qos1-queued-bytes.conf000066400000000000000000000001041450213760600241400ustar00rootroot00000000000000sys_interval 1 max_queued_messages 0 max_queued_bytes 400 port 1888 mosquitto-2.0.18/test/broker/03-publish-qos1-queued-bytes.py000077500000000000000000000123201450213760600236510ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with an offline subscriber results in a queued message import Queue import random import string import subprocess import socket import threading import time try: import paho.mqtt.client import paho.mqtt.publish except ImportError: print("WARNING: paho.mqtt module not available, skipping byte count test.") exit(0) from mosq_test_helper import * rc = 1 port = mosq_test.get_port() def registerOfflineSubscriber(): """Just a durable client to trigger queuing""" client = paho.mqtt.client.Client("sub-qos1-offline", clean_session=False) client.connect("localhost", port=port) client.subscribe("test/publish/queueing/#", 1) client.loop() client.disconnect() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) class BrokerMonitor(threading.Thread): def __init__(self, group=None, target=None, name=None, args=(), kwargs=None, verbose=None): threading.Thread.__init__(self, group=group, target=target, name=name, verbose=verbose) self.rq, self.cq = args self.stored = -1 self.stored_bytes = -1 self.dropped = -1 def store_count(self, client, userdata, message): self.stored = int(message.payload) def store_bytes(self, client, userdata, message): self.stored_bytes = int(message.payload) def publish_dropped(self, client, userdata, message): self.dropped = int(message.payload) def run(self): client = paho.mqtt.client.Client("broker-monitor") client.connect("localhost", port=port) client.message_callback_add("$SYS/broker/store/messages/count", self.store_count) client.message_callback_add("$SYS/broker/store/messages/bytes", self.store_bytes) client.message_callback_add("$SYS/broker/publish/messages/dropped", self.publish_dropped) client.subscribe("$SYS/broker/store/messages/#") client.subscribe("$SYS/broker/publish/messages/dropped") while True: expect_drops = cq.get() self.cq.task_done() if expect_drops == "quit": break first = time.time() while self.stored < 0 or self.stored_bytes < 0 or (expect_drops and self.dropped < 0): client.loop(timeout=0.5) if time.time() - 10 > first: print("ABORT TIMEOUT") break if expect_drops: self.rq.put((self.stored, self.stored_bytes, self.dropped)) else: self.rq.put((self.stored, self.stored_bytes, 0)) self.stored = -1 self.stored_bytes = -1 self.dropped = -1 client.disconnect() rq = Queue.Queue() cq = Queue.Queue() brokerMonitor = BrokerMonitor(args=(rq,cq)) class StoreCounts(): def __init__(self): self.stored = 0 self.bstored = 0 self.drops = 0 self.diff_stored = 0 self.diff_bstored = 0 self.diff_drops = 0 def update(self, tup): self.diff_stored = tup[0] - self.stored self.stored = tup[0] self.diff_bstored = tup[1] - self.bstored self.bstored = tup[1] self.diff_drops = tup[2] - self.drops self.drops = tup[2] def __repr__(self): 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) try: registerOfflineSubscriber() time.sleep(2.5) # Wait for first proper dump of stats brokerMonitor.start() counts = StoreCounts() cq.put(True) # Expect a dropped count (of 0, initial) counts.update(rq.get()) # Initial start print("rq.get (INITIAL) gave us: ", counts) rq.task_done() # publish 10 short messages, should be no drops print("publishing 10 short") cq.put(False) # expect no updated drop count msgs_short10 = [("test/publish/queueing/%d" % x, ''.join(random.choice(string.hexdigits) for _ in range(10)), 1, False) for x in range(1, 10 + 1)] paho.mqtt.publish.multiple(msgs_short10, port=port) counts.update(rq.get()) # Initial start print("rq.get (short) gave us: ", counts) rq.task_done() if counts.diff_stored != 10 or counts.diff_bstored < 100: raise ValueError if counts.diff_drops != 0: raise ValueError # publish 10 mediums (40bytes). should fail after 8, when it finally crosses 400 print("publishing 10 medium") cq.put(True) # expect a drop count msgs_medium10 = [("test/publish/queueing/%d" % x, ''.join(random.choice(string.hexdigits) for _ in range(40)), 1, False) for x in range(1, 10 + 1)] paho.mqtt.publish.multiple(msgs_medium10, port=port) counts.update(rq.get()) # Initial start print("rq.get (medium) gave us: ", counts) rq.task_done() if counts.diff_stored != 8 or counts.diff_bstored < 320: raise ValueError if counts.diff_drops != 2: raise ValueError rc = 0 except mosq_test.TestError: pass finally: cq.put("quit") brokerMonitor.join() rq.join() cq.join() broker.terminate() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/03-publish-qos1-retain-disabled.py000077500000000000000000000033321450213760600242670ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH with a retain set when retains are disabled results in # the correct DISCONNECT. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("retain_available false\n") def do_test(proto_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 mid = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_RETAIN_AVAILABLE, 0) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", retain=True, proto_ver=5) puback_packet = mosq_test.gen_puback(mid, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(reason_code=154, proto_ver=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "disconnect") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-qos1.py000077500000000000000000000025541450213760600212470ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with QoS 1 results in the correct PUBACK packet. from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 19 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos1-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=proto_ver) if proto_ver == 5: puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) else: puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-qos2-dup.py000077500000000000000000000033701450213760600220330ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def do_test(proto_ver): rc = 1 connect_packet = mosq_test.gen_connect("03-pub-qos2-dup-test", proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="message", proto_ver=proto_ver, dup=1) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) disconnect_packet = mosq_test.gen_disconnect(reason_code=130, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec 1") mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec 2") if proto_ver == 5: mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "disconnect") rc = 0 else: try: mosq_test.do_send_receive(sock, publish_packet, b"", "disconnect1") rc = 0 except BrokenPipeError: rc = 0 sock.close() except Exception as e: print(e) except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) def all_tests(): rc = do_test(proto_ver=4) if rc: return rc; rc = do_test(proto_ver=5) if rc: return rc; return 0 if __name__ == '__main__': all_tests() mosquitto-2.0.18/test/broker/03-publish-qos2-max-inflight.py000077500000000000000000000037731450213760600236410ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. # With max_inflight_messages set to 1 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_inflight_messages 1\n") def do_test(proto_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos2-test", keepalive=keepalive, proto_ver=proto_ver) properties = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) \ + mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, properties=properties, property_helper=False) mid = 312 publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", proto_ver=proto_ver) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, timeout=10) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/03-publish-qos2.py000077500000000000000000000026421450213760600212460ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a PUBLISH to a topic with QoS 2 results in the correct packet flow. from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("pub-qos2-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 312 publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", proto_ver=proto_ver) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(sock, pubrel_packet, pubcomp_packet, "pubcomp") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/04-retain-check-source-persist-diff-port.py000077500000000000000000000107151450213760600261310ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for CVE-2018-12546, with the broker being stopped to write the persistence file, plus subscriber on different port. from mosq_test_helper import * import os.path import signal def write_config(filename, port1, port2, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("check_retain_source true\n") f.write("port %d\n" % (port1)) f.write("allow_anonymous true\n") f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) f.write("persistence true\n") f.write("persistence_file %s\n" % (filename.replace('.conf', '.db'))) f.write("listener %d\n" % (port2)) f.write("allow_anonymous true\n") def write_acl_1(filename, username): with open(filename, 'w') as f: if username is not None: f.write('user %s\n' % (username)) f.write('topic readwrite test/topic\n') def write_acl_2(filename, username): with open(filename, 'w') as f: if username is not None: f.write('user %s\n' % (username)) f.write('topic read test/topic\n') def do_test(proto_ver, per_listener, username): conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, per_listener) persistence_file = os.path.basename(__file__).replace('.py', '.db') try: os.remove(persistence_file) except OSError: pass acl_file = os.path.basename(__file__).replace('.py', '.acl') write_acl_1(acl_file, username) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("retain-check", keepalive=keepalive, username=username, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) if per_listener == "true": u = None else: # If per listener is false, then the second client will be denied # unless we provide a username u = username connect2_packet = mosq_test.gen_connect("retain-recv", keepalive=keepalive, username=u, proto_ver=proto_ver) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port1) sock.send(publish_packet) sock.close() sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 1") mosq_test.expect_packet(sock, "publish", publish_packet) sock.close() # Remove "write" ability write_acl_2(acl_file, username) broker.terminate() broker.wait() if os.path.isfile(persistence_file) == False: raise FileNotFoundError("Persistence file not written") broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1) sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port2) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") # If we receive the retained message here, it is a failure. mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() os.remove(conf_file) os.remove(acl_file) try: os.remove(persistence_file) except FileNotFoundError: pass (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) (port1, port2) = mosq_test.get_port(2) do_test(proto_ver=4, per_listener="true", username=None) do_test(proto_ver=4, per_listener="true", username="test") do_test(proto_ver=4, per_listener="false", username=None) do_test(proto_ver=4, per_listener="false", username="test") do_test(proto_ver=5, per_listener="true", username=None) do_test(proto_ver=5, per_listener="true", username="test") do_test(proto_ver=5, per_listener="false", username=None) do_test(proto_ver=5, per_listener="false", username="test") mosquitto-2.0.18/test/broker/04-retain-check-source-persist.py000077500000000000000000000073511450213760600242430ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for CVE-2018-12546, with the broker being stopped to write the persistence file. from mosq_test_helper import * import signal def write_config(filename, port, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("check_retain_source true\n") f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) f.write("persistence true\n") f.write("persistence_file %s\n" % (filename.replace('.conf', '.db'))) def write_acl_1(filename, username): with open(filename, 'w') as f: if username is not None: f.write('user %s\n' % (username)) f.write('topic readwrite test/topic\n') def write_acl_2(filename, username): with open(filename, 'w') as f: if username is not None: f.write('user %s\n' % (username)) f.write('topic read test/topic\n') def do_test(proto_ver, per_listener, username): conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, per_listener) persistence_file = os.path.basename(__file__).replace('.py', '.db') try: os.remove(persistence_file) except OSError: pass acl_file = os.path.basename(__file__).replace('.py', '.acl') write_acl_1(acl_file, username) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("retain-check", keepalive=keepalive, username=username, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.send(publish_packet) sock.close() sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 1") mosq_test.expect_packet(sock, "publish", publish_packet) sock.close() # Remove "write" ability write_acl_2(acl_file, username) broker.terminate() broker.wait() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") # If we receive the retained message here, it is a failure. mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() os.remove(conf_file) os.remove(acl_file) os.remove(persistence_file) (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) port = mosq_test.get_port() do_test(proto_ver=4, per_listener="true", username=None) do_test(proto_ver=4, per_listener="true", username="test") do_test(proto_ver=4, per_listener="false", username=None) do_test(proto_ver=4, per_listener="false", username="test") do_test(proto_ver=5, per_listener="true", username=None) do_test(proto_ver=5, per_listener="true", username="test") do_test(proto_ver=5, per_listener="false", username=None) do_test(proto_ver=5, per_listener="false", username="test") mosquitto-2.0.18/test/broker/04-retain-check-source.py000077500000000000000000000054001450213760600225450ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for CVE-2018-12546 from mosq_test_helper import * import signal def write_config(filename, port, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("check_retain_source true\n") f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) def write_acl_1(filename): with open(filename, 'w') as f: f.write('topic readwrite test/topic\n') def write_acl_2(filename): with open(filename, 'w') as f: f.write('topic read test/topic\n') def do_test(proto_ver, per_listener): conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, per_listener) acl_file = os.path.basename(__file__).replace('.py', '.acl') write_acl_1(acl_file) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("retain-check", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.send(publish_packet) sock.close() sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 1") mosq_test.expect_packet(sock, "publish", publish_packet) sock.close() # Remove "write" ability write_acl_2(acl_file) broker.send_signal(signal.SIGHUP) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") # If we receive the retained message here, it is a failure. mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) os.remove(acl_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) port = mosq_test.get_port() do_test(proto_ver=4, per_listener="true") do_test(proto_ver=4, per_listener="false") do_test(proto_ver=5, per_listener="true") do_test(proto_ver=5, per_listener="false") mosquitto-2.0.18/test/broker/04-retain-clear-multiple.py000077500000000000000000000057201450213760600231160ustar00rootroot00000000000000#!/usr/bin/env python3 # Exercise multi-level retain clearing from mosq_test_helper import * def send_retain(port, topic, payload): connect_packet = mosq_test.gen_connect("retain-clear-test") connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish(topic, qos=1, mid=1, payload=payload, retain=True) puback_packet = mosq_test.gen_puback(mid=1) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=4, port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, f"set retain {topic}") sock.close() def do_test(): rc = 1 connect_packet = mosq_test.gen_connect("retain-clear-test") connack_packet = mosq_test.gen_connack(rc=0) subscribe_packet = mosq_test.gen_subscribe(1, "#", 0) suback_packet = mosq_test.gen_suback(1, 0) retain1_packet = mosq_test.gen_publish("1/2/3/4/5/6/7", qos=0, payload="retained message", retain=True) retain2_packet = mosq_test.gen_publish("1/2/3/4", qos=0, payload="retained message", retain=True) retain3_packet = mosq_test.gen_publish("1", qos=0, payload="retained message", retain=True) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: send_retain(port, "1/2/3/4/5/6/7", "retained message") send_retain(port, "1/2/3/4", "retained message") send_retain(port, "1", "retained message") sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "retain3", retain3_packet) mosq_test.expect_packet(sock, "retain2", retain2_packet) mosq_test.expect_packet(sock, "retain1", retain1_packet) sock.close() send_retain(port, "1/2/3/4", None) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "retain3", retain3_packet) mosq_test.expect_packet(sock, "retain1", retain1_packet) sock.close() send_retain(port, "1/2/3/4/5/6/7", None) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "retain3", retain3_packet) sock.close() send_retain(port, "1", None) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_ping(sock) sock.close() rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() exit(0) mosquitto-2.0.18/test/broker/04-retain-qos0-clear.py000077500000000000000000000050071450213760600221430ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a retained PUBLISH is cleared when a zero length retained # message is published to a topic. from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("retain-clear-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("retain/clear/test", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) retain_clear_packet = mosq_test.gen_publish("retain/clear/test", qos=0, payload=None, retain=True, proto_ver=proto_ver) mid_sub = 592 subscribe_packet = mosq_test.gen_subscribe(mid_sub, "retain/clear/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid_sub, 0, proto_ver=proto_ver) mid_unsub = 593 unsubscribe_packet = mosq_test.gen_unsubscribe(mid_unsub, "retain/clear/test", proto_ver=proto_ver) unsuback_packet = mosq_test.gen_unsuback(mid_unsub, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=4, port=port) # Send retained message sock.send(publish_packet) # Subscribe to topic, we should get the retained message back. mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish", publish_packet) # Now unsubscribe from the topic before we clear the retained # message. mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, "unsuback") # Now clear the retained message. sock.send(retain_clear_packet) # Subscribe to topic, we shouldn't get anything back apart # from the SUBACK. mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") time.sleep(1) # If we do get something back, it should be before this ping, so if # this succeeds then we're ok. mosq_test.do_ping(sock) # This is the expected event rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/04-retain-qos0-fresh.py000077500000000000000000000030751450213760600221670ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a retained PUBLISH to a topic with QoS 0 is sent with # retain=false to an already subscribed client. from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 mid = 16 connect_packet = mosq_test.gen_connect("retain-qos0-fresh-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) publish_fresh_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "retain/qos0/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, publish_packet, publish_fresh_packet, "publish") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/04-retain-qos0-repeated.py000077500000000000000000000036461450213760600226550ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a retained PUBLISH to a topic with QoS 0 is actually retained # and delivered when multiple sub/unsub operations are carried out. from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 mid = 16 connect_packet = mosq_test.gen_connect("retain-qos0-rep-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "retain/qos0/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) unsub_mid = 13 unsubscribe_packet = mosq_test.gen_unsubscribe(unsub_mid, "retain/qos0/test", proto_ver=proto_ver) unsuback_packet = mosq_test.gen_unsuback(unsub_mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) sock.send(publish_packet) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish", publish_packet) mosq_test.do_send_receive(sock, unsubscribe_packet, unsuback_packet, "unsuback") mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/04-retain-qos0.py000077500000000000000000000026351450213760600210630ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a retained PUBLISH to a topic with QoS 0 is actually retained. from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 mid = 16 connect_packet = mosq_test.gen_connect("retain-qos0-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "retain/qos0/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.send(publish_packet) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/04-retain-qos1-qos0.py000077500000000000000000000036301450213760600217400ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a retained PUBLISH to a topic with QoS 1 is retained. # Subscription is made with QoS 0 so the retained message should also have QoS # 0. from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("retain-qos1-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 6 publish_packet = mosq_test.gen_publish("retain/qos1/test", qos=1, mid=mid, payload="retained message", retain=True, proto_ver=proto_ver) if proto_ver == 5: puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS) else: puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 18 subscribe_packet = mosq_test.gen_subscribe(mid, "retain/qos1/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish0_packet = mosq_test.gen_publish("retain/qos1/test", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish0", publish0_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/04-retain-upgrade-outgoing-qos.py000077500000000000000000000036541450213760600242630ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a retained PUBLISH to a topic with QoS 0 is sent with subscriber QoS # when upgrade_outgoing_qos is true from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("upgrade_outgoing_qos true\n") def do_test(proto_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 60 mid = 16 connect_packet = mosq_test.gen_connect("retain-qos0-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "retain/qos0/test", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) publish_packet2 = mosq_test.gen_publish("retain/qos0/test", mid=1, qos=1, payload="retained message", retain=True, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.send(publish_packet) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish", publish_packet2) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/05-clean-session-qos1.py000077500000000000000000000046351450213760600223500ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a clean session client has a QoS 1 message queued for it. from mosq_test_helper import * def helper(port): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60) connack_packet = mosq_test.gen_connack(rc=0) mid = 128 publish_packet = mosq_test.gen_publish("qos1/clean_session/test", qos=1, mid=mid, payload="clean-session-message") puback_packet = mosq_test.gen_puback(mid) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") sock.close() def do_test(proto_ver): rc = 1 mid = 109 keepalive = 60 connect_packet = mosq_test.gen_connect("clean-qos2-test", keepalive=keepalive, clean_session=False, proto_ver=proto_ver, session_expiry=60) connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=proto_ver) connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=proto_ver) disconnect_packet = mosq_test.gen_disconnect(proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/clean_session/test", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("qos1/clean_session/test", qos=1, mid=mid, payload="clean-session-message", proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.send(disconnect_packet) sock.close() helper(port) # Now reconnect and expect a publish message. sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=30, port=port) mosq_test.expect_packet(sock, "publish", publish_packet) sock.send(puback_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/05-session-expiry-v5.py000077500000000000000000000100111450213760600222360ustar00rootroot00000000000000#!/usr/bin/env python3 # MQTT v5. Test whether session expiry interval works correctly. from mosq_test_helper import * rc = 1 keepalive = 60 # This client exists to test possible fixed size int overflow and sorting of the session intervals # https://github.com/eclipse/mosquitto/issues/1525 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 4294967294) connect0_packet = mosq_test.gen_connect("overflow", keepalive=keepalive, clean_session=False, proto_ver=5, properties=props) connack0_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 1) connect_packet = mosq_test.gen_connect("clean-qos2-test", keepalive=keepalive, clean_session=False, proto_ver=5, properties=props) connack1_packet = mosq_test.gen_connack(flags=0, rc=0, proto_ver=5) connack2_packet = mosq_test.gen_connack(flags=1, rc=0, proto_ver=5) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 3) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 0) disconnect2_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # Connect client with wildly different session expiry, this should impact # on the test if all is well sock0 = mosq_test.do_client_connect(connect0_packet, connack0_packet, port=port, connack_error="connack 0") # Immediately disconnect, this should now be queued to expire sock0.close() # First connect, clean start is false, we expect a normal connack sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error="connack 1") # Forceful disconnect sock.close() # Immediate second connect, clean start is false, we expect a connack with # previous state sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error="connack 2") sock.close() # Session should expire in one second, so sleep longer time.sleep(2) # Third connect, clean start is false, session should have expired so we # expect a normal connack sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error="connack 3") # Send DISCONNECT with new session expiry, then close sock.send(disconnect_packet) sock.close() # Immediate reconnect, clean start is false, we expect a connack with # previous state sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error="connack 4") # Send DISCONNECT with new session expiry, then close sock.send(disconnect_packet) sock.close() # Session should expire in three seconds if it has been updated, sleep for # 2 to check it is updated from 1 second. time.sleep(2) # Immediate reconnect, clean start is false, we expect a connack with # previous state sock = mosq_test.do_client_connect(connect_packet, connack2_packet, port=port, connack_error="connack 5") # Send DISCONNECT with new session expiry, then close sock.send(disconnect_packet) sock.close() # Session should expire in three seconds, so sleep longer time.sleep(4) # Third connect, clean start is false, session should have expired so we # expect a normal connack sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error="connack 6") # Send DISCONNECT with 0 session expiry, then close sock.send(disconnect2_packet) sock.close() # Immediate reconnect, session should have been removed. sock = mosq_test.do_client_connect(connect_packet, connack1_packet, port=port, connack_error="connack 7") sock.close() rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/06-bridge-b2br-disconnect-qos1.py000077500000000000000000000076541450213760600240220ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a bridge resend a QoS=1 message correctly after a disconnect? from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# both 1\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "bridge/#", 1 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid, "bridge/#", 1 | opts, proto_ver=proto_ver) suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 3 publish_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=1, mid=mid, payload="disconnect-message", proto_ver=proto_ver) publish_dup_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=1, mid=mid, payload="disconnect-message", dup=True, proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 20 publish2_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=1, mid=mid, payload="disconnect-message", proto_ver=proto_ver) puback2_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) ssock.bind(('', port1)) ssock.listen(5) try: broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "subscribe", subscribe_packet) bridge.send(suback_packet) bridge.send(publish_packet) # Bridge doesn't have time to respond but should expect us to retry # and so remove PUBACK. bridge.close() (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "2nd subscribe", subscribe2_packet) bridge.send(suback2_packet) # Send a different publish message to make sure the response isn't to the old one. bridge.send(publish2_packet) mosq_test.expect_packet(bridge, "puback", puback2_packet) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-b2br-disconnect-qos2.py000077500000000000000000000105731450213760600240150ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a bridge resend a QoS=1 message correctly after a disconnect? from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# both 2\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "bridge/#", 2 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid, "bridge/#", 2 | opts, proto_ver=proto_ver) suback2_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 3 subscribe3_packet = mosq_test.gen_subscribe(mid, "bridge/#", 2 | opts, proto_ver=proto_ver) suback3_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 5 publish_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=2, mid=mid, payload="disconnect-message", proto_ver=proto_ver) publish_dup_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=2, mid=mid, payload="disconnect-message", dup=True, proto_ver=proto_ver) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) ssock.bind(('', port1)) ssock.listen(5) try: broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "subscribe", subscribe_packet) bridge.send(suback_packet) bridge.send(publish_packet) bridge.close() (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "2nd subscribe", subscribe2_packet) bridge.send(suback2_packet) bridge.send(publish_dup_packet) mosq_test.expect_packet(bridge, "pubrec", pubrec_packet) bridge.send(pubrel_packet) bridge.close() (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "3rd subscribe", subscribe3_packet) bridge.send(suback3_packet) bridge.send(publish_dup_packet) mosq_test.expect_packet(bridge, "2nd pubrec", pubrec_packet) bridge.send(pubrel_packet) mosq_test.expect_packet(bridge, "pubcomp", pubcomp_packet) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-b2br-late-connection-retain.py000077500000000000000000000077671450213760600253570ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a bridge queue up retained messages correctly if the remote broker starts up late? from mosq_test_helper import * def write_config1(filename, persistence_file, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("persistence true\n") f.write("persistence_file %s\n" % (persistence_file)) def write_config2(filename, persistence_file, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# out 1\n") f.write("notifications false\n") f.write("bridge_attempt_unsubscribe false\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) f.write("persistence true\n") f.write("persistence_file %s\n" % (persistence_file)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') persistence_file = os.path.basename(__file__).replace('.py', '.db') rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) c_connect_packet = mosq_test.gen_connect("client", keepalive=keepalive, proto_ver=proto_ver) c_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("bridge/test", qos=1, mid=mid, payload="message", retain=True, proto_ver=proto_ver) if proto_ver == 5: puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver, reason_code=16) else: puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) ssock.bind(('', port1)) ssock.listen(5) write_config1(conf_file, persistence_file, port1, port2) try: broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) client = mosq_test.do_client_connect(c_connect_packet, c_connack_packet, timeout=20, port=port2) mosq_test.do_send_receive(client, publish_packet, puback_packet, "puback") client.close() broker.terminate() broker.wait() # Restart, with retained message in place write_config2(conf_file, persistence_file, port1, port2, bridge_protocol) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "publish", publish_packet) bridge.send(puback_packet) # Guard against multiple retained messages of the same type by # sending a pingreq to give us something to expect back. If we get # a publish, it's a fail. mosq_test.do_ping(bridge) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() os.remove(persistence_file) ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-b2br-late-connection.py000077500000000000000000000056261450213760600240670ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a bridge queue up messages correctly if the remote broker starts up late? from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# out 1\n") f.write("notifications false\n") f.write("bridge_attempt_unsubscribe false\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) c_connect_packet = mosq_test.gen_connect("client", keepalive=keepalive, proto_ver=proto_ver) c_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 publish_packet = mosq_test.gen_publish("bridge/test", qos=1, mid=mid, payload="message", proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) ssock.bind(('', port1)) ssock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: (bridge, address) = ssock.accept() bridge.settimeout(20) client = mosq_test.do_client_connect(c_connect_packet, c_connack_packet, timeout=20, port=port2) mosq_test.do_send_receive(client, publish_packet, puback_packet, "puback") client.close() # We've now sent a message to the broker that should be delivered to us via the bridge mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "publish", publish_packet) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-b2br-remapping.py000077500000000000000000000123461450213760600227640ustar00rootroot00000000000000#!/usr/bin/env python3 # Test remapping of topic name for incoming message from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("bridge_attempt_unsubscribe false\n") f.write("topic # in 0 local/topic/ remote/topic/\n") f.write("topic prefix/# in 0 local2/topic/ remote2/topic/\n") f.write("topic +/value in 0 local3/topic/ remote3/topic/\n") f.write("topic ic/+ in 0 local4/top remote4/tip\n") f.write("topic clients/total in 0 test/mosquitto/org $SYS/broker/\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) connect_packet = None connack_packet = None def inner_test(bridge, sock, proto_ver): global connect_packet, connack_packet if not mosq_test.expect_packet(bridge, "connect", connect_packet): return 1 bridge.send(connack_packet) if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 mid = 0 patterns = [ "remote/topic/#", "remote2/topic/prefix/#", "remote3/topic/+/value", "remote4/tipic/+", "$SYS/broker/clients/total", ] for pattern in ("remote/topic/#", "remote2/topic/prefix/#", "remote3/topic/+/value"): mid += 1 subscribe_packet = mosq_test.gen_subscribe(mid, pattern, 0 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) if not mosq_test.expect_packet(bridge, "subscribe", subscribe_packet): return 1 bridge.send(suback_packet) mid += 1 subscribe_packet = mosq_test.gen_subscribe(mid, "#", 0 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) sock.send(subscribe_packet) if not mosq_test.expect_packet(sock, "suback", suback_packet): return 1 cases = [ ('local/topic/something', 'remote/topic/something'), ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'), ('local/topic/value', 'remote/topic/value'), # Don't work, #40 must be fixed before # ('local/topic', 'remote/topic'), ('local2/topic/prefix/something', 'remote2/topic/prefix/something'), ('local3/topic/something/value', 'remote3/topic/something/value'), ('local4/topic/something', 'remote4/tipic/something'), ('test/mosquitto/orgclients/total', '$SYS/broker/clients/total'), ] for (local_topic, remote_topic) in cases: mid += 1 remote_publish_packet = mosq_test.gen_publish( remote_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver) local_publish_packet = mosq_test.gen_publish( local_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver) bridge.send(remote_publish_packet) match = mosq_test.expect_packet(sock, "publish", local_publish_packet) if not match: print("Fail on cases local_topic=%r, remote_topic=%r" % ( local_topic, remote_topic, )) return 1 return 0 def do_test(proto_ver): global connect_packet, connack_packet if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) client_connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, proto_ver=proto_ver) client_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(4) ssock.bind(('', port1)) ssock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: (bridge, address) = ssock.accept() bridge.settimeout(2) sock = mosq_test.do_client_connect( client_connect_packet, client_connack_packet, port=port2, ) rc = inner_test(bridge, sock, proto_ver) sock.close() bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-br2b-disconnect-qos1.py000077500000000000000000000104311450213760600240050ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a bridge resend a QoS=1 message correctly after a disconnect? from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# both 1\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "bridge/#", 1 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 3 subscribe2_packet = mosq_test.gen_subscribe(mid, "bridge/#", 1 | opts, proto_ver=proto_ver) suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 2 publish_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=1, mid=mid, payload="disconnect-message", proto_ver=proto_ver) publish_dup_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=1, mid=mid, payload="disconnect-message", dup=True, proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) ssock.bind(('', port1)) ssock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "subscribe", subscribe_packet) bridge.send(suback_packet) # Helper helper_connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive, proto_ver=proto_ver) helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 128 helper_publish_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=1, mid=mid, payload="disconnect-message", proto_ver=proto_ver) helper_puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) helper_sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2, connack_error="helper connack") mosq_test.do_send_receive(helper_sock, publish_packet, puback_packet, "helper puback") helper_sock.close() # End helper mosq_test.expect_packet(bridge, "publish", publish_packet) bridge.close() (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "2nd connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "2nd subscribe", subscribe2_packet) bridge.send(suback2_packet) mosq_test.expect_packet(bridge, "2nd publish", publish_dup_packet) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-br2b-disconnect-qos2.py000077500000000000000000000126041450213760600240120ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a bridge resend a QoS=1 message correctly after a disconnect? from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# both 2\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "bridge/#", 2 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 3 subscribe2_packet = mosq_test.gen_subscribe(mid, "bridge/#", 2 | opts, proto_ver=proto_ver) suback2_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 4 subscribe3_packet = mosq_test.gen_subscribe(mid, "bridge/#", 2 | opts, proto_ver=proto_ver) suback3_packet = mosq_test.gen_suback(mid, 2, proto_ver=proto_ver) mid = 2 publish_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=2, mid=mid, payload="disconnect-message", proto_ver=proto_ver) publish_dup_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=2, mid=mid, payload="disconnect-message", dup=True, proto_ver=proto_ver) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) ssock.bind(('', port1)) ssock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "subscribe", subscribe_packet) bridge.send(suback_packet) # Helper helper_connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive, proto_ver=proto_ver) helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 312 helper_publish_packet = mosq_test.gen_publish("bridge/disconnect/test", qos=2, mid=mid, payload="disconnect-message", proto_ver=proto_ver) helper_pubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=proto_ver) helper_pubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=proto_ver) helper_pubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=proto_ver) helper_sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2, connack_error="helper connack") mosq_test.do_send_receive(helper_sock, helper_publish_packet, helper_pubrec_packet, "helper pubrec") mosq_test.do_send_receive(helper_sock, helper_pubrel_packet, helper_pubcomp_packet, "helper pubcomp") helper_sock.close() # End helper mosq_test.expect_packet(bridge, "publish", publish_packet) bridge.close() (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "2nd subscribe", subscribe2_packet) bridge.send(suback2_packet) mosq_test.expect_packet(bridge, "2nd publish", publish_dup_packet) bridge.send(pubrec_packet) mosq_test.expect_packet(bridge, "pubrel", pubrel_packet) bridge.close() (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "3rd subscribe", subscribe3_packet) bridge.send(suback3_packet) mosq_test.expect_packet(bridge, "2nd pubrel", pubrel_packet) bridge.send(pubcomp_packet) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-br2b-remapping.py000077500000000000000000000107401450213760600227600ustar00rootroot00000000000000#!/usr/bin/env python3 # Test remapping of topic name for outgoing message from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("bridge_attempt_unsubscribe false\n") f.write("topic # out 0 local/topic/ remote/topic/\n") f.write("topic prefix/# out 0 local2/topic/ remote2/topic/\n") f.write("topic +/value out 0 local3/topic/ remote3/topic/\n") f.write("topic ic/+ out 0 local4/top remote4/tip\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def inner_test(bridge, sock, proto_ver): global connect_packet, connack_packet if not mosq_test.expect_packet(bridge, "connect", connect_packet): return 1 bridge.send(connack_packet) cases = [ ('local/topic/something', 'remote/topic/something'), ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'), ('local/topic/value', 'remote/topic/value'), # Don't work, #40 must be fixed before # ('local/topic', 'remote/topic'), ('local2/topic/something', None), # don't match topic pattern ('local2/topic/prefix/something', 'remote2/topic/prefix/something'), ('local3/topic/something/value', 'remote3/topic/something/value'), ('local4/topic/something', 'remote4/tipic/something'), ('local5/topic/something', None), ] mid = 3 for (local_topic, remote_topic) in cases: mid += 1 local_publish_packet = mosq_test.gen_publish( local_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver ) sock.send(local_publish_packet) if remote_topic: remote_publish_packet = mosq_test.gen_publish( remote_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver ) match = mosq_test.expect_packet(bridge, "publish", remote_publish_packet) if not match: print("Fail on cases local_topic=%r, remote_topic=%r" % ( local_topic, remote_topic, )) return 1 else: time.sleep(1) mosq_test.do_ping(bridge, "FAIL: Received data when nothing is expected\nFail on cases local_topic=%r, remote_topic=%r" % ( local_topic, remote_topic, )) return 0 def do_test(proto_ver): global connect_packet, connack_packet if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) client_connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, proto_ver=proto_ver) client_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(4) ssock.bind(('', port1)) ssock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: (bridge, address) = ssock.accept() bridge.settimeout(2) sock = mosq_test.do_client_connect( client_connect_packet, client_connack_packet, port=port2, ) rc = inner_test(bridge, sock, proto_ver) sock.close() bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-clean-session-core.py000077500000000000000000000265071450213760600236520ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a broker handles cleansession and local_cleansession correctly on bridges # # Test cases (with settings for broker A (edge). The settings on broker B (core) # are irrelevant, though you'll need persistence enabled to test, unless you # can simulate network interruptions. # Similarly, you'll need persistence on A, _purely_ to simplify the testing with a client # t# | LCS | CS | queued from (expected) # | A->B | B->A # 1 | -(t| t | no | no # 2 | -(f| f | yes | yes # 3 | t | t | no | no (as per #1) # 4 | t | f | no | yes # 5 | f | t | yes | no # 6 | f | f | yes | yes (as per #2) # # Test setup is two (real) brokers, so that messages can be published and subscribed in both # directions, with two test clients, one at each end. # Disable on Travis for now, too unreliable import os if os.environ.get('TRAVIS') is not None: exit(0) from mosq_test_helper import * from collections import namedtuple # Normally we don't want tests to spew debug, but if you're working on a test, it's useful VERBOSE_TEST=False def tprint(*args, **kwargs): if VERBOSE_TEST: print(" ".join(map(str,args)), **kwargs) # this is our "A" broker def write_config_edge(filename, persistence_file, remote_port, listen_port, protocol_version, cs=False, lcs=None): with open(filename, 'w') as f: f.write("port %d\n" % (listen_port)) f.write("allow_anonymous true\n") f.write("\n") f.write("persistence true\n") f.write("persistence_file %s\n" % (persistence_file)) f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (remote_port)) f.write("topic br_out/# out 1\n") f.write("topic br_in/# in 1\n") f.write("notifications false\n") # We need to ensure connections break fast enough to keep test times sane f.write("keepalive_interval 5\n") f.write("restart_timeout 5\n") f.write("cleansession %s\n" % ("true" if cs else "false")) # Ensure defaults are tested if lcs is not None: f.write("local_cleansession %s\n" % ("true" if lcs else "false")) f.write("bridge_protocol_version %s\n" % (protocol_version)) # this is our "B" broker def write_config_core(filename, listen_port, persistence_file): with open(filename, 'w') as f: f.write("port %d\n" % (listen_port)) f.write("allow_anonymous true\n") f.write("\n") f.write("persistence true\n") f.write("persistence_file %s\n" % (persistence_file)) def do_test(proto_ver, cs, lcs=None): tprint("Running test with cs:%s, lcs: %s and proto: %d" % (cs, lcs, proto_ver)) if proto_ver == 4: bridge_protocol = "mqttv311" else: bridge_protocol = "mqttv50" # Match default behaviour of broker expect_queued_ab = True expect_queued_ba = True if lcs is None: lcs = cs if lcs: expect_queued_ab = False if cs: expect_queued_ba = False (port_a_listen, port_b_listen) = mosq_test.get_port(2) conf_file_a = os.path.basename(__file__).replace('.py', '%d_a_edge.conf'%(port_a_listen)) persistence_file_a = os.path.basename(__file__).replace('.py', '%d_a_edge.db'%(port_a_listen)) write_config_edge(conf_file_a, persistence_file_a, port_b_listen, port_a_listen, bridge_protocol, cs=cs, lcs=lcs) conf_file_b = os.path.basename(__file__).replace('.py', '%d_b_core.conf'%(port_b_listen)) persistence_file_b = os.path.basename(__file__).replace('.py', '%d_b_core.db'%(port_b_listen)) write_config_core(conf_file_b, port_b_listen, persistence_file_b) AckedPair = namedtuple("AckedPair", "p ack") def make_conn(client_tag, proto, cs, session_present=False): client_id = socket.gethostname() + "." + client_tag keepalive = 60 conn = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=cs, proto_ver=proto, session_expiry=0 if cs else 5000) connack = mosq_test.gen_connack(rc=0, proto_ver=proto_ver, flags=1 if session_present else 0) return AckedPair(conn, connack) def make_sub(topic, mid, qos, proto): if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 sub = mosq_test.gen_subscribe(mid, topic, qos | opts, proto_ver=proto) suback = mosq_test.gen_suback(mid, qos, proto_ver=proto) return AckedPair(sub, suback) def make_pub(topic, mid, proto, qos=1, payload_tag="message", rc=-1): # Using the mid automatically makes it hard to verify messages that might have been retransmitted. # encourage users to put sequence numbers in topics instead.... pub = mosq_test.gen_publish(topic, mid=mid, qos=qos, retain=False, payload=payload_tag + "-from-" + topic, proto_ver=proto) puback = mosq_test.gen_puback(mid, proto_ver=proto, reason_code=rc) return AckedPair(pub, puback) # Clients are testing messages in both directions, they need to be durable conn_a = make_conn("client_a_edge", proto_ver, False) conn_b = make_conn("client_b_core", proto_ver, False) # We expect session present when we reconnect reconn_a = make_conn("client_a_edge", proto_ver, False, session_present=True) reconn_b = make_conn("client_b_core", proto_ver, False, session_present=True) # remember, mids are from each broker's point of view, not the "world" sub_a = make_sub("br_in/#", qos=1, mid=1, proto=proto_ver) sub_b = make_sub("br_out/#", qos=1, mid=1, proto=proto_ver) pub_a1 = make_pub("br_out/test-queued1", mid=1, proto=proto_ver) pub_a2 = make_pub("br_out/test-queued2", mid=2, proto=proto_ver) pub_a3 = make_pub("br_out/test-queued3", mid=3, proto=proto_ver) pub_a3r = make_pub("br_out/test-queued3", mid=2, proto=proto_ver) # without queueing, there is no a2 pub_b1 = make_pub("br_in/test-queued1", mid=1, proto=proto_ver) pub_b2 = make_pub("br_in/test-queued2", mid=2, proto=proto_ver) pub_b3 = make_pub("br_in/test-queued3", mid=3, proto=proto_ver) pub_b3r = make_pub("br_in/test-queued3", mid=2, proto=proto_ver) # without queueing, there is no b2 success = False stde_a1 = stde_b1 = None try: # b must start first, as it's the destination of a broker_b = mosq_test.start_broker(filename=conf_file_b, port=port_b_listen, use_conf=True) broker_a = mosq_test.start_broker(filename=conf_file_a, port=port_a_listen, use_conf=True) client_a = mosq_test.do_client_connect(conn_a.p, conn_a.ack, port=port_a_listen) mosq_test.do_send_receive(client_a, sub_a.p, sub_a.ack, "suback_a") client_b = mosq_test.do_client_connect(conn_b.p, conn_b.ack, port=port_b_listen) mosq_test.do_send_receive(client_b, sub_b.p, sub_b.ack, "suback_b") mosq_test.do_send_receive(client_a, pub_a1.p, pub_a1.ack, "puback_a1") mosq_test.do_receive_send(client_b, pub_a1.p, pub_a1.ack, "a->b1 (b-side)") mosq_test.do_send_receive(client_b, pub_b1.p, pub_b1.ack, "puback_b1") mosq_test.do_receive_send(client_a, pub_b1.p, pub_b1.ack, "b->a1 (a-side)") tprint("Normal bi-dir bridging works. continuing") broker_b.terminate() broker_b.wait() (stdo_b1, stde_b1) = broker_b.communicate() # as we're _terminating_ the connections should close ~straight away tprint("terminated B", time.time()) time.sleep(0.5) # should be queued (or not) mosq_test.do_send_receive(client_a, pub_a2.p, pub_a2.ack, "puback_a2") broker_b = mosq_test.start_broker(filename=conf_file_b, port=port_b_listen, use_conf=True) # client b needs to reconnect now! client_b = mosq_test.do_client_connect(reconn_b.p, reconn_b.ack, port=port_b_listen) tprint("client b reconnected after restarting broker b at ", time.time()) # Need to sleep long enough to be sure of a re-connection... time.sleep(10) # yuck, this makes the test run for ages! # should go through tprint("(B should be alive again now!) sending (after reconn!) a3 at ", time.time()) mosq_test.do_send_receive(client_a, pub_a3.p, pub_a3.ack, "puback_a3") if expect_queued_ab: tprint("1.expecting a->b queueing") mosq_test.do_receive_send(client_b, pub_a2.p, pub_a2.ack, "a->b_2") mosq_test.do_receive_send(client_b, pub_a3.p, pub_a3.ack, "a->b_3") else: tprint("not expecting a->b queueing") mosq_test.do_receive_send(client_b, pub_a3r.p, pub_a3r.ack, "a->b_3(r)") tprint("Stage 1 complete, repeating in other direction") # ok, now repeat in the other direction... broker_a.terminate() broker_a.wait() (stdo_a1, stde_a1) = broker_a.communicate() time.sleep(0.5) mosq_test.do_send_receive(client_b, pub_b2.p, pub_b2.ack, "puback_b2") broker_a = mosq_test.start_broker(filename=conf_file_a, port=port_a_listen, use_conf=True) # client a needs to reconnect now! client_a = mosq_test.do_client_connect(reconn_a.p, reconn_a.ack, port=port_a_listen) tprint("client A reconnected after restarting broker A at ", time.time()) # Need to sleep long enough to be sure of a re-connection... time.sleep(10) # yuck, this makes the test run for ages! # should go through mosq_test.do_send_receive(client_b, pub_b3.p, pub_b3.ack, "puback_b3") if expect_queued_ba: tprint("2.expecting b->a queueueing") mosq_test.do_receive_send(client_a, pub_b2.p, pub_b2.ack, "b->a_2") mosq_test.do_receive_send(client_a, pub_b3.p, pub_b3.ack, "b->a_3") else: tprint("not expecting message b->a_2") mosq_test.do_receive_send(client_a, pub_b3r.p, pub_b3r.ack, "b->a_3(r)") success = True except mosq_test.TestError: pass finally: os.remove(conf_file_a) os.remove(conf_file_b) broker_a.terminate() broker_b.terminate() broker_a.wait() broker_b.wait() (stdo_a, stde_a) = broker_a.communicate() (stdo_b, stde_b) = broker_b.communicate() # Must be after terminating! try: os.remove(persistence_file_a) except FileNotFoundError: print("persistence file a didn't exist, skipping remove") try: os.remove(persistence_file_b) except FileNotFoundError: print("persistence file b didn't exist, skipping remove") if not success: print("Test failed, dumping broker A logs: ") if stde_a1: print(stde_a1.decode('utf-8')) print(stde_a.decode('utf-8')) print("Test failed, dumping broker B logs: ") if stde_b1: print(stde_b1.decode('utf-8')) print(stde_b.decode('utf-8')) exit(1) if sys.argv[3] == "True": cs = True elif sys.argv[3] == "False": cs = False else: raise ValueError("cs") if sys.argv[4] == "True": lcs = True elif sys.argv[4] == "False": lcs = False elif sys.argv[4] == "None": lcs = None else: raise ValueError("lcs") do_test(proto_ver=4, cs=cs, lcs=lcs) # FIXME - v5 clean session bridging doesn't work: see # https://github.com/eclipse/mosquitto/issues/1632 #do_test(proto_ver=5, cs=cs, lcs=lcs) exit(0) mosquitto-2.0.18/test/broker/06-bridge-clean-session-csF-lcsF.py000077500000000000000000000005341450213760600243120ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a broker handles cleansession and local_cleansession correctly on bridges from mosq_test_helper import * from collections import namedtuple (port_a_listen, port_b_listen) = mosq_test.get_port(2) subprocess.run(['./06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), "False", "False"]) mosquitto-2.0.18/test/broker/06-bridge-clean-session-csF-lcsN.py000077500000000000000000000005331450213760600243210ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a broker handles cleansession and local_cleansession correctly on bridges from mosq_test_helper import * from collections import namedtuple (port_a_listen, port_b_listen) = mosq_test.get_port(2) subprocess.run(['./06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), "False", "None"]) mosquitto-2.0.18/test/broker/06-bridge-clean-session-csF-lcsT.py000077500000000000000000000005331450213760600243270ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a broker handles cleansession and local_cleansession correctly on bridges from mosq_test_helper import * from collections import namedtuple (port_a_listen, port_b_listen) = mosq_test.get_port(2) subprocess.run(['./06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), "False", "True"]) mosquitto-2.0.18/test/broker/06-bridge-clean-session-csT-lcsF.py000077500000000000000000000005331450213760600243270ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a broker handles cleansession and local_cleansession correctly on bridges from mosq_test_helper import * from collections import namedtuple (port_a_listen, port_b_listen) = mosq_test.get_port(2) subprocess.run(['./06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), "True", "False"]) mosquitto-2.0.18/test/broker/06-bridge-clean-session-csT-lcsN.py000077500000000000000000000005321450213760600243360ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a broker handles cleansession and local_cleansession correctly on bridges from mosq_test_helper import * from collections import namedtuple (port_a_listen, port_b_listen) = mosq_test.get_port(2) subprocess.run(['./06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), "True", "None"]) mosquitto-2.0.18/test/broker/06-bridge-clean-session-csT-lcsT.py000077500000000000000000000005321450213760600243440ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a broker handles cleansession and local_cleansession correctly on bridges from mosq_test_helper import * from collections import namedtuple (port_a_listen, port_b_listen) = mosq_test.get_port(2) subprocess.run(['./06-bridge-clean-session-core.py', str(port_a_listen), str(port_b_listen), "True", "True"]) mosquitto-2.0.18/test/broker/06-bridge-fail-persist-resend-qos1.py000077500000000000000000000060171450213760600247140ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a bridge can cope with an unknown PUBACK from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("connection bridge-u-test\n") f.write("remote_clientid bridge-u-test\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# out\n") f.write("\n") f.write("cleansession true\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("try_private false\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("bridge-u-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 180 mid_unknown = 2000 publish_packet = mosq_test.gen_publish("bridge/unknown/qos1", qos=1, payload="bridge-message", mid=mid, proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) puback_packet_unknown = mosq_test.gen_puback(mid_unknown, proto_ver=proto_ver) unsubscribe_packet = mosq_test.gen_unsubscribe(1, "bridge/#", proto_ver=proto_ver) unsuback_packet = mosq_test.gen_unsuback(1, proto_ver=proto_ver) if os.environ.get('MOSQ_USE_VALGRIND') is not None: sleep_time = 5 else: sleep_time = 0.5 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port1)) sock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) time.sleep(sleep_time) try: (conn, address) = sock.accept() conn.settimeout(20) mosq_test.expect_packet(conn, "connect", connect_packet) conn.send(connack_packet) mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet) conn.send(unsuback_packet) # Send the unexpected puback packet conn.send(puback_packet_unknown) # Send a legitimate publish packet to verify everything is still ok conn.send(publish_packet) mosq_test.expect_packet(conn, "puback", puback_packet) rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() sock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-fail-persist-resend-qos2.py000077500000000000000000000071371450213760600247210ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a bridge can cope with an unknown PUBACK from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("connection bridge-u-test\n") f.write("remote_clientid bridge-u-test\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# out\n") f.write("\n") f.write("cleansession true\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("try_private false\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("bridge-u-test", keepalive=keepalive, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 180 mid_unknown = 2000 publish_packet = mosq_test.gen_publish("bridge/unknown/qos2", qos=1, payload="bridge-message", mid=mid, proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) pubrec_packet_unknown1 = mosq_test.gen_pubrec(mid_unknown+1, proto_ver=proto_ver) pubrel_packet_unknown1 = mosq_test.gen_pubrel(mid_unknown+1, proto_ver=proto_ver) pubrel_packet_unknown2 = mosq_test.gen_pubrel(mid_unknown+2, proto_ver=proto_ver) pubcomp_packet_unknown2 = mosq_test.gen_pubcomp(mid_unknown+2, proto_ver=proto_ver) pubcomp_packet_unknown3 = mosq_test.gen_pubcomp(mid_unknown+3, proto_ver=proto_ver) unsubscribe_packet = mosq_test.gen_unsubscribe(1, "bridge/#", proto_ver=proto_ver) unsuback_packet = mosq_test.gen_unsuback(1, proto_ver=proto_ver) if os.environ.get('MOSQ_USE_VALGRIND') is not None: sleep_time = 5 else: sleep_time = 0.5 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port1)) sock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) time.sleep(sleep_time) try: (conn, address) = sock.accept() conn.settimeout(20) mosq_test.expect_packet(conn, "connect", connect_packet) conn.send(connack_packet) mosq_test.expect_packet(conn, "unsubscribe", unsubscribe_packet) conn.send(unsuback_packet) # Send the unexpected pubrec packet conn.send(pubrec_packet_unknown1) mosq_test.expect_packet(conn, "pubrel", pubrel_packet_unknown1) conn.send(pubrel_packet_unknown2) mosq_test.expect_packet(conn, "pubcomp", pubcomp_packet_unknown2) conn.send(pubcomp_packet_unknown3) # Send a legitimate publish packet to verify everything is still ok conn.send(publish_packet) mosq_test.expect_packet(conn, "puback", puback_packet) rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() sock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-no-local.py000077500000000000000000000027661450213760600216660ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether an incoming bridge connection receives its own messages. It # shouldn't because for v3.1 and v3.1.1 we have no-local set for all bridges. from mosq_test_helper import * def do_test(proto_ver_connect, proto_ver_msgs, sub_opts): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("bridge-test", keepalive=keepalive, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver_msgs) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "loop/test", 0 | sub_opts, proto_ver=proto_ver_msgs) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver_msgs) publish_packet = mosq_test.gen_publish("loop/test", qos=0, payload="message", proto_ver=proto_ver_msgs) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.send(publish_packet) mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(128+3, 3, 0) do_test(128+4, 4, 0) do_test(5, 5, mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL) exit(0) mosquitto-2.0.18/test/broker/06-bridge-outgoing-retain.py000077500000000000000000000076251450213760600232740ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a bridge with bridge_outgoing_retain set to false not set the retain bit # on outgoing messages? from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version, outgoing_retain): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# both 1\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" %(protocol_version)) f.write("bridge_outgoing_retain %s\n" %(outgoing_retain)) def do_test(proto_ver, outgoing_retain): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, bridge_protocol, outgoing_retain) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 subscribe_packet = mosq_test.gen_subscribe(mid, "bridge/#", 1 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) if outgoing_retain == "true": publish_packet = mosq_test.gen_publish("bridge/retain/test", qos=0, retain=True, payload="message", proto_ver=proto_ver) else: publish_packet = mosq_test.gen_publish("bridge/retain/test", qos=0, retain=False, payload="message", proto_ver=proto_ver) helper_connect_packet = mosq_test.gen_connect("helper", keepalive=keepalive, clean_session=True, proto_ver=proto_ver) helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) helper_publish_packet = mosq_test.gen_publish("bridge/retain/test", qos=0, retain=True, payload="message", proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(40) ssock.bind(('', port1)) ssock.listen(5) try: broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "subscribe", subscribe_packet) bridge.send(suback_packet) # Broker is now connected to us on port1. # Connect our client to the broker on port2 and send a publish # message, which we will then receive by way of the bridge helper = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2) helper.send(helper_publish_packet) helper.close() mosq_test.expect_packet(bridge, "publish", publish_packet) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4, outgoing_retain="true") do_test(proto_ver=4, outgoing_retain="false") do_test(proto_ver=5, outgoing_retain="true") do_test(proto_ver=5, outgoing_retain="false") exit(0) mosquitto-2.0.18/test/broker/06-bridge-per-listener-settings.py000077500000000000000000000124101450213760600244140ustar00rootroot00000000000000#!/usr/bin/env python3 # Test remapping of topic name for incoming message from mosq_test_helper import * def write_config(filename, port1, port2, port3, protocol_version): with open(filename, 'w') as f: f.write("per_listener_settings true\n") f.write("port %d\n" % (port2)) f.write("listener %d 127.0.0.1\n" % (port3)) f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("bridge_attempt_unsubscribe false\n") f.write("topic # in 0 local/topic/ remote/topic/\n") f.write("topic prefix/# in 0 local2/topic/ remote2/topic/\n") f.write("topic +/value in 0 local3/topic/ remote3/topic/\n") f.write("topic ic/+ in 0 local4/top remote4/tip\n") f.write("topic clients/total in 0 test/mosquitto/org $SYS/broker/\n") f.write("notifications false\n") f.write("restart_timeout 5\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) def inner_test(bridge, sock, proto_ver): global connect_packet, connack_packet if not mosq_test.expect_packet(bridge, "connect", connect_packet): return 1 bridge.send(connack_packet) if proto_ver == 5: opts = mqtt5_opts.MQTT_SUB_OPT_NO_LOCAL | mqtt5_opts.MQTT_SUB_OPT_RETAIN_AS_PUBLISHED else: opts = 0 mid = 0 patterns = [ "remote/topic/#", "remote2/topic/prefix/#", "remote3/topic/+/value", "remote4/tipic/+", "$SYS/broker/clients/total", ] for pattern in ("remote/topic/#", "remote2/topic/prefix/#", "remote3/topic/+/value"): mid += 1 subscribe_packet = mosq_test.gen_subscribe(mid, pattern, 0 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) if not mosq_test.expect_packet(bridge, "subscribe", subscribe_packet): return 1 bridge.send(suback_packet) mid += 1 subscribe_packet = mosq_test.gen_subscribe(mid, "#", 0 | opts, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) sock.send(subscribe_packet) if not mosq_test.expect_packet(sock, "suback", suback_packet): return 1 cases = [ ('local/topic/something', 'remote/topic/something'), ('local/topic/some/t/h/i/n/g', 'remote/topic/some/t/h/i/n/g'), ('local/topic/value', 'remote/topic/value'), # Don't work, #40 must be fixed before # ('local/topic', 'remote/topic'), ('local2/topic/prefix/something', 'remote2/topic/prefix/something'), ('local3/topic/something/value', 'remote3/topic/something/value'), ('local4/topic/something', 'remote4/tipic/something'), ('test/mosquitto/orgclients/total', '$SYS/broker/clients/total'), ] for (local_topic, remote_topic) in cases: mid += 1 remote_publish_packet = mosq_test.gen_publish( remote_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver) local_publish_packet = mosq_test.gen_publish( local_topic, qos=0, mid=mid, payload='', proto_ver=proto_ver) bridge.send(remote_publish_packet) match = mosq_test.expect_packet(sock, "publish", local_publish_packet) if not match: print("Fail on cases local_topic=%r, remote_topic=%r" % ( local_topic, remote_topic, )) return 1 return 0 def do_test(proto_ver): global connect_packet, connack_packet if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2, port3) = mosq_test.get_port(3) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2, port3, bridge_protocol) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_sample" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) client_connect_packet = mosq_test.gen_connect("pub-test", keepalive=keepalive, proto_ver=proto_ver) client_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) ssock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) ssock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) ssock.settimeout(4) ssock.bind(('', port1)) ssock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: (bridge, address) = ssock.accept() bridge.settimeout(2) sock = mosq_test.do_client_connect( client_connect_packet, client_connack_packet, port=port2, ) rc = inner_test(bridge, sock, proto_ver) sock.close() bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() ssock.close() if rc: print(stde.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/06-bridge-reconnect-local-out.py000077500000000000000000000107021450213760600240240ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a bridge topics work correctly after reconnection. # Important point here is that persistence is enabled. from mosq_test_helper import * def write_config(filename, port1, port2, protocol_version): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("persistence true\n") f.write("persistence_file mosquitto-%d.db" % (port1)) f.write("\n") f.write("connection bridge_sample\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# out\n") f.write("bridge_protocol_version %s\n" % (protocol_version)) f.write("cleansession false\n") def do_test(proto_ver): if proto_ver == 4: bridge_protocol = "mqttv311" proto_ver_connect = 128+4 else: bridge_protocol = "mqttv50" proto_ver_connect = 5 (port1, port2) = mosq_test.get_port(2) conf_file = '06-bridge-reconnect-local-out.conf' write_config(conf_file, port1, port2, bridge_protocol) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("bridge-reconnect-test", keepalive=keepalive, proto_ver=proto_ver_connect) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 180 subscribe_packet = mosq_test.gen_subscribe(mid, "bridge/#", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("bridge/reconnect", qos=0, payload="bridge-reconnect-message", proto_ver=proto_ver) try: os.remove('mosquitto-%d.db' % (port1)) except OSError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port1, use_conf=False) local_cmd = ['../../src/mosquitto', '-c', '06-bridge-reconnect-local-out.conf'] local_broker = mosq_test.start_broker(cmd=local_cmd, filename=os.path.basename(__file__)+'_local1', use_conf=False, port=port2) if os.environ.get('MOSQ_USE_VALGRIND') is not None: time.sleep(5) else: time.sleep(0.5) local_broker.terminate() local_broker.wait() if os.environ.get('MOSQ_USE_VALGRIND') is not None: time.sleep(5) else: time.sleep(0.5) local_broker = mosq_test.start_broker(cmd=local_cmd, filename=os.path.basename(__file__)+'_local2', port=port2) if os.environ.get('MOSQ_USE_VALGRIND') is not None: time.sleep(5) else: time.sleep(0.5) pub = None try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port1) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Helper helper_connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive, proto_ver=proto_ver) helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) helper_publish_packet = mosq_test.gen_publish("bridge/reconnect", qos=1, mid=1, payload="bridge-reconnect-message", proto_ver=proto_ver) helper_puback_packet = mosq_test.gen_puback(mid=1, proto_ver=proto_ver) helper_disconnect_packet = mosq_test.gen_disconnect(proto_ver=proto_ver) helper_sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, port=port2, connack_error="helper connack") mosq_test.do_send_receive(helper_sock, helper_publish_packet, helper_puback_packet, "puback") helper_sock.send(helper_disconnect_packet) helper_sock.close() # End of helper # Should have now received a publish command mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) time.sleep(1) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) local_broker.terminate() local_broker.wait() try: os.remove('mosquitto-%d.db' % (port1)) except OSError: pass if rc: (stdo, stde) = local_broker.communicate() print(stde.decode('utf-8')) if pub: (stdo, stde) = pub.communicate() print(stdo.decode('utf-8')) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/07-will-control.py000077500000000000000000000023551450213760600213500ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client setting a will with $CONTROL in is denied from mosq_test_helper import * def do_test(start_broker, proto_ver): rc = 1 mid = 1 connect_packet = mosq_test.gen_connect("will", will_topic="$CONTROL/dynamic-security/v1", will_payload=b"will-message", proto_ver=proto_ver) port = mosq_test.get_port() if start_broker: broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.client_connect_only(port=port) sock.send(connect_packet) d = sock.recv(1) if d == b"": rc = 0 sock.close() except mosq_test.TestError: pass except Exception as e: print(e) finally: if start_broker: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) else: return rc def all_tests(start_broker=False): rc = do_test(start_broker, proto_ver=4) if rc: return rc; rc = do_test(start_broker, proto_ver=5) if rc: return rc; return 0 if __name__ == '__main__': all_tests(True) mosquitto-2.0.18/test/broker/07-will-delay-invalid-573191.py000077500000000000000000000016211450213760600231540ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for https://bugs.eclipse.org/bugs/show_bug.cgi?id=573191 # Check under valgrind/asan for leaks. from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 mid = 1 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 3) connect_packet = mosq_test.gen_connect("will-573191-test", keepalive=keepalive, proto_ver=5, will_topic="", will_properties=props) connack_packet = b"" port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port) sock.close() rc = 0 finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() mosquitto-2.0.18/test/broker/07-will-delay-reconnect.py000077500000000000000000000044041450213760600227410ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client with a will delay handles correctly on the client reconnecting # First connection is durable, second is clean session, and without a will, so the will should not be received. # MQTT 5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 3) connect2a_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5, will_topic="will/test", will_payload=b"will delay", will_properties=props, clean_session=False) connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect2b_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5, clean_session=True) connack2b_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port) mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2a_packet, connack2a_packet, timeout=30, port=port) sock2.close() time.sleep(1) sock2 = mosq_test.do_client_connect(connect2b_packet, connack2b_packet, timeout=30, port=port) time.sleep(3) # The client2 has reconnected within the original will delay interval, which has now # passed, but it should have been deleted anyway. Disconnect and see # whether we get the old will. We should not. sock2.close() mosq_test.do_ping(sock1) rc = 0 sock1.close() sock2.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() mosquitto-2.0.18/test/broker/07-will-delay-recover.py000077500000000000000000000043551450213760600224330ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client with a will delay recovers on the client reconnecting # MQTT 5 from mosq_test_helper import * def do_test(clean_session): rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 30) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 3) connect2_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5, will_topic="will/test", will_payload=b"will delay", will_properties=props, clean_session=clean_session, properties=connect_props) connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=5) if clean_session == True: connack2b_packet = mosq_test.gen_connack(rc=0, proto_ver=5) else: connack2b_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1) subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port) mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2a_packet, timeout=30, port=port) sock2.close() time.sleep(1) sock2 = mosq_test.do_client_connect(connect2_packet, connack2b_packet, timeout=30, port=port) time.sleep(3) # The client2 has reconnected within the will delay interval, which has now # passed. We should not have received the will at this point. mosq_test.do_ping(sock1) rc = 0 sock1.close() sock2.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(clean_session=True) do_test(clean_session=False) mosquitto-2.0.18/test/broker/07-will-delay-session-expiry.py000077500000000000000000000035701450213760600237650ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client that connects with a will delay that is longer than # their session expiry interval has their will published. # MQTT 5 # https://github.com/eclipse/mosquitto/issues/1401 from mosq_test_helper import * rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-test", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 4) connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 2) connect2_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5, properties=connect_props, will_topic="will/test", will_payload=b"will delay", will_qos=2, will_properties=will_props) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish_packet = mosq_test.gen_publish("will/test", qos=0, payload="will delay", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port, connack_error="connack1") mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port, connack_error="connack2") time.sleep(1) sock2.close() # Wait for session to expire time.sleep(3) mosq_test.expect_packet(sock1, "publish", publish_packet) rc = 0 sock1.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/07-will-delay-session-expiry2.py000077500000000000000000000035711450213760600240500ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client that connects with a will delay that is shorter than # their session expiry interval has their will published. # MQTT 5 # https://github.com/eclipse/mosquitto/issues/1401 from mosq_test_helper import * rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-test", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 2) connect_props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 4) connect2_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5, properties=connect_props, will_topic="will/test", will_payload=b"will delay", will_qos=2, will_properties=will_props) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish_packet = mosq_test.gen_publish("will/test", qos=0, payload="will delay", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port, connack_error="connack1") mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port, connack_error="connack2") time.sleep(1) sock2.close() # Wait for session to expire time.sleep(3) mosq_test.expect_packet(sock1, "publish", publish_packet) rc = 0 sock1.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/07-will-delay.py000077500000000000000000000035721450213760600207700ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client will is transmitted with a delay correctly. # MQTT 5 from mosq_test_helper import * def do_test(clean_session): rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 3) connect2_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5, will_topic="will/test", will_payload=b"will delay", will_qos=2, will_properties=props, clean_session=clean_session) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish_packet = mosq_test.gen_publish("will/test", qos=0, payload="will delay", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port) mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port) sock2.close() t_start = time.time() mosq_test.expect_packet(sock1, "publish", publish_packet) t_finish = time.time() if t_finish - t_start > 2 and t_finish - t_start < 5: rc = 0 sock1.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(clean_session=True) do_test(clean_session=False) mosquitto-2.0.18/test/broker/07-will-disconnect-with-will.py000077500000000000000000000033121450213760600237310ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client will is transmitted when a client disconnects with DISCONNECT with will. # MQTT 5 from mosq_test_helper import * def do_test(): rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connect2_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5, will_topic="will/test", will_payload=b"will delay", will_qos=2) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(reason_code=4, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish_packet = mosq_test.gen_publish("will/test", qos=0, payload="will delay", proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port) mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port) sock2.send(disconnect_packet) mosq_test.expect_packet(sock1, "publish", publish_packet) rc = 0 sock2.close() sock1.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test() mosquitto-2.0.18/test/broker/07-will-invalid-utf8.py000077500000000000000000000020401450213760600221710ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a will topic with invalid UTF-8 fails from mosq_test_helper import * def do_test(proto_ver): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("will-invalid-utf8", keepalive=keepalive, will_topic="invalid/utf8", proto_ver=proto_ver) b = list(struct.unpack("B"*len(connect_packet), connect_packet)) b[40] = 0 # Topic should never have a 0x0000 connect_packet = struct.pack("B"*len(b), *b) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, b"", timeout=30, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/07-will-no-flag.py000077500000000000000000000022541450213760600212110ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is disconnected if it sets the will flag but does # not provide a will payload. from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("will-no-payload", keepalive=keepalive, will_topic="will/topic", will_qos=1, will_retain=True, proto_ver=proto_ver) b = list(struct.unpack("B"*len(connect_packet), connect_packet)) bmod = b[0:len(b)-2] bmod[1] = bmod[1] - 2 # Reduce remaining length by two to remove final two payload length values connect_packet = struct.pack("B"*len(bmod), *bmod) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, b"", port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/07-will-null-topic.py000077500000000000000000000021411450213760600217470ustar00rootroot00000000000000#!/usr/bin/env python3 import struct from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("will-null-topic", keepalive=keepalive, will_topic="", will_payload=struct.pack("!4sB7s", b"will", 0, b"message"), proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=2, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, b"", timeout=30, port=port) rc = 0 sock.close() except socket.error as e: if e.errno == errno.ECONNRESET: # Connection has been closed by peer, this is the expected behaviour rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/07-will-null.py000077500000000000000000000032701450213760600206370ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client will is transmitted correctly with a null payload. from mosq_test_helper import * def helper(port, proto_ver): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60, will_topic="will/null/test", proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() def do_test(proto_ver): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "will/null/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("will/null/test", qos=0, proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") helper(port, proto_ver) mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/07-will-oversize-payload.py000077500000000000000000000052651450213760600231700ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client will that is too large is handled from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("message_size_limit 1\n") def do_test(proto_ver, clean_session): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("will-test", keepalive=keepalive, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connect_packet_ok = mosq_test.gen_connect("test-helper", keepalive=keepalive, will_topic="will/qos0/test", will_payload=b"A", clean_session=clean_session, proto_ver=proto_ver, session_expiry=60) connack_packet_ok = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connect_packet_bad = mosq_test.gen_connect("test-helper", keepalive=keepalive, will_topic="will/qos0/test", will_payload=b"AB", clean_session=clean_session, proto_ver=proto_ver, session_expiry=60) if proto_ver == 5: connack_packet_bad = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_PACKET_TOO_LARGE, proto_ver=proto_ver, property_helper=False) else: connack_packet_bad = mosq_test.gen_connack(rc=5, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "will/qos0/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("will/qos0/test", qos=0, payload="A", proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect_packet_bad, connack_packet_bad, port=port, timeout=5) sock2.close() sock2 = mosq_test.do_client_connect(connect_packet_ok, connack_packet_ok, port=port, timeout=5) sock2.close() mosq_test.expect_packet(sock, "publish", publish_packet) # Check there are no more messages mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4, True) do_test(4, False) do_test(5, True) do_test(5, False) exit(0) mosquitto-2.0.18/test/broker/07-will-per-listener.py000077500000000000000000000040521450213760600222750ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client will is transmitted correctly, with per_listener_settings enabled from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("per_listener_settings true\n") f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") def do_test(proto_ver, clean_session): rc = 1 mid = 53 connect1_packet = mosq_test.gen_connect("will-qos0-test", proto_ver=proto_ver) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) 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) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "will/qos0/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("will/qos0/test", qos=0, payload="will-message", proto_ver=proto_ver) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) try: sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port, timeout=5) sock2.close() mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4, True) do_test(4, False) do_test(5, True) do_test(5, False) exit(0) mosquitto-2.0.18/test/broker/07-will-properties.py000077500000000000000000000111341450213760600220570ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for bug #1244. This occurs if a V5 will message is used where the first # Will property is one of: content-type, payload-format-indicator, # response-topic. These are the properties that are attached to the will for # later use, as opposed to e.g. will-delay-interval which is a value which is # read immediately and not passed from mosq_test_helper import * def do_test(will_props, recvd_props): rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe1_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) connect2_packet = mosq_test.gen_connect("will-test", keepalive=keepalive, proto_ver=5, will_topic="will/test", will_payload=b"will payload", will_properties=will_props) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5) publish_packet = mosq_test.gen_publish("will/test", qos=0, payload="will payload", proto_ver=5, properties=recvd_props) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port) mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=30, port=port) sock2.close() mosq_test.expect_packet(sock1, "publish", publish_packet) rc = 0 sock1.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) # Single test property will_props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") #do_test(will_props, will_props) # Multiple test properties will_props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") will_props += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0) #do_test(will_props, will_props) # Multiple test properties, with property that is removed will_props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") will_props += mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 0) will_props += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0) recv_props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") recv_props += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0) #do_test(will_props, recv_props) # Multiple test properties, with property that is removed *first* will_props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 0) will_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") will_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "data") recv_props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") recv_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "data") #do_test(will_props, recv_props) # All properties, plus multiple user properties (excluding # message-expiry-interval, for ease of testing reasons) will_props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key1", "value1") will_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") will_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "data") will_props += mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_WILL_DELAY_INTERVAL, 0) will_props += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 1) will_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "application/test") will_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key2", "value2") recv_props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key1", "value1") recv_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") recv_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "data") recv_props += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 1) recv_props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "application/test") recv_props += mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key2", "value2") do_test(will_props, recv_props) mosquitto-2.0.18/test/broker/07-will-qos0.py000077500000000000000000000033531450213760600205510ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client will is transmitted correctly. from mosq_test_helper import * def do_test(proto_ver, clean_session): rc = 1 mid = 53 keepalive = 60 connect1_packet = mosq_test.gen_connect("will-qos0-test", keepalive=keepalive, proto_ver=proto_ver) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connect2_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive, will_topic="will/qos0/test", will_payload=b"will-message", clean_session=clean_session, proto_ver=proto_ver, session_expiry=60) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "will/qos0/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("will/qos0/test", qos=0, payload="will-message", proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port, timeout=5) sock2.close() mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4, True) do_test(4, False) do_test(5, True) do_test(5, False) exit(0) mosquitto-2.0.18/test/broker/07-will-reconnect-1273.py000077500000000000000000000052011450213760600222330ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a persistent client that disconnects with DISCONNECT has its # will published when it reconnects. It shouldn't. Bug 1273: # https://github.com/eclipse/mosquitto/issues/1273 from mosq_test_helper import * def do_test(proto_ver): rc = 1 keepalive = 60 connect1_packet = mosq_test.gen_connect("will-sub", keepalive=keepalive, proto_ver=proto_ver) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=proto_ver) suback1_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) connect2_packet = mosq_test.gen_connect("will-1273", keepalive=keepalive, will_topic="will/test", will_payload=b"will msg",clean_session=False, proto_ver=proto_ver, session_expiry=60) connack2a_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connack2b_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver) disconnect_packet = mosq_test.gen_disconnect(proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("will/test", qos=0, payload="alive", proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # Connect and subscribe will-sub sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=30, port=port, connack_error="connack1") mosq_test.do_send_receive(sock1, subscribe1_packet, suback1_packet, "suback") # Connect will-1273 sock2 = mosq_test.do_client_connect(connect2_packet, connack2a_packet, timeout=30, port=port) # Publish our "alive" message sock2.send(publish_packet) # Clean disconnect sock2.send(disconnect_packet) # will-1273 should get the "alive" mosq_test.expect_packet(sock1, "publish1", publish_packet) sock2.close() # Reconnect sock2 = mosq_test.do_client_connect(connect2_packet, connack2b_packet, timeout=30, port=port, connack_error="connack2") # will-1273 to publish "alive" again, and will-sub to receive it. sock2.send(publish_packet) mosq_test.expect_packet(sock1, "publish2", publish_packet) # Do a ping to make sure there are no other packets received. mosq_test.do_ping(sock1) rc = 0 sock1.close() sock2.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) exit(0) mosquitto-2.0.18/test/broker/07-will-takeover.py000077500000000000000000000104701450213760600215050ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a will is published when a client takes over an existing session that has a will set. # from mosq_test_helper import * def do_test(proto_ver, clean_session1, clean_session2): rc = 1 keepalive = 60 mid = 1 connect1_packet = mosq_test.gen_connect("will-helper", keepalive=keepalive, proto_ver=proto_ver) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) if proto_ver == 5: if clean_session1 == False: connect_props1 = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 60) else: connect_props1 = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 0) if clean_session2 == False: connect_props2 = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 60) else: connect_props2 = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 0) else: connect_props1 = b"" connect_props2 = b"" connect2_packet = mosq_test.gen_connect("will-test", keepalive=keepalive, proto_ver=proto_ver, will_topic="will/test", will_payload=b"LWT", clean_session=clean_session1, properties=connect_props1) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connect3_packet = mosq_test.gen_connect("will-test", keepalive=keepalive, proto_ver=proto_ver, clean_session=clean_session2, properties=connect_props2) if clean_session1 == False and clean_session2 == False: connack3_packet = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver) else: connack3_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet = mosq_test.gen_subscribe(mid, "will/test", 0, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish(topic="will/test", qos=0, payload="Client ready", proto_ver=proto_ver) publish_lwt_packet = mosq_test.gen_publish(topic="will/test", qos=0, payload="LWT", proto_ver=proto_ver) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # Connect helper to look for will being published sock1 = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=5, port=port) mosq_test.do_send_receive(sock1, subscribe_packet, suback_packet, "suback") # Connect client with will sock2 = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=5, port=port) # Send a "ready" message sock2.send(publish_packet) mosq_test.expect_packet(sock1, "publish 1", publish_packet) # Connect client with will again as a separate connection, this should # take over from the previous one but only trigger a Will if we are taking # over a clean session/session-expiry-interval==0 client sock3 = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=5, port=port) sock2.close() if clean_session1 == True or clean_session2 == True: mosq_test.expect_packet(sock1, "publish LWT", publish_lwt_packet) # Send the "ready" message again sock3.send(publish_packet) mosq_test.expect_packet(sock1, "publish 2", publish_packet) # If the helper has received a will message, then the ping test will fail mosq_test.do_ping(sock1) rc = 0 sock1.close() sock2.close() sock3.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d clean_session1=%d clean_session2=%d" % (proto_ver, clean_session1, clean_session2)) exit(rc) do_test(proto_ver=4, clean_session1=True, clean_session2=True) do_test(proto_ver=4, clean_session1=False, clean_session2=True) do_test(proto_ver=4, clean_session1=True, clean_session2=False) do_test(proto_ver=4, clean_session1=False, clean_session2=False) do_test(proto_ver=5, clean_session1=True, clean_session2=True) do_test(proto_ver=5, clean_session1=False, clean_session2=True) do_test(proto_ver=5, clean_session1=True, clean_session2=False) do_test(proto_ver=5, clean_session1=False, clean_session2=False) mosquitto-2.0.18/test/broker/08-ssl-bridge-helper.py000077500000000000000000000010441450213760600222260ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("test-helper", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish("bridge/ssl/test", qos=0, payload="message") disconnect_packet = mosq_test.gen_disconnect() sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, connack_error="helper connack") sock.send(publish_packet) sock.send(disconnect_packet) sock.close() exit(0) mosquitto-2.0.18/test/broker/08-ssl-bridge.py000077500000000000000000000046441450213760600207620ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge_test\n") f.write("address 127.0.0.1:%d\n" % (port1)) f.write("topic bridge/# both 0\n") f.write("notifications false\n") f.write("restart_timeout 2\n") f.write("\n") f.write("bridge_cafile ../ssl/all-ca.crt\n") f.write("bridge_insecure true\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 60 client_id = socket.gethostname()+".bridge_test" connect_packet = mosq_test.gen_connect(client_id, keepalive=keepalive, clean_session=False, proto_ver=128+4) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "bridge/#", 0) suback_packet = mosq_test.gen_suback(mid, 0) publish_packet = mosq_test.gen_publish("bridge/ssl/test", qos=0, payload="message") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt") context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key") ssock = context.wrap_socket(sock, server_side=True) ssock.settimeout(20) ssock.bind(('', port1)) ssock.listen(5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: (bridge, address) = ssock.accept() bridge.settimeout(20) mosq_test.expect_packet(bridge, "connect", connect_packet) bridge.send(connack_packet) mosq_test.expect_packet(bridge, "subscribe", subscribe_packet) bridge.send(suback_packet) pub = subprocess.Popen(['./08-ssl-bridge-helper.py', str(port2)], stdout=subprocess.PIPE, stderr=subprocess.PIPE) pub.wait() (stdo, stde) = pub.communicate() mosq_test.expect_packet(bridge, "publish", publish_packet) rc = 0 bridge.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: bridge.close() except NameError: pass broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) ssock.close() exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-cert-auth-crl.py000077500000000000000000000032671450213760600236270ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("listener %d\n" % (port1)) f.write("allow_anonymous true\n") f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("require_certificate true\n") f.write("crlfile ../ssl/crl.pem\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-success-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, connack_packet, "connack") rc = 0 ssock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-cert-auth-expired.py000077500000000000000000000035171450213760600245050ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a valid CONNECT results in the correct CONNACK packet using an # SSL connection with client certificates required. from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("listener %d\n" % (port1)) f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("require_certificate true\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-success-test", keepalive=keepalive) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") context.load_cert_chain(certfile="../ssl/client-expired.crt", keyfile="../ssl/client-expired.key") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) try: ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, "", "connack") except ssl.SSLError as err: if err.errno == 1: rc = 0 else: broker.terminate() raise ValueError(err.errno) except mosq_test.TestError: pass finally: os.remove(conf_file) time.sleep(0.5) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-cert-auth-revoked.py000077500000000000000000000036011450213760600244760ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("listener %d\n" % (port1)) f.write("allow_anonymous true\n") f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("require_certificate true\n") f.write("crlfile ../ssl/crl.pem\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-revoked-test", keepalive=keepalive) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") context.load_cert_chain(certfile="../ssl/client-revoked.crt", keyfile="../ssl/client-revoked.key") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) try: ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, "", "connack") except ssl.SSLError as err: if err.errno == 1 and "certificate revoked" in err.strerror: rc = 0 else: broker.terminate() print(err.strerror) raise ValueError(err.errno) except mosq_test.TestError: pass finally: os.remove(conf_file) time.sleep(0.5) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-cert-auth-without.py000077500000000000000000000030711450213760600245430ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client can connect without an SSL certificate if one is required. from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("listener %d\n" % (port1)) f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("require_certificate true\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-cert-test", keepalive=keepalive) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH) ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) try: ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, "", "connack") except ssl.SSLError as err: if err.errno == 1: rc = 0 except socket.error as err: if err.errno == errno.ECONNRESET: rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-cert-auth.py000077500000000000000000000033521450213760600230440ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("listener %d\n" % (port1)) f.write("allow_anonymous true\n") f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("require_certificate true\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-success-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, connack_packet, "connack") rc = 0 ssock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-identity.py000077500000000000000000000034331450213760600230010ustar00rootroot00000000000000#!/usr/bin/env python3 # Client connects with a certificate to a server that has use_identity_as_username=true. Shouldn't be rejected. from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("listener %d\n" %(port1)) f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("\n") f.write("use_identity_as_username true\n") f.write("require_certificate true\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-identity-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, connack_packet, "connack") rc = 0 ssock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) time.sleep(0.5) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-no-auth-wrong-ca.py000077500000000000000000000027661450213760600242460ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("listener %d\n" % (port1)) f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-success-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-alt-ca.crt") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) try: ssock.connect(("localhost", port1)) except ssl.SSLError as err: if err.errno == 1: rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) ssock.close() time.sleep(0.5) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-no-auth.py000077500000000000000000000031731450213760600225240ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a valid CONNECT results in the correct CONNACK packet using an SSL connection. from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("\n") f.write("listener %d\n" % (port1)) f.write("allow_anonymous true\n") f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-success-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, connack_packet, "connack") rc = 0 ssock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-connect-no-identity.py000077500000000000000000000032301450213760600234060ustar00rootroot00000000000000#!/usr/bin/env python3 # Client connects without a certificate to a server that has use_identity_as_username=true. Should be rejected. from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("port %d\n" % (port2)) f.write("\n") f.write("listener %d\n" % (port1)) f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("\n") f.write("use_identity_as_username true\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-no-identity-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=4) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port2, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) ssock.connect(("localhost", port1)) mosq_test.do_send_receive(ssock, connect_packet, connack_packet, "connack") rc = 0 ssock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) time.sleep(2) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-ssl-hup-disconnect.py000077500000000000000000000050551450213760600224460ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client connected with a client certificate when # use_identity_as_username is true is then disconnected when a SIGHUP is # received. # https://github.com/eclipse/mosquitto/issues/1402 from mosq_test_helper import * import signal if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, pw_file, port, option): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("cafile ../ssl/all-ca.crt\n") f.write("certfile ../ssl/server.crt\n") f.write("keyfile ../ssl/server.key\n") f.write("require_certificate true\n") f.write("%s true\n" % (option)) f.write("password_file %s\n" % (pw_file)) def write_pwfile(filename): with open(filename, 'w') as f: # Username "test client", password test f.write('test client:$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==\n') def do_test(option): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') pw_file = os.path.basename(__file__).replace('.py', '.pwfile') write_config(conf_file, pw_file, port, option) write_pwfile(pw_file) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-success-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) context = ssl.create_default_context(ssl.Purpose.SERVER_AUTH, cafile="../ssl/test-root-ca.crt") context.load_cert_chain(certfile="../ssl/client.crt", keyfile="../ssl/client.key") ssock = context.wrap_socket(sock, server_hostname="localhost") ssock.settimeout(20) ssock.connect(("localhost", port)) mosq_test.do_send_receive(ssock, connect_packet, connack_packet, "connack") broker.send_signal(signal.SIGHUP) time.sleep(1) # This will fail if we've been disconnected mosq_test.do_ping(ssock) rc = 0 ssock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) os.remove(pw_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test("use_identity_as_username") do_test("use_subject_as_username") exit(0) mosquitto-2.0.18/test/broker/08-tls-psk-bridge.psk000066400000000000000000000000221450213760600217020ustar00rootroot00000000000000psk-test:deadbeef mosquitto-2.0.18/test/broker/08-tls-psk-bridge.py000077500000000000000000000055271450213760600215570ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config1(filename, port1, port2): with open(filename, 'w') as f: f.write("allow_anonymous true\n") f.write("\n") f.write("psk_file 08-tls-psk-bridge.psk\n") f.write("\n") f.write("port %d\n" % (port1)) f.write("\n") f.write("listener %d\n" % (port2)) f.write("psk_hint hint\n") def write_config2(filename, port2, port3): with open(filename, 'w') as f: f.write("port %d\n" % (port3)) f.write("allow_anonymous true\n") f.write("\n") f.write("connection bridge-psk\n") f.write("address localhost:%d\n" % (port2)) f.write("topic psk/test out\n") f.write("bridge_identity psk-test\n") f.write("bridge_psk deadbeef\n") (port1, port2, port3) = mosq_test.get_port(3) conf_file1 = "08-tls-psk-bridge.conf" conf_file2 = "08-tls-psk-bridge.conf2" write_config1(conf_file1, port1, port2) write_config2(conf_file2, port2, port3) env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("no-psk-test-client", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "psk/test", 0) suback_packet = mosq_test.gen_suback(mid, 0) publish_packet = mosq_test.gen_publish(topic="psk/test", payload="message", qos=0) bridge_cmd = ['../../src/mosquitto', '-c', '08-tls-psk-bridge.conf2'] broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1) bridge = mosq_test.start_broker(filename=os.path.basename(__file__)+'_bridge', cmd=bridge_cmd, port=port3) pub = None try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=30, port=port1) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") pub = subprocess.Popen(['./c/08-tls-psk-bridge.test', str(port3)], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if pub.wait(): raise ValueError (stdo, stde) = pub.communicate() mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file1) os.remove(conf_file2) time.sleep(1) broker.terminate() broker.wait() time.sleep(1) bridge.terminate() bridge.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) (stdo, stde) = bridge.communicate() print(stde.decode('utf-8')) if pub: (stdo, stde) = pub.communicate() print(stdo.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/08-tls-psk-pub.psk000066400000000000000000000000201450213760600212320ustar00rootroot00000000000000psk-id:deadbeef mosquitto-2.0.18/test/broker/08-tls-psk-pub.py000077500000000000000000000036601450213760600211050ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("allow_anonymous true\n") f.write("psk_file 08-tls-psk-pub.psk\n") f.write("\n") f.write("port %d\n" % (port1)) f.write("psk_hint hint\n") f.write("\n") f.write("listener %d\n" % (port2)) f.write("log_type all\n") (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("no-psk-test-client", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "psk/test", 0) suback_packet = mosq_test.gen_suback(mid, 0) publish_packet = mosq_test.gen_publish(topic="psk/test", payload="message", qos=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port2) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port2) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") pub = subprocess.Popen(['./c/08-tls-psk-pub.test', str(port1)], env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE) if pub.wait(): raise ValueError (stdo, stde) = pub.communicate() mosq_test.expect_packet(sock, "publish", publish_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-acl-access-variants.py000077500000000000000000000107041450213760600225450ustar00rootroot00000000000000#!/usr/bin/env python3 # Check access from mosq_test_helper import * def write_config(filename, port, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) def write_acl(filename, global_en, user_en, pattern_en): with open(filename, 'w') as f: if global_en: f.write('topic readwrite topic/global/#\n') f.write('topic deny topic/global/except\n') if user_en: f.write('user username\n') f.write('topic readwrite topic/username/#\n') f.write('topic deny topic/username/except\n') if pattern_en: f.write('pattern readwrite pattern/%u/#\n') f.write('pattern deny pattern/%u/except\n') def single_test(port, per_listener, username, topic, expect_deny): rc = 1 conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, per_listener) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: keepalive = 60 connect_packet = mosq_test.gen_connect("acl-check", keepalive=keepalive, username=username) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid=mid, topic=topic, qos=1) suback_packet = mosq_test.gen_suback(mid=mid, qos=1) mid = 2 publish1s_packet = mosq_test.gen_publish(topic=topic, mid=mid, qos=1, payload="message") puback1s_packet = mosq_test.gen_puback(mid) mid=1 publish1r_packet = mosq_test.gen_publish(topic=topic, mid=mid, qos=1, payload="message") sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.send(publish1s_packet) if expect_deny: mosq_test.expect_packet(sock, "puback", puback1s_packet) mosq_test.do_ping(sock) else: mosq_test.receive_unordered(sock, puback1s_packet, publish1r_packet, "puback / publish1r") sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) def acl_test(port, per_listener, global_en, user_en, pattern_en): acl_file = os.path.basename(__file__).replace('.py', '.acl') write_acl(acl_file, global_en=global_en, user_en=user_en, pattern_en=pattern_en) if global_en: single_test(port, per_listener, username=None, topic="topic/global", expect_deny=False) single_test(port, per_listener, username="username", topic="topic/global", expect_deny=True) single_test(port, per_listener, username=None, topic="topic/global/except", expect_deny=True) if user_en: single_test(port, per_listener, username=None, topic="topic/username", expect_deny=True) single_test(port, per_listener, username="username", topic="topic/username", expect_deny=False) single_test(port, per_listener, username="username", topic="topic/username/except", expect_deny=True) if pattern_en: single_test(port, per_listener, username=None, topic="pattern/username", expect_deny=True) single_test(port, per_listener, username="username", topic="pattern/username", expect_deny=False) single_test(port, per_listener, username="username", topic="pattern/username/except", expect_deny=True) def do_test(port, per_listener): try: acl_test(port, per_listener, global_en=False, user_en=False, pattern_en=True) acl_test(port, per_listener, global_en=False, user_en=True, pattern_en=False) acl_test(port, per_listener, global_en=True, user_en=False, pattern_en=False) acl_test(port, per_listener, global_en=False, user_en=True, pattern_en=True) acl_test(port, per_listener, global_en=True, user_en=False, pattern_en=True) acl_test(port, per_listener, global_en=True, user_en=True, pattern_en=True) finally: acl_file = os.path.basename(__file__).replace('.py', '.acl') os.remove(acl_file) port = mosq_test.get_port() do_test(port, "true") do_test(port, "false") mosquitto-2.0.18/test/broker/09-acl-change.py000077500000000000000000000103261450213760600207040ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether messages deliver or not after some access is revoked. from mosq_test_helper import * import signal def write_config(filename, port, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) def write_acl(filename, en): with open(filename, 'w') as f: f.write('user username\n') f.write('topic readwrite topic/one\n') if en: f.write('topic readwrite topic/two\n') keepalive = 60 username = "username" connect1_packet = mosq_test.gen_connect("acl-check", keepalive=keepalive, username=username, clean_session=False) connack1a_packet = mosq_test.gen_connack(rc=0) connack1b_packet = mosq_test.gen_connack(rc=0, flags=1) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid=mid, topic="topic/one", qos=1) suback1_packet = mosq_test.gen_suback(mid=mid, qos=1) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid=mid, topic="topic/two", qos=1) suback2_packet = mosq_test.gen_suback(mid=mid, qos=1) disconnect_packet = mosq_test.gen_disconnect() connect2_packet = mosq_test.gen_connect("helper", keepalive=keepalive, username=username) connack2_packet = mosq_test.gen_connack(rc=0) mid = 1 publish1s_packet = mosq_test.gen_publish(topic="topic/one", mid=mid, qos=1, payload="message1") puback1s_packet = mosq_test.gen_puback(mid) mid = 2 publish2s_packet = mosq_test.gen_publish(topic="topic/two", mid=mid, qos=1, payload="message2") puback2s_packet = mosq_test.gen_puback(mid) mid = 1 publish1r_packet = mosq_test.gen_publish(topic="topic/one", mid=mid, qos=1, payload="message1") puback1r_packet = mosq_test.gen_puback(mid) mid = 2 publish3s_packet = mosq_test.gen_publish(topic="topic/one", mid=mid, qos=1, payload="message3") puback3s_packet = mosq_test.gen_puback(mid) mid = 3 publish3r_packet = mosq_test.gen_publish(topic="topic/one", mid=mid, qos=1, payload="message3") puback3r_packet = mosq_test.gen_puback(mid) mid = 3 publish4s_packet = mosq_test.gen_publish(topic="topic/two", mid=mid, qos=1, payload="message4") puback4s_packet = mosq_test.gen_puback(mid) rc = 1 port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, "false") acl_file = os.path.basename(__file__).replace('.py', '.acl') write_acl(acl_file, True) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: keepalive = 60 # Connect, subscribe, then disconnect sock = mosq_test.do_client_connect(connect1_packet, connack1a_packet, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") sock.send(disconnect_packet) sock.close() # Helper publish to topic/one and topic/two, will be queued for other client sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, port=port) mosq_test.do_send_receive(sock, publish1s_packet, puback1s_packet, "puback1") mosq_test.do_send_receive(sock, publish2s_packet, puback2s_packet, "puback2") sock.close() # Reload ACLs with topic/two now disabled write_acl(acl_file, False) broker.send_signal(signal.SIGHUP) sock = mosq_test.do_client_connect(connect1_packet, connack1b_packet, port=port) sock.settimeout(10) mosq_test.expect_packet(sock, "publish1r", publish1r_packet) # We don't expect messages to topic/two any more, so we don't expect the queued one sock.send(publish3s_packet) mosq_test.receive_unordered(sock, puback3s_packet, publish3r_packet, "puback3/publish3r") # Send this, don't expect it to succeed mosq_test.do_send_receive(sock, publish4s_packet, puback4s_packet, "puback4") # Check for non delivery with a ping mosq_test.do_ping(sock) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) os.remove(acl_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) port = mosq_test.get_port() mosquitto-2.0.18/test/broker/09-acl-empty-file.py000077500000000000000000000035711450213760600215360ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for CVE-2018-xxxxx from mosq_test_helper import * import signal def write_config(filename, port, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("acl_file %s\n" % (filename.replace('.conf', '.acl'))) def write_acl(filename): with open(filename, 'w') as f: f.write('#comment\n') f.write('\n') def do_test(port, per_listener): conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, per_listener) acl_file = os.path.basename(__file__).replace('.py', '.acl') write_acl(acl_file) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("acl-check", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) mid = 1 publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="message") subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 0) suback_packet = mosq_test.gen_suback(mid, 0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.send(publish_packet) # If we receive the message, this will fail. mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) os.remove(acl_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) port = mosq_test.get_port() do_test(port, "false") do_test(port, "true") mosquitto-2.0.18/test/broker/09-auth-bad-method.py000077500000000000000000000016171450213760600216700ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether sending an Authentication Method produces the correct response # when no auth methods are defined. from mosq_test_helper import * rc = 1 keepalive = 10 props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "basic") connect_packet = mosq_test.gen_connect("connect-test", proto_ver=5, keepalive=keepalive, properties=props) connack_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_BAD_AUTHENTICATION_METHOD, proto_ver=5, properties=None) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-extended-auth-change-username.py000077500000000000000000000064631450213760600245300ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether an extended auth plugin can change the username of a client. from mosq_test_helper import * def write_config(filename, acl_file, port, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("acl_file %s\n" % (acl_file)) f.write("auth_plugin c/auth_plugin_extended_single.so\n") def write_acl(filename): with open(filename, 'w') as f: f.write('user new_username\n') f.write('topic readwrite topic/one\n') port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') acl_file = os.path.basename(__file__).replace('.py', '.acl') def do_test(per_listener): write_config(conf_file, acl_file, port, per_listener) write_acl(acl_file) rc = 1 # Connect without a username - this means no access connect1_packet = mosq_test.gen_connect("client-params-test1", keepalive=42, proto_ver=5) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "topic/one", 1, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 2 publish1_packet = mosq_test.gen_publish("topic/one", qos=1, mid=mid, payload="message", proto_ver=5) puback1_packet = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED) # Connect without a username, but have the plugin change it props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "change") connect2_packet = mosq_test.gen_connect("client-params-test2", keepalive=42, proto_ver=5, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "change") connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) mid = 2 publish2s_packet = mosq_test.gen_publish("topic/one", qos=1, mid=mid, payload="message", proto_ver=5) puback2s_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 1 publish2r_packet = mosq_test.gen_publish("topic/one", qos=1, mid=mid, payload="message", proto_ver=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback1") mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback1") mosq_test.do_ping(sock) sock.close() sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback2") sock.send(publish2s_packet) mosq_test.receive_unordered(sock, puback2s_packet, publish2r_packet, "puback2/publish2") mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) os.remove(acl_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test("true") do_test("false") exit(0) mosquitto-2.0.18/test/broker/09-extended-auth-multistep-reauth.py000077500000000000000000000075331450213760600250010ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("auth_plugin c/auth_plugin_extended_multiple.so\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 # First auth # ========== props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "step1") connect1_packet = mosq_test.gen_connect("client-params-test", keepalive=42, proto_ver=5, properties=props) # Server to client props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "1pets") auth1_1_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props) # Client to server props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "supercalifragilisticexpialidocious") auth1_2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) # Second auth # =========== props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "step1") reauth2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_REAUTHENTICATE, properties=props) # Server to client props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "1pets") auth2_1_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props) # Client to server props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "supercalifragilisticexpialidocious") auth2_2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") auth2_3_packet = mosq_test.gen_auth(reason_code=0, properties=props) # Third auth - bad due to different method # ======================================== props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "badmethod") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "step1") reauth3_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_REAUTHENTICATE, properties=props) # Server to client disconnect3_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_PROTOCOL_ERROR, proto_ver=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, auth1_1_packet, timeout=20, port=port, connack_error="auth1") mosq_test.do_send_receive(sock, auth1_2_packet, connack1_packet, "connack1") mosq_test.do_ping(sock, "pingresp1") mosq_test.do_send_receive(sock, reauth2_packet, auth2_1_packet, "auth2_1") mosq_test.do_send_receive(sock, auth2_2_packet, auth2_3_packet, "auth2_3") mosq_test.do_ping(sock, "pingresp2") mosq_test.do_send_receive(sock, reauth3_packet, disconnect3_packet, "disconnect3") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-extended-auth-multistep.py000077500000000000000000000036331450213760600235100ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_extended_multiple.so\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "step1") connect_packet = mosq_test.gen_connect("client-params-test", keepalive=42, proto_ver=5, properties=props) # Server to client props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "1pets") auth1_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props) # Client to server props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "supercalifragilisticexpialidocious") auth2_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_CONTINUE_AUTHENTICATION, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, auth1_packet, timeout=20, port=port, connack_error="auth1") mosq_test.do_send_receive(sock, auth2_packet, connack_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-extended-auth-reauth.py000077500000000000000000000035651450213760600227560ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_extended_reauth.so\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 # First authentication succeeds props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "repeat") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "repeat") connect_packet = mosq_test.gen_connect("client-params-test", keepalive=42, proto_ver=5, properties=props) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "repeat") props += mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False) # Reauthentication fails props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "repeat") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "repeat") auth_packet = mosq_test.gen_auth(reason_code=mqtt5_rc.MQTT_RC_REAUTHENTICATE, properties=props) disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, auth_packet, disconnect_packet) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-extended-auth-single.py000077500000000000000000000070571450213760600227470ustar00rootroot00000000000000#!/usr/bin/env python3 # Multi tests for extended auth with a single step. # * Error in plugin # * No matching authentication method # * Matching authentication method, but auth rejected # * Matching authentication method, auth succeeds # * Matching authentication method, auth succeeds, new auth data sent back to client from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_extended_single.so\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 # Single, error in plugin props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "error") connect1_packet = mosq_test.gen_connect("client-params-test1", keepalive=42, proto_ver=5, properties=props) # Single, no matching authentication method props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "non-matching") connect2_packet = mosq_test.gen_connect("client-params-test2", keepalive=42, proto_ver=5, properties=props) connack2_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_BAD_AUTHENTICATION_METHOD, proto_ver=5, properties=None) # Single step, matching method, failure props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "single") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "baddata") connect3_packet = mosq_test.gen_connect("client-params-test3", keepalive=42, proto_ver=5, properties=props) connack3_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, properties=None) # Single step, matching method, success props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "single") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "data") connect4_packet = mosq_test.gen_connect("client-params-test5", keepalive=42, proto_ver=5, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "single") connack4_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) # Single step, matching method, success, auth data back to client props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "somedata") connect5_packet = mosq_test.gen_connect("client-params-test6", keepalive=42, proto_ver=5, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "atademos") connack5_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, b"", timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect4_packet, connack4_packet, timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect5_packet, connack5_packet, timeout=20, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-extended-auth-single2.py000077500000000000000000000077551450213760600230360ustar00rootroot00000000000000#!/usr/bin/env python3 # Multi tests for extended auth with a single step - multiple plugins at once. # * Error in plugin # * No matching authentication method # * Matching authentication method, but auth rejected # * Matching authentication method, auth succeeds # * Matching authentication method, auth succeeds, new auth data sent back to client from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_extended_single.so\n") f.write("auth_plugin c/auth_plugin_extended_single2.so\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') def do_test(suffix): write_config(conf_file, port) rc = 1 # Single, error in plugin props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "error%s" % (suffix)) connect1_packet = mosq_test.gen_connect("client-params-test1", keepalive=42, proto_ver=5, properties=props) # Single, no matching authentication method props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "non-matching%s" % (suffix)) connect2_packet = mosq_test.gen_connect("client-params-test2", keepalive=42, proto_ver=5, properties=props) connack2_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_BAD_AUTHENTICATION_METHOD, proto_ver=5, properties=None) # Single step, matching method, failure props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "single%s" % (suffix)) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "baddata") connect3_packet = mosq_test.gen_connect("client-params-test3", keepalive=42, proto_ver=5, properties=props) connack3_packet = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, properties=None) # Single step, matching method, success props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "single%s" % (suffix)) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "data") connect4_packet = mosq_test.gen_connect("client-params-test5", keepalive=42, proto_ver=5, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "single%s" % (suffix)) connack4_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) # Single step, matching method, success, auth data back to client props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror%s" % (suffix)) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "somedata") connect5_packet = mosq_test.gen_connect("client-params-test6", keepalive=42, proto_ver=5, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_METHOD, "mirror%s" % (suffix)) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_AUTHENTICATION_DATA, "atademos") connack5_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, b"", timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect3_packet, connack3_packet, timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect4_packet, connack4_packet, timeout=20, port=port) sock.close() sock = mosq_test.do_client_connect(connect5_packet, connack5_packet, timeout=20, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test("") do_test("2") exit(0) mosquitto-2.0.18/test/broker/09-plugin-acl-change.py000077500000000000000000000046731450213760600222100ustar00rootroot00000000000000#!/usr/bin/env python3 # A clean start=False client connects, and publishes to a topic it has access # to with QoS 2 - but does not send a PUBREL. It closes the connection. The # access to the topic is revoked, the client reconnects and it attempts to # complete the flow. Is the publish allowed? It should not be. from mosq_test_helper import * def write_config(filename, port, plugin_ver): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("auth_plugin c/auth_plugin_acl_change.so\n") f.write("allow_anonymous true\n") def do_test(plugin_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, plugin_ver) rc = 1 connect1_packet = mosq_test.gen_connect("acl-change-test", clean_session=False) connack1_packet = mosq_test.gen_connack(rc=0) connect2_packet = mosq_test.gen_connect("acl-change-test", clean_session=False) connack2_packet = mosq_test.gen_connack(rc=0,flags=1) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "#", 0) suback_packet = mosq_test.gen_suback(mid, 0) mid = 2 publish1_packet = mosq_test.gen_publish("publish/topic", qos=2, mid=mid, payload="message") pubrec1_packet = mosq_test.gen_pubrec(mid) pubrel1_packet = mosq_test.gen_pubrel(mid) pubcomp1_packet = mosq_test.gen_pubcomp(mid) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 1") mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, "pubrec") sock.close() # ACL has changed sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback 2") mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, "pubcomp") mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass except Exception as err: print(err) finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) mosquitto-2.0.18/test/broker/09-plugin-auth-acl-pub.py000077500000000000000000000045261450213760600225050ustar00rootroot00000000000000#!/usr/bin/env python3 # Bug specific test - if a QoS2 publish is denied, then we publish again with # the same mid to a topic that is allowed, does it work properly? from mosq_test_helper import * def write_config(filename, port, plugin_ver): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v%d.so\n" % (plugin_ver)) f.write("allow_anonymous false\n") def do_test(plugin_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, plugin_ver) rc = 1 keepalive = 10 connect1_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="readwrite", clean_session=False) connack1_packet = mosq_test.gen_connack(rc=0) connect2_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="readwrite", clean_session=False) connack2_packet = mosq_test.gen_connack(rc=0,flags=1) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "readonly", 2) suback_packet = mosq_test.gen_suback(mid, 2) mid = 2 publish1_packet = mosq_test.gen_publish("readonly", qos=2, mid=mid, payload="message") pubrec1_packet = mosq_test.gen_pubrec(mid) pubrel1_packet = mosq_test.gen_pubrel(mid) pubcomp1_packet = mosq_test.gen_pubcomp(mid) mid = 2 publish2_packet = mosq_test.gen_publish("writeable", qos=1, mid=mid, payload="message") puback2_packet = mosq_test.gen_puback(mid) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, "pubrec1") sock.close() sock = mosq_test.do_client_connect(connect2_packet, connack2_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, publish2_packet, puback2_packet, "puback2") mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) mosquitto-2.0.18/test/broker/09-plugin-auth-acl-sub-denied.py000077500000000000000000000030621450213760600237300ustar00rootroot00000000000000#!/usr/bin/env python3 # Test topic subscription. All SUBSCRIBE requests are denied. Check this # produces the correct response, and check the client isn't disconnected (ref: # issue #1016). from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_acl_sub_denied.so\n") f.write("allow_anonymous false\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("sub-denied-test", keepalive=keepalive, username="denied") connack_packet = mosq_test.gen_connack(rc=0) mid = 53 subscribe_packet = mosq_test.gen_subscribe(mid, "qos0/test", 0) suback_packet = mosq_test.gen_suback(mid, 128) mid_pub = 54 publish_packet = mosq_test.gen_publish("topic", qos=1, payload="test", mid=mid_pub) puback_packet = mosq_test.gen_puback(mid_pub) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-plugin-auth-acl-sub.py000077500000000000000000000032701450213760600225030ustar00rootroot00000000000000#!/usr/bin/env python3 # Test topic subscription. All topic are allowed but not using wildcard in subscribe. from mosq_test_helper import * def write_config(filename, port, plugin_ver): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v%d.so\n" % (plugin_ver)) f.write("allow_anonymous false\n") def do_test(plugin_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, plugin_ver) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="readonly") connack_packet = mosq_test.gen_connack(rc=0) mid = 53 subscribe_packet = mosq_test.gen_subscribe(mid, "qos0/test", 0) suback_packet = mosq_test.gen_suback(mid, 0) mid_fail = 54 subscribe_packet_fail = mosq_test.gen_subscribe(mid_fail, "#", 0) suback_packet_fail = mosq_test.gen_suback(mid_fail, 0x80) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.do_send_receive(sock, subscribe_packet_fail, suback_packet_fail, "suback") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) mosquitto-2.0.18/test/broker/09-plugin-auth-context-params.py000077500000000000000000000027071450213760600241260ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether message parameters are passed to the plugin acl check function. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_context_params.so\n") f.write("allow_anonymous false\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 connect_packet = mosq_test.gen_connect("client-params-test", keepalive=42, username="client-username") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "param/topic", 1) suback_packet = mosq_test.gen_suback(mid, 1) mid = 3 publish_packet = mosq_test.gen_publish(topic="param/topic", qos=1, payload="payload contents", retain=1, mid=mid) puback_packet = mosq_test.gen_puback(mid) mid = 1 publish_packet_recv = mosq_test.gen_publish(topic="param/topic", qos=1, payload="payload contents", retain=0, mid=mid) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-plugin-auth-defer-unpwd-fail.py000077500000000000000000000024141450213760600243050ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection fail when using a auth_plugin that defer authentication. from mosq_test_helper import * def write_config(filename, port, plugin_ver): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v%d.so\n" % (plugin_ver)) f.write("allow_anonymous false\n") def do_test(plugin_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, plugin_ver) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username@v2", password="doesNotMatter") connack_packet = mosq_test.gen_connack(rc=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) mosquitto-2.0.18/test/broker/09-plugin-auth-defer-unpwd-success.py000077500000000000000000000026001450213760600250370ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is successful with correct username and password # when using a two auth_plugin (first will defer, second will accept). from mosq_test_helper import * def write_config(filename, port, plugin_ver): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v%d.so\n" % (plugin_ver)) f.write("auth_plugin c/auth_plugin_v2.so\n") f.write("allow_anonymous false\n") def do_test(plugin_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, plugin_ver) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username@v2", password="doesNotMatter") connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) mosquitto-2.0.18/test/broker/09-plugin-auth-msg-params.py000077500000000000000000000032121450213760600232200ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether message parameters are passed to the plugin acl check function. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_msg_params.so\n") f.write("allow_anonymous true\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("msg-param-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "param/topic", 1) suback_packet = mosq_test.gen_suback(mid, 1) mid = 3 publish_packet = mosq_test.gen_publish(topic="param/topic", qos=1, payload="payload contents", retain=1, mid=mid) puback_packet = mosq_test.gen_puback(mid) mid = 1 publish_packet_recv = mosq_test.gen_publish(topic="param/topic", qos=1, payload="payload contents", retain=0, mid=mid) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.send(publish_packet) mosq_test.receive_unordered(sock, puback_packet, publish_packet_recv, "puback/publish_receive") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-plugin-auth-unpwd-fail.py000077500000000000000000000024351450213760600232250ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. from mosq_test_helper import * def write_config(filename, port, plugin_ver): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v%d.so\n" % (plugin_ver)) f.write("allow_anonymous false\n") def do_test(plugin_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, plugin_ver) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username", password="wrong") connack_packet = mosq_test.gen_connack(rc=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) mosquitto-2.0.18/test/broker/09-plugin-auth-unpwd-success.py000077500000000000000000000024431450213760600237610ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. from mosq_test_helper import * def write_config(filename, port, plugin_ver): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v%d.so\n" % (plugin_ver)) f.write("allow_anonymous false\n") def do_test(plugin_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, plugin_ver) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username", password="cnwTICONIURW") connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(4) do_test(5) mosquitto-2.0.18/test/broker/09-plugin-auth-v2-unpwd-fail.py000077500000000000000000000021461450213760600235510ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v2.so\n") f.write("allow_anonymous false\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username", password="wrong") connack_packet = mosq_test.gen_connack(rc=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-plugin-auth-v2-unpwd-success.py000077500000000000000000000021551450213760600243060ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a connection is successful with correct username and password # when using a simple auth_plugin. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_v2.so\n") f.write("allow_anonymous false\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("connect-uname-pwd-test", keepalive=keepalive, username="test-username", password="cnwTICONIURW") connack_packet = mosq_test.gen_connack(rc=0) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-plugin-publish.py000077500000000000000000000064621450213760600216720ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("auth_plugin c/auth_plugin_publish.so\n") f.write("allow_anonymous true\n") proto_ver = 5 port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect1_packet = mosq_test.gen_connect("test-client", keepalive=keepalive, username="readwrite", clean_session=False, proto_ver=proto_ver) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("init", qos=0, proto_ver=proto_ver) publish0_packet = mosq_test.gen_publish("topic/0", qos=0, payload="test-message-0", proto_ver=proto_ver) mid = 1 publish1_packet = mosq_test.gen_publish("topic/1", qos=1, mid=mid, payload="test-message-1", proto_ver=proto_ver) puback1_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 2 publish2_packet = mosq_test.gen_publish("topic/2", qos=2, mid=mid, payload="test-message-2", proto_ver=proto_ver) pubrec2_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel2_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp2_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 1) publish0p_packet = mosq_test.gen_publish("topic/0", qos=0, payload="test-message-0", proto_ver=proto_ver, properties=props) mid = 3 publish1p_packet = mosq_test.gen_publish("topic/1", qos=1, mid=mid, payload="test-message-1", proto_ver=proto_ver, properties=props) puback1p_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 4 publish2p_packet = mosq_test.gen_publish("topic/2", qos=2, mid=mid, payload="test-message-2", proto_ver=proto_ver, properties=props) pubrec2p_packet = mosq_test.gen_pubrec(mid, proto_ver=proto_ver) pubrel2p_packet = mosq_test.gen_pubrel(mid, proto_ver=proto_ver) pubcomp2p_packet = mosq_test.gen_pubcomp(mid, proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect1_packet, connack1_packet, timeout=20, port=port) # Trigger the plugin to send us some messages sock.send(publish_packet) mosq_test.expect_packet(sock, "publish0", publish0_packet) mosq_test.expect_packet(sock, "publish1", publish1_packet) sock.send(puback1_packet) mosq_test.expect_packet(sock, "publish2", publish2_packet) mosq_test.do_send_receive(sock, pubrec2_packet, pubrel2_packet, "pubrel1") sock.send(pubcomp2_packet) # And trigger the second set of messages, with properties sock.send(publish_packet) mosq_test.expect_packet(sock, "publish0p", publish0p_packet) mosq_test.expect_packet(sock, "publish1p", publish1p_packet) sock.send(puback1_packet) mosq_test.expect_packet(sock, "publish2p", publish2p_packet) mosq_test.do_send_receive(sock, pubrec2p_packet, pubrel2p_packet, "pubrel1p") sock.send(pubcomp2p_packet) mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/09-plugin-tick.py000077500000000000000000000033461450213760600211540ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a plugin can subscribe to the tick event from mosq_test_helper import * def write_config(filename, port, per_listener_settings="false"): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener_settings)) f.write("listener %d\n" % (port)) f.write("plugin c/auth_plugin_v5_handle_tick.so\n") f.write("allow_anonymous true\n") def do_test(per_listener_settings): proto_ver = 5 port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port, per_listener_settings) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("plugin-tick-test", keepalive=keepalive, username="readwrite", clean_session=False, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) tick_packet = mosq_test.gen_publish("topic/tick", qos=0, payload="test-message", proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=10, port=port) mosq_test.expect_packet(sock, "tick message", tick_packet) mosq_test.expect_packet(sock, "tick message", tick_packet) mosq_test.expect_packet(sock, "tick message", tick_packet) mosq_test.do_ping(sock) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test("false") do_test("true") mosquitto-2.0.18/test/broker/09-pwfile-parse-invalid.py000077500000000000000000000152061450213760600227460ustar00rootroot00000000000000#!/usr/bin/env python3 # Test for CVE-2018-xxxxx. from mosq_test_helper import * import signal def write_config(filename, port, per_listener): with open(filename, 'w') as f: f.write("per_listener_settings %s\n" % (per_listener)) f.write("port %d\n" % (port)) f.write("password_file %s\n" % (filename.replace('.conf', '.pwfile'))) f.write("allow_anonymous false") def write_pwfile(filename, bad_line1, bad_line2): with open(filename, 'w') as f: if bad_line1 is not None: f.write('%s\n' % (bad_line1)) # Username test, password test f.write('test:$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==\n') # Username empty, password 0 length f.write('empty:$6$o+53eGXtmlfHeYrg$FY7X9DNQ4uU1j0NiPmGOOSU05ZSzhqNmNhXIof/0nLpVb1zDhcRHdaC72E3YryH7dtTiG/r6jH6C8J+30cZBgA==\n') if bad_line2 is not None: f.write('%s\n' % (bad_line2)) def do_test(port, connack_rc, username, password): rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("username-password-check", keepalive=keepalive, username=username, password=password) connack_packet = mosq_test.gen_connack(rc=connack_rc) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) rc = 0 sock.close() except mosq_test.TestError: pass finally: if rc: raise AssertionError def username_password_tests(port): broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: do_test(port, connack_rc=0, username='test', password='test') do_test(port, connack_rc=5, username='test', password='bad') do_test(port, connack_rc=5, username='test', password='') do_test(port, connack_rc=5, username='test', password=None) do_test(port, connack_rc=5, username='empty', password='test') do_test(port, connack_rc=5, username='empty', password='bad') do_test(port, connack_rc=5, username='empty', password='') do_test(port, connack_rc=5, username='empty', password=None) do_test(port, connack_rc=5, username='bad', password='test') do_test(port, connack_rc=5, username='bad', password='bad') do_test(port, connack_rc=5, username='bad', password='') do_test(port, connack_rc=5, username='bad', password=None) do_test(port, connack_rc=5, username='', password='test') do_test(port, connack_rc=5, username='', password='bad') do_test(port, connack_rc=5, username='', password='') do_test(port, connack_rc=5, username='', password=None) do_test(port, connack_rc=5, username=None, password='test') do_test(port, connack_rc=5, username=None, password='bad') do_test(port, connack_rc=5, username=None, password='') do_test(port, connack_rc=5, username=None, password=None) except ValueError: pass finally: broker.terminate() broker.wait() def all_tests(port): # Valid file, single user write_pwfile(pw_file, bad_line1=None, bad_line2=None) username_password_tests(port) # Invalid file, first line blank write_pwfile(pw_file, bad_line1='', bad_line2=None) username_password_tests(port) # Invalid file, last line blank write_pwfile(pw_file, bad_line1=None, bad_line2='') username_password_tests(port) # Invalid file, first and last line blank write_pwfile(pw_file, bad_line1='', bad_line2='') username_password_tests(port) # Invalid file, first line 'comment' write_pwfile(pw_file, bad_line1='#comment', bad_line2=None) username_password_tests(port) # Invalid file, last line 'comment' write_pwfile(pw_file, bad_line1=None, bad_line2='#comment') username_password_tests(port) # Invalid file, first and last line 'comment' write_pwfile(pw_file, bad_line1='#comment', bad_line2='#comment') username_password_tests(port) # Invalid file, first line blank and last line 'comment' write_pwfile(pw_file, bad_line1='', bad_line2='#comment') username_password_tests(port) # Invalid file, first line incomplete write_pwfile(pw_file, bad_line1='bad:', bad_line2=None) username_password_tests(port) # Invalid file, first line incomplete, but with "password" write_pwfile(pw_file, bad_line1='bad:bad', bad_line2=None) username_password_tests(port) # Invalid file, first line incomplete, partial password hash write_pwfile(pw_file, bad_line1='bad:$', bad_line2=None) username_password_tests(port) # Invalid file, first line incomplete, partial password hash write_pwfile(pw_file, bad_line1='bad:$6', bad_line2=None) username_password_tests(port) # Invalid file, first line incomplete, partial password hash write_pwfile(pw_file, bad_line1='bad:$6$', bad_line2=None) username_password_tests(port) # Valid file, first line incomplete, has valid salt but no password hash write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E', bad_line2=None) username_password_tests(port) # Valid file, first line incomplete, has valid salt but no password hash write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E$', bad_line2=None) username_password_tests(port) # Valid file, first line has invalid hash designator write_pwfile(pw_file, bad_line1='bad:$5$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) username_password_tests(port) # Invalid file, missing username but valid password hash write_pwfile(pw_file, bad_line1=':$6$njERlZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) username_password_tests(port) # Valid file, valid username but password salt not base64 write_pwfile(pw_file, bad_line1='bad:$6$njER{ZMi/7DzNB9E$iiavfuXvUm8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) username_password_tests(port) # Valid file, valid username but password hash not base64 write_pwfile(pw_file, bad_line1='bad:$6$njERlZMi/7DzNB9E$iiavfuXv{}8iyDZArTy7smTxh07GXXOrOsqxfW6gkOYVXHGk+W+i/8d3xDxrMwEPygEBhoA8A/gjQC0N2M4Lkw==', bad_line2=None) username_password_tests(port) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') pw_file = os.path.basename(__file__).replace('.py', '.pwfile') try: write_config(conf_file, port, "false") all_tests(port) write_config(conf_file, port, "true") all_tests(port) finally: os.remove(conf_file) os.remove(pw_file) sys.exit(0) mosquitto-2.0.18/test/broker/10-listener-mount-point.py000077500000000000000000000060621450213760600230300ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * def write_config(filename, port1, port2): with open(filename, 'w') as f: f.write("listener %d\n" % (port1)) f.write("allow_anonymous true\n") f.write("\n") f.write("listener %d\n" % (port2)) f.write("allow_anonymous true\n") f.write("mount_point mount/\n") f.write("\n") f.write("log_type debug\n") def helper(port, proto_ver): connect_packet = mosq_test.gen_connect("test-helper", keepalive=60, proto_ver=proto_ver) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) publish_packet = mosq_test.gen_publish("test", qos=0, payload="mount point", proto_ver=proto_ver) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port, connack_error="helper connack") sock.send(publish_packet) sock.close() def do_test(proto_ver): (port1, port2) = mosq_test.get_port(2) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port1, port2) rc = 1 mid = 1 # Subscriber for listener with mount point connect_packet1 = mosq_test.gen_connect("test1", proto_ver=proto_ver) connack_packet1 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet1 = mosq_test.gen_subscribe(mid, "#", 0, proto_ver=proto_ver) suback_packet1 = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet1 = mosq_test.gen_publish("mount/test", qos=0, payload="mount point", proto_ver=proto_ver) # Subscriber for listener without mount point connect_packet2 = mosq_test.gen_connect("test2", proto_ver=proto_ver) connack_packet2 = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) subscribe_packet2 = mosq_test.gen_subscribe(mid, "#", 0, proto_ver=proto_ver) suback_packet2 = mosq_test.gen_suback(mid, 0, proto_ver=proto_ver) publish_packet2 = mosq_test.gen_publish("test", qos=0, payload="mount point", proto_ver=proto_ver) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port1) try: sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet1, timeout=20, port=port1) mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet1, "suback1") sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet2, timeout=20, port=port2) mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet2, "suback2") helper(port2, proto_ver) # Should have now received a publish command mosq_test.expect_packet(sock1, "publish1", publish_packet1) mosq_test.expect_packet(sock2, "publish2", publish_packet2) rc = 0 sock1.close() sock2.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/11-message-expiry.py000077500000000000000000000076131450213760600216620ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether the broker reduces the message expiry interval when republishing. # MQTT v5, with a broker restart and persistence. # Client connects with clean session set false, subscribes with qos=1, then disconnects # Helper publishes two messages, one with a short expiry and one with a long expiry # We wait until the short expiry will have expired but the long one not. # Client reconnects, expects delivery of the long expiry message with a reduced # expiry interval property. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("persistence true\n") f.write("persistence_file mosquitto-%d.db\n" % (port)) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5, clean_session=False, session_expiry=60) connack1_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack2_packet = mosq_test.gen_connack(rc=0, proto_ver=5, flags=1) mid = 53 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) helper_connect = mosq_test.gen_connect("helper", proto_ver=5) helper_connack = mosq_test.gen_connack(rc=0, proto_ver=5) mid=1 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 5) publish1s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message1", proto_ver=5, properties=props) puback1s_packet = mosq_test.gen_puback(mid) mid=2 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, 100) publish2s_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message2", proto_ver=5, properties=props) puback2s_packet = mosq_test.gen_puback(mid) mid=3 publish3_packet = mosq_test.gen_publish("subpub/qos1", mid=mid, qos=1, payload="message3", proto_ver=5) puback3_packet = mosq_test.gen_puback(mid) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack1_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.close() helper = mosq_test.do_client_connect(helper_connect, helper_connack, timeout=20, port=port) mosq_test.do_send_receive(helper, publish1s_packet, puback1s_packet, "puback 1") mosq_test.do_send_receive(helper, publish2s_packet, puback2s_packet, "puback 2") mosq_test.do_send_receive(helper, publish3_packet, puback3_packet, "puback 3") broker.terminate() broker.wait() (stdo1, stde1) = broker.communicate() sock.close() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) time.sleep(7) sock = mosq_test.do_client_connect(connect_packet, connack2_packet, timeout=20, port=port) packet = sock.recv(len(publish2s_packet)) for i in range(100, 1, -1): props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MESSAGE_EXPIRY_INTERVAL, i) publish2r_packet = mosq_test.gen_publish("subpub/qos1", mid=2, qos=1, payload="message2", proto_ver=5, properties=props) if packet == publish2r_packet: mosq_test.expect_packet(sock, "publish3", publish3_packet) rc = 0 break sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) exit(rc) mosquitto-2.0.18/test/broker/11-persistent-subscription-no-local.py000077500000000000000000000065501450213760600253430ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. # And whether the no-local option is persisted. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("persistence true\n") f.write("persistence_file mosquitto-%d.db\n" % (port)) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect( "persistent-subscription-test", keepalive=keepalive, clean_session=False, proto_ver=5, session_expiry=60 ) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=5) # session present mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, "subpub/nolocal", 5, proto_ver=5) suback1_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 2 subscribe2_packet = mosq_test.gen_subscribe(mid, "subpub/local", 1, proto_ver=5) suback2_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 1 publish1_packet = mosq_test.gen_publish("subpub/nolocal", qos=1, mid=mid, payload="message", proto_ver=5) puback1_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 2 publish2s_packet = mosq_test.gen_publish("subpub/local", qos=1, mid=mid, payload="message", proto_ver=5) puback2s_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 1 publish2a_packet = mosq_test.gen_publish("subpub/local", qos=1, mid=mid, payload="message", proto_ver=5) puback2a_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 2 publish2b_packet = mosq_test.gen_publish("subpub/local", qos=1, mid=mid, payload="message", proto_ver=5) puback2b_packet = mosq_test.gen_puback(mid, proto_ver=5) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) (stdo1, stde1) = ("", "") try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe1_packet, suback1_packet, "suback1") mosq_test.do_send_receive(sock, subscribe2_packet, suback2_packet, "suback2") mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback1a") sock.send(publish2s_packet) mosq_test.receive_unordered(sock, puback2s_packet, publish2a_packet, "puback2a/publish2a") sock.send(puback2a_packet) broker.terminate() broker.wait() (stdo1, stde1) = broker.communicate() sock.close() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=20, port=port) mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback1b") sock.send(publish2s_packet) mosq_test.receive_unordered(sock, puback2s_packet, publish2b_packet, "puback2b/publish2b") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) exit(rc) mosquitto-2.0.18/test/broker/11-persistent-subscription-v5.py000077500000000000000000000045541450213760600241730ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("persistence true\n") f.write("persistence_file mosquitto-%d.db\n" % (port)) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 mid = 530 keepalive = 60 connect_packet = mosq_test.gen_connect( "persistent-subscription-test", keepalive=keepalive, clean_session=False, proto_ver=5, session_expiry=60 ) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=5) # session present subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 300 publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) puback_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 1 publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) (stdo1, stde1) = ("", "") try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") broker.terminate() broker.wait() (stdo1, stde1) = broker.communicate() sock.close() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=20, port=port) sock.send(publish_packet) mosq_test.receive_unordered(sock, puback_packet, publish_packet2, "puback/publish2") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) exit(rc) mosquitto-2.0.18/test/broker/11-persistent-subscription.py000077500000000000000000000053511450213760600236370ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("persistence true\n") f.write("persistence_file mosquitto-%d.db\n" % (port)) def do_test(proto_ver): port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 mid = 530 keepalive = 60 connect_packet = mosq_test.gen_connect( "persistent-subscription-test", keepalive=keepalive, clean_session=False, proto_ver=proto_ver, session_expiry=60 ) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=proto_ver) connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=proto_ver) # session present subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=proto_ver) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=proto_ver) mid = 300 publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=proto_ver) puback_packet = mosq_test.gen_puback(mid, proto_ver=proto_ver) mid = 1 publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=proto_ver) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) (stdo1, stde1) = ("", "") try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") broker.terminate() broker.wait() (stdo1, stde1) = broker.communicate() sock.close() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=20, port=port) sock.send(publish_packet) mosq_test.receive_unordered(sock, puback_packet, publish_packet2, "puback/publish2") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) if rc: print(stde.decode('utf-8')) print("proto_ver=%d" % (proto_ver)) exit(rc) do_test(proto_ver=4) do_test(proto_ver=5) exit(0) mosquitto-2.0.18/test/broker/11-pub-props.py000077500000000000000000000055741450213760600206530ustar00rootroot00000000000000#!/usr/bin/env python3 # Does a persisted PUBLISH keep its properties? from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("persistence true\n") f.write("persistence_file mosquitto-%d.db\n" % (port)) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect( "persistent-props-test", keepalive=keepalive, clean_session=True, proto_ver=5 ) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 1) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "plain/text") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "/dev/null") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "2357289375902345") props += mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "name", "value") publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5, properties=props, retain=True) puback_packet = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NO_MATCHING_SUBSCRIBERS, proto_ver=5) publish2_packet = mosq_test.gen_publish("subpub/qos1", qos=0, payload="message", proto_ver=5, properties=props, retain=True) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) (stdo1, stde1) = ("", "") try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, publish_packet, puback_packet, "puback") mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish2", publish2_packet) broker.terminate() broker.wait() (stdo1, stde1) = broker.communicate() sock.close() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") mosq_test.expect_packet(sock, "publish2", publish2_packet) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) exit(rc) mosquitto-2.0.18/test/broker/11-subscription-id.py000077500000000000000000000056451450213760600220410ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client message maintains its subscription id when persisted and restored. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("persistence true\n") f.write("persistence_file mosquitto-%d.db\n" % (port)) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect( "persistent-subscription-test", keepalive=keepalive, clean_session=False, proto_ver=5, session_expiry=60 ) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) connack_packet2 = mosq_test.gen_connack(rc=0, flags=1, proto_ver=5) # session present mid = 1 props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 53) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos1", 1, proto_ver=5, properties=props) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid = 1 props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 53) publish_packet2 = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5, properties=props) helper_connect_packet = mosq_test.gen_connect("helper", keepalive=keepalive, clean_session=True, proto_ver=5) helper_connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 helper_publish_packet = mosq_test.gen_publish("subpub/qos1", qos=1, mid=mid, payload="message", proto_ver=5) helper_puback_packet = mosq_test.gen_puback(mid, proto_ver=5) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) (stdo1, stde1) = ("", "") try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") sock.close() sock = mosq_test.do_client_connect(helper_connect_packet, helper_connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, helper_publish_packet, helper_puback_packet, "helper puback") sock.close() broker.terminate() broker.wait() (stdo1, stde1) = broker.communicate() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet2, timeout=20, port=port) mosq_test.expect_packet(sock, "publish2", publish_packet2) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) if os.path.exists('mosquitto-%d.db' % (port)): os.unlink('mosquitto-%d.db' % (port)) pass exit(rc) mosquitto-2.0.18/test/broker/12-prop-assigned-client-identifier.py000077500000000000000000000031361450213760600250640ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether sending a non zero session expiry interval in DISCONNECT after # having sent a zero session expiry interval is treated correctly in MQTT v5. from mosq_test_helper import * def do_test(clean_start): rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect(None, proto_ver=5, keepalive=keepalive, clean_session=clean_start) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_ASSIGNED_CLIENT_IDENTIFIER, "auto-00000000-0000-0000-0000-000000000000") connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 1) disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(10) sock.connect(("localhost", port)) sock.send(connect_packet) connack_recvd = sock.recv(len(connack_packet)) if connack_recvd[0:12] == connack_packet[0:12]: # FIXME - this test could be tightened up a lot rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) do_test(True) do_test(False) exit(0) mosquitto-2.0.18/test/broker/12-prop-maximum-packet-size-broker.py000077500000000000000000000025671450213760600250560ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether the broker disconnects a client nicely when they send a too large packet. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("max_packet_size 30\n") port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 30) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) publish_packet = mosq_test.gen_publish("test/topic", qos=0, payload="0123456789012345678901234567890", proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(reason_code=149, proto_ver=5) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, "disconnect") rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() os.remove(conf_file) (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/12-prop-maximum-packet-size-publish-qos1.py000077500000000000000000000032061450213760600261100ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether maximum packet size is honoured on a PUBLISH to a client # MQTTv5 from mosq_test_helper import * rc = 1 keepalive = 10 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 20) connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive, properties=props) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 1, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 1, proto_ver=5) mid=1 publish1_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=1, payload="12345678901234567890", proto_ver=5) puback1_packet = mosq_test.gen_puback(mid, proto_ver=5) mid=2 publish2_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=1, payload="7890", proto_ver=5) puback2_packet = mosq_test.gen_puback(mid, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet) mosq_test.do_send_receive(sock, publish1_packet, puback1_packet, "puback 1") # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE mosq_test.do_ping(sock) sock.send(publish2_packet) mosq_test.receive_unordered(sock, puback2_packet, publish2_packet, "puback 2/publish2") rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/12-prop-maximum-packet-size-publish-qos2.py000077500000000000000000000040161450213760600261110ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether maximum packet size is honoured on a PUBLISH to a client # MQTTv5 from mosq_test_helper import * rc = 1 keepalive = 10 props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 20) connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive, properties=props) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "test/topic", 2, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) mid=1 publish1_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=2, payload="12345678901234567890", proto_ver=5) pubrec1_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel1_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid=2 publish2_packet = mosq_test.gen_publish(topic="test/topic", mid=mid, qos=2, payload="7890", proto_ver=5) pubrec2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet) mosq_test.do_send_receive(sock, publish1_packet, pubrec1_packet, "pubrec 1") mosq_test.do_send_receive(sock, pubrel1_packet, pubcomp1_packet, "pubcomp 1") # We shouldn't receive the publish here because it is > MAXIMUM_PACKET_SIZE mosq_test.do_ping(sock) mosq_test.do_send_receive(sock, publish2_packet, pubrec2_packet, "pubrec 2") sock.send(pubrel2_packet) mosq_test.receive_unordered(sock, pubcomp2_packet, publish2_packet, "pubcomp 2/publish2") rc = 0 except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/12-prop-response-topic-correlation-data.py000077500000000000000000000045401450213760600260730ustar00rootroot00000000000000#!/usr/bin/env python3 # client 1 subscribes to normal-topic # client 2 susbscribes to response-topic # client 2 publishes message to normal-topic with response-topic property and correlation-data property # client 1 receives message, publishes a response on response-topic # client 2 receives message, checks payload from mosq_test_helper import * rc = 1 keepalive = 10 connect_packet1 = mosq_test.gen_connect("client1", proto_ver=5, keepalive=keepalive) connect_packet2 = mosq_test.gen_connect("client2", proto_ver=5, keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe_packet1 = mosq_test.gen_subscribe(mid=1, topic="normal/topic", qos=0, proto_ver=5) subscribe_packet2 = mosq_test.gen_subscribe(mid=1, topic="response/topic", qos=0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=5) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "45vyvynq30q3vt4 nuy893b4v3") publish_packet2 = mosq_test.gen_publish(topic="normal/topic", qos=0, payload="2", proto_ver=5, properties=props) publish_packet1 = mosq_test.gen_publish(topic="response/topic", qos=0, payload="22", proto_ver=5) disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet, port=port) sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet, port=port) mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet, "subscribe1") mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet, "subscribe2") sock2.send(publish_packet2) mosq_test.expect_packet(sock1, "publish1", publish_packet2) # FIXME - it would be better to extract the property and payload, even though we know them sock1.send(publish_packet1) mosq_test.expect_packet(sock2, "publish2", publish_packet1) rc = 0 sock1.close() sock2.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/12-prop-response-topic.py000077500000000000000000000043351450213760600226470ustar00rootroot00000000000000#!/usr/bin/env python3 # client 1 subscribes to normal-topic # client 2 susbscribes to response-topic # client 2 publishes message to normal-topic with response-topic property # client 1 receives message, publishes a response on response-topic # client 2 receives message, checks payload from mosq_test_helper import * rc = 1 keepalive = 10 connect_packet1 = mosq_test.gen_connect("client1", proto_ver=5, keepalive=keepalive) connect_packet2 = mosq_test.gen_connect("client2", proto_ver=5, keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe_packet1 = mosq_test.gen_subscribe(mid=1, topic="normal/topic", qos=0, proto_ver=5) subscribe_packet2 = mosq_test.gen_subscribe(mid=1, topic="response/topic", qos=0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid=1, qos=0, proto_ver=5) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "response/topic") publish_packet2 = mosq_test.gen_publish(topic="normal/topic", qos=0, payload="2", proto_ver=5, properties=props) publish_packet1 = mosq_test.gen_publish(topic="response/topic", qos=0, payload="22", proto_ver=5) disconnect_client_packet = mosq_test.gen_disconnect(proto_ver=5, properties=props) disconnect_server_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=130) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock1 = mosq_test.do_client_connect(connect_packet1, connack_packet, port=port) sock2 = mosq_test.do_client_connect(connect_packet2, connack_packet, port=port) mosq_test.do_send_receive(sock1, subscribe_packet1, suback_packet, "subscribe1") mosq_test.do_send_receive(sock2, subscribe_packet2, suback_packet, "subscribe2") sock2.send(publish_packet2) mosq_test.expect_packet(sock1, "publish1", publish_packet2) # FIXME - it would be better to extract the property and payload, even though we know them sock1.send(publish_packet1) mosq_test.expect_packet(sock2, "publish2", publish_packet1) rc = 0 sock1.close() sock2.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/12-prop-server-keepalive.py000077500000000000000000000026011450213760600231400ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether sending a non zero session expiry interval in DISCONNECT after # having sent a zero session expiry interval is treated correctly in MQTT v5. from mosq_test_helper import * def write_config(filename, port): with open(filename, 'w') as f: f.write("port %d\n" % (port)) f.write("allow_anonymous true\n") f.write("\n") f.write("max_keepalive 60\n") port = mosq_test.get_port(1) conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) rc = 1 keepalive = 61 connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_SERVER_KEEP_ALIVE, 60) \ + mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) \ + mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False) broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, use_conf=True) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) sock.close() rc = 0 except mosq_test.TestError: pass finally: os.remove(conf_file) broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/12-prop-subpub-content-type.py000077500000000000000000000010601450213760600236140ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. # Does the Content Type property get sent through? # MQTT v5 import prop_subpub_helper as helper from mosq_test_helper import * props_out = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "text") props_out = props_out+mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 1) props_in = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "text") helper.prop_subpub_helper(props_out, props_in, expect_proto_error=False) mosquitto-2.0.18/test/broker/12-prop-subpub-payload-format.py000077500000000000000000000011131450213760600241010ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. # Does the Payload Format Indicator property get sent through? # MQTT v5 import prop_subpub_helper as helper from mosq_test_helper import * props_out = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0xed) props_out = props_out+mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS, 1) props_in = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0xed) helper.prop_subpub_helper(props_out, props_in, expect_proto_error=True) mosquitto-2.0.18/test/broker/13-malformed-publish-v5.py000077500000000000000000000077631450213760600226720ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether the broker handles malformed packets correctly - PUBLISH # MQTTv5 from mosq_test_helper import * rc = 1 def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("maximum_qos 1\n") f.write("retain_available false\n") def do_test(publish_packet, reason_code, error_string): global rc rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) connack_props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) connack_props += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_RETAIN_AVAILABLE, 0) connack_props += mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20) connack_props += mqtt5_props.gen_byte_prop(mqtt5_props.PROP_MAXIMUM_QOS, 1) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=connack_props, property_helper=False) mid = 0 disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=reason_code) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, publish_packet, disconnect_packet, error_string=error_string) rc = 0 port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: # mid == 0 publish_packet = mosq_test.gen_publish(topic="test/topic", qos=1, mid=0, proto_ver=5) do_test(publish_packet, mqtt5_rc.MQTT_RC_PROTOCOL_ERROR, "mid == 0") # qos > 2 publish_packet = mosq_test.gen_publish(topic="test/topic", qos=3, mid=1, proto_ver=5) do_test(publish_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "qos > 2") # qos > maximum qos publish_packet = mosq_test.gen_publish(topic="test/topic", qos=2, mid=1, proto_ver=5) do_test(publish_packet, mqtt5_rc.MQTT_RC_QOS_NOT_SUPPORTED, "qos > maximum qos") # retain not supported publish_packet = mosq_test.gen_publish(topic="test/topic", qos=0, retain=True, proto_ver=5, payload="a") do_test(publish_packet, mqtt5_rc.MQTT_RC_RETAIN_NOT_SUPPORTED, "retain not supported") # Incorrect property props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 0) publish_packet = mosq_test.gen_publish(topic="test/topic", qos=1, mid=1, proto_ver=5, properties=props) do_test(publish_packet, mqtt5_rc.MQTT_RC_PROTOCOL_ERROR, "Incorrect property") # Truncated packet, remaining length only publish_packet = struct.pack("!BB", 48, 0) do_test(publish_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, remaining length only") # Truncated packet, empty topic publish_packet = struct.pack("!BBH", 48, 2, 0) do_test(publish_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, empty topic") # Truncated packet, with topic, no properties publish_packet = struct.pack("!BBH1s", 48, 3, 1, b"a") do_test(publish_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with topic, no properties") # Truncated packet, with topic, no mid publish_packet = struct.pack("!BBH1s", 48+2, 3, 1, b"a") do_test(publish_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with topic, no mid") # Truncated packet, with topic, with mid, no properties publish_packet = struct.pack("!BBH1sH", 48+2, 5, 1, b"a", 1) do_test(publish_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with topic, with mid, no properties") # Bad topic publish_packet = mosq_test.gen_publish(topic="#/test/topic", qos=1, mid=1, proto_ver=5) do_test(publish_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Bad topic") except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() os.remove(conf_file) if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/13-malformed-subscribe-v5.py000077500000000000000000000107421450213760600231740ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether the broker handles malformed packets correctly - SUBSCRIBE # MQTTv5 from mosq_test_helper import * rc = 1 def do_test(subscribe_packet, reason_code, error_string): global rc rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 0 disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=reason_code) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, subscribe_packet, disconnect_packet, error_string=error_string) rc = 0 port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # mid == 0 subscribe_packet = mosq_test.gen_subscribe(topic="test/topic", qos=1, mid=0, proto_ver=5) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "mid == 0") # qos > 2 subscribe_packet = mosq_test.gen_subscribe(topic="test/topic", qos=3, mid=1, proto_ver=5) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "qos > 2") # retain handling = 0x30 subscribe_packet = mosq_test.gen_subscribe(topic="test/topic", qos=0x30, mid=1, proto_ver=5) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "retain handling = 0x30") # subscription options = 0xC0 subscribe_packet = mosq_test.gen_subscribe(topic="test/topic", qos=0xC0, mid=1, proto_ver=5) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "subscription options = 0xC0") # command flags != 0x02 subscribe_packet = mosq_test.gen_subscribe(topic="test/topic", qos=1, mid=1, proto_ver=5, cmd=128) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "command flags != 0x02") # Incorrect property props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 0) subscribe_packet = mosq_test.gen_subscribe(topic="test/topic", qos=1, mid=1, proto_ver=5, properties=props) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Incorrect property") # Truncated packet, no mid subscribe_packet = struct.pack("!BB", 130, 0) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, no mid") # Truncated packet, no properties subscribe_packet = struct.pack("!BBH", 130, 2, 1) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, no properties") # Truncated packet, with properties field subscribe_packet = struct.pack("!BBHB", 130, 3, 1, 0) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with properties field") # Truncated packet, with properties field, empty topic subscribe_packet = struct.pack("!BBHBH", 130, 5, 1, 0, 0) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with properties field, empty topic") # Truncated packet, with properties field, empty topic, with qos subscribe_packet = struct.pack("!BBHBHB", 130, 6, 1, 0, 0, 1) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with properties field, empty topic, with qos") # Truncated packet, with properties field, with topic, no qos subscribe_packet = struct.pack("!BBHBH1s", 130, 6, 1, 0, 1, b"a") do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with properties field, with topic, no qos") # Truncated packet, with properties field, with 1st topic and qos ok, second topic ok, no second qos subscribe_packet = struct.pack("!BBHHH1sBH1s", 130, 10, 1, 0, 1, b"a", 0, 1, b"b") do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with properties field, with 1st topic and qos ok, second topic ok, no second qos") # Bad topic subscribe_packet = mosq_test.gen_subscribe(topic="#/test/topic", qos=1, mid=1, proto_ver=5) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Bad topic") # Subscription ID set to 0 props = mqtt5_props.gen_varint_prop(mqtt5_props.PROP_SUBSCRIPTION_IDENTIFIER, 0) subscribe_packet = mosq_test.gen_subscribe(topic="test/topic", qos=1, mid=1, proto_ver=5, properties=props) do_test(subscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Subscription ID set to 0") except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/13-malformed-unsubscribe-v5.py000077500000000000000000000053231450213760600235360ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether the broker handles malformed packets correctly - UNSUBSCRIBE # MQTTv5 from mosq_test_helper import * rc = 1 def do_test(unsubscribe_packet, reason_code, error_string): global rc rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("test", proto_ver=5, keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 0 disconnect_packet = mosq_test.gen_disconnect(proto_ver=5, reason_code=reason_code) sock = mosq_test.do_client_connect(connect_packet, connack_packet, port=port) mosq_test.do_send_receive(sock, unsubscribe_packet, disconnect_packet, error_string=error_string) rc = 0 port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: # mid == 0 unsubscribe_packet = mosq_test.gen_unsubscribe(topic="test/topic", mid=0, proto_ver=5) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "mid == 0") # command flags != 0x02 unsubscribe_packet = mosq_test.gen_unsubscribe(topic="test/topic", mid=1, proto_ver=5, cmd=160) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "command flags != 0x02") # Incorrect property props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, 0) unsubscribe_packet = mosq_test.gen_unsubscribe(topic="test/topic", mid=1, proto_ver=5, properties=props) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Incorrect property") # Truncated packet, no mid unsubscribe_packet = struct.pack("!BB", 162, 0) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, no mid") # Truncated packet, no properties unsubscribe_packet = struct.pack("!BBH", 162, 2, 1) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, no properties") # Truncated packet, with properties field, no topic unsubscribe_packet = struct.pack("!BBHH", 162, 4, 1, 0) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with properties field, no topic") # Truncated packet, with properties field, empty topic unsubscribe_packet = struct.pack("!BBHHH", 162, 5, 1, 0, 0) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Truncated packet, with properties field, empty topic") # Bad topic unsubscribe_packet = mosq_test.gen_unsubscribe(topic="#/test/topic", mid=1, proto_ver=5) do_test(unsubscribe_packet, mqtt5_rc.MQTT_RC_MALFORMED_PACKET, "Bad topic") except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-acl.py000077500000000000000000000361401450213760600207420ustar00rootroot00000000000000#!/usr/bin/env python3 # Test ACL for allow/deny. This does not consider ACL priority and the ACLs do not overlap. from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous false\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) add_client_command_with_id = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "correlationData": "2" }] } add_client_response_with_id = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} add_client_group_role_command = {"commands":[ { "command": "createGroup", "groupname": "mygroup" }, { "command": "createRole", "rolename": "myrole" }, { "command": "addGroupRole", "groupname": "mygroup", "rolename": "myrole" }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribeLiteral", "topic": "simple/topic", "allow": True }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribePattern", "topic": "single-wildcard/+/topic", "allow": True }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "subscribePattern", "topic": "multilevel-wildcard/#", "allow": True }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "unsubscribeLiteral", "topic": "simple/topic", "allow": False }, { "command": "addGroupClient", "groupname": "mygroup", "username": "user_one" } ]} add_client_group_role_response = {'responses': [ {'command': 'createGroup'}, {'command': 'createRole'}, {'command': 'addGroupRole'}, {'command': 'addRoleACL'}, {'command': 'addRoleACL'}, {'command': 'addRoleACL'}, {'command': 'addRoleACL'}, {'command': 'addGroupClient'} ]} add_publish_acl_command = {"commands":[ { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "simple/topic", "allow": True }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "single-wildcard/deny/deny", "priority":10, "allow": False }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "single-wildcard/+/+", "allow": True }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientSend", "topic": "multilevel-wildcard/topic/#", "allow": True }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "single-wildcard/bob/bob", "allow": False }, { "command": "addRoleACL", "rolename": "myrole", "acltype": "publishClientReceive", "topic": "multilevel-wildcard/topic/topic/denied", "allow": False }, ]} add_publish_acl_response = {'responses': [ {'command': 'addRoleACL'}, {'command': 'addRoleACL'}, {'command': 'addRoleACL'}, {'command': 'addRoleACL'}, {'command': 'addRoleACL'}, {'command': 'addRoleACL'} ]} delete_role_command = {"commands":[ { "command": "deleteRole", "rolename": "myrole"} ]} delete_role_response = {'responses': [{'command': 'deleteRole'}]} rc = 1 keepalive = 10 connect_packet_admin = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet_admin = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet_admin = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet_admin = mosq_test.gen_suback(mid, 1) # Success connect_packet_with_id1 = mosq_test.gen_connect("cid", keepalive=keepalive, username="user_one", password="password", proto_ver=5) connack_packet_with_id1 = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 4 subscribe_simple_packet = mosq_test.gen_subscribe(mid, "simple/topic", 0, proto_ver=5) suback_simple_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5) suback_simple_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) mid = 5 subscribe_single_packet = mosq_test.gen_subscribe(mid, "single-wildcard/bob/topic", 0, proto_ver=5) suback_single_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5) suback_single_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) mid = 6 subscribe_multi_packet = mosq_test.gen_subscribe(mid, "multilevel-wildcard/topic/topic/#", 0, proto_ver=5) suback_multi_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5) suback_multi_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) mid = 7 publish_simple_packet = mosq_test.gen_publish(mid=mid, topic="simple/topic", qos=1, payload="message", proto_ver=5) puback_simple_packet_success = mosq_test.gen_puback(mid, proto_ver=5) puback_simple_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) publish_simple_packet_r = mosq_test.gen_publish(topic="simple/topic", qos=0, payload="message", proto_ver=5) # This message is in single-wildcard/+/+ so could be allowed, but the single-wildcard/deny/deny with higher priority should override mid = 9 publish_single_packet_denied = mosq_test.gen_publish(mid=mid, topic="single-wildcard/deny/deny", qos=1, payload="message", proto_ver=5) puback_single_packet_denied_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) mid = 8 publish_single_packet = mosq_test.gen_publish(mid=mid, topic="single-wildcard/bob/topic", qos=1, payload="message", proto_ver=5) puback_single_packet_success = mosq_test.gen_puback(mid, proto_ver=5) puback_single_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) publish_single_packet_r = mosq_test.gen_publish(topic="single-wildcard/bob/topic", qos=0, payload="message", proto_ver=5) mid = 9 publish_multi_packet = mosq_test.gen_publish(mid=mid, topic="multilevel-wildcard/topic/topic/allowed", qos=1, payload="message", proto_ver=5) puback_multi_packet_success = mosq_test.gen_puback(mid, proto_ver=5) puback_multi_packet_fail = mosq_test.gen_puback(mid, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) mid = 10 publish_multi_denied_packet = mosq_test.gen_publish(mid=mid, topic="multilevel-wildcard/topic/topic/denied", qos=1, payload="message", proto_ver=5) puback_multi_denied_packet = mosq_test.gen_puback(mid, proto_ver=5) publish_multi_packet_r = mosq_test.gen_publish(topic="multilevel-wildcard/topic/topic/allowed", qos=0, payload="message", proto_ver=5) mid = 11 unsubscribe_simple_packet = mosq_test.gen_unsubscribe(mid, "simple/topic", proto_ver=5) unsuback_simple_packet_fail = mosq_test.gen_unsuback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) mid = 12 unsubscribe_single_packet = mosq_test.gen_unsubscribe(mid, "single-wildcard/bob/topic", proto_ver=5) unsuback_single_packet_success = mosq_test.gen_unsuback(mid, 0, proto_ver=5) mid = 13 unsubscribe_multi_packet = mosq_test.gen_unsubscribe(mid, "multilevel-wildcard/topic/topic/#", proto_ver=5) unsuback_multi_packet_success = mosq_test.gen_unsuback(mid, 0, proto_ver=5) disconnect_kick_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_ADMINISTRATIVE_ACTION, proto_ver=5) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, "suback") # Add client command_check(sock, add_client_command_with_id, add_client_response_with_id) # Client with username, password, and client id csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 1") # Subscribe to "simple/topic" - not allowed mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_fail, "suback simple 1") # Subscribe to "single-wildcard/bob/topic" - not allowed mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_fail, "suback single 1") # Subscribe to "multilevel-wildcard/topic/topic/topic" - not allowed mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_fail, "suback multi 1") # Publish to "simple/topic" - not allowed mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, "puback simple 1") # Publish to "single-wildcard/bob/topic" - not allowed mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, "puback single 1") # Publish to "multilevel-wildcard/topic/topic/topic" - not allowed mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, "puback multi 1") # Create a group, add a role to the group, add the client to the group # Add some subscribe/unsubscribe ACLs - this will kick the client command_check(sock, add_client_group_role_command, add_client_group_role_response) mosq_test.expect_packet(csock, "disconnect kick 1", disconnect_kick_packet) csock.close() # Reconnect csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 2") # Subscribe to "simple/topic" - this is now allowed mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_success, "suback simple 2") # Subscribe to "single-wildcard/bob/topic" - this is now allowed mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_success, "suback single 2") # Subscribe to "multilevel-wildcard/topic/topic/topic" - this is now allowed mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_success, "suback multi 2") # Publish to "simple/topic" - not allowed mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, "puback 2") # Publish to "single-wildcard/bob/topic" - not allowed mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, "puback single 2") # Publish to "multilevel-wildcard/topic/topic/topic" - not allowed mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, "puback multi 2") # Add some publish ACLs - this will kick the client command_check(sock, add_publish_acl_command, add_publish_acl_response) mosq_test.expect_packet(csock, "disconnect kick 2", disconnect_kick_packet) csock.close() # Reconnect csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 3") # Subscribe to "simple/topic" - this is now allowed mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_success, "suback simple 3") # Subscribe to "single-wildcard/bob/topic" - this is now allowed mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_success, "suback single 3") # Subscribe to "multilevel-wildcard/topic/topic/allowed" - this is now allowed mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_success, "suback multi 3") # Publish to "simple/topic" - this is now allowed csock.send(publish_simple_packet) mosq_test.receive_unordered(csock, publish_simple_packet_r, puback_simple_packet_success, "puback simple 3 / publish r") # Publish to "single-wildcard/bob/topic" - this is now allowed csock.send(publish_single_packet) mosq_test.receive_unordered(csock, publish_single_packet_r, puback_single_packet_success, "puback single 3 / publish r") # Publish to "single-wildcard/deny/deny" - this is stillnot allowed mosq_test.do_send_receive(csock, publish_single_packet_denied, puback_single_packet_denied_fail, "puback single denied 1") # Publish to "multilevel-wildcard/topic/topic/allowed" - this is now allowed csock.send(publish_multi_packet) mosq_test.receive_unordered(csock, publish_multi_packet_r, puback_multi_packet_success, "puback multi 3 / publish r") # Publish to "multilevel-wildcard/topic/topic/denied" - receiving is denied by publishClientReceive mosq_test.do_send_receive(csock, publish_multi_denied_packet, puback_multi_denied_packet, "puback multi denied") mosq_test.do_ping(csock) # Simple unsubscribe should be denied mosq_test.do_send_receive(csock, unsubscribe_simple_packet, unsuback_simple_packet_fail, "unsuback simple 1") # Single unsubscribe should be allowed mosq_test.do_send_receive(csock, unsubscribe_single_packet, unsuback_single_packet_success, "unsuback single 1") # Multi unsubscribe should be allowed mosq_test.do_send_receive(csock, unsubscribe_multi_packet, unsuback_multi_packet_success, "unsuback multi 1") # Delete the role, client should be kicked command_check(sock, delete_role_command, delete_role_response) mosq_test.expect_packet(csock, "disconnect kick 3", disconnect_kick_packet) csock.close() # Reconnect - these should all be denied again. csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="connack 4") # Subscribe to "simple/topic" - not allowed mosq_test.do_send_receive(csock, subscribe_simple_packet, suback_simple_packet_fail, "suback simple 4") # Subscribe to "single-wildcard/bob/topic" - not allowed mosq_test.do_send_receive(csock, subscribe_single_packet, suback_single_packet_fail, "suback single 4") # Subscribe to "multilevel-wildcard/topic/topic/topic" - not allowed mosq_test.do_send_receive(csock, subscribe_multi_packet, suback_multi_packet_fail, "suback multi 4") # Publish to "simple/topic" - not allowed mosq_test.do_send_receive(csock, publish_simple_packet, puback_simple_packet_fail, "puback simple 4") # Publish to "single-wildcard/bob/topic" - not allowed mosq_test.do_send_receive(csock, publish_single_packet, puback_single_packet_fail, "puback single 4") # Publish to "multilevel-wildcard/topic/topic/topic" - not allowed mosq_test.do_send_receive(csock, publish_multi_packet, puback_multi_packet_fail, "puback multi 4") csock.close() rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) publishClientSend publishClientReceive subscribeLiteral subscribePattern mosquitto-2.0.18/test/broker/14-dynsec-anon-group.py000077500000000000000000000140301450213760600222620ustar00rootroot00000000000000#!/usr/bin/env python3 # Test the anonymous group support by adding a group, setting the anon group, adding a role to the group and checking a subscription. from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) get_anon_group_none_command = { "commands": [{ "command": "getAnonymousGroup", "correlationData": "2" }] } get_anon_group_none_response = {'responses': [{'command': 'getAnonymousGroup', 'data': {'group': {'groupname': ''}}, 'correlationData': '2'}]} create_group_set_anon_command = { "commands": [ { "command": "createGroup", "groupname": "anon-clients", "correlationData": "3" }, { "command": "setAnonymousGroup", "groupname": "anon-clients", "correlationData": "4" } ] } create_group_set_anon_response = {'responses': [ {'command': 'createGroup', 'correlationData': '3'}, {'command': 'setAnonymousGroup', 'correlationData': '4'}, ]} get_anon_group_command = { "commands": [{ "command": "getAnonymousGroup", "correlationData": "3" }] } get_anon_group_response = {'responses': [{'command': 'getAnonymousGroup', 'data': {'group': {'groupname': 'anon-clients'}}, 'correlationData': '3'}]} create_role_apply_command = { "commands": [ { "command": "createRole", "rolename": "anon", "correlationData": "4" }, { "command": "addRoleACL", "rolename": "anon", "acltype": "subscribeLiteral", "topic": "anon/topic", "allow": True, "correlationData": "5" }, { "command": "addGroupRole", "groupname": "anon-clients", "rolename": "anon", "correlationData": "6"} ] } create_role_apply_response = {'responses': [ {'command': 'createRole', 'correlationData': '4'}, {'command': 'addRoleACL', 'correlationData': '5'}, {'command': 'addGroupRole', 'correlationData': '6'} ]} delete_anon_group_command = { "commands": [ { "command": "deleteGroup", "groupname": "anon-clients", "correlationData": "40" } ] } delete_anon_group_response = {'responses': [ {'command': 'deleteGroup', "error":'Deleting the anonymous group is forbidden', 'correlationData': '40'} ]} rc = 1 keepalive = 10 # Admin connect_packet_admin = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet_admin = mosq_test.gen_connack(rc=0) mid = 1 subscribe_packet_admin = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet_admin = mosq_test.gen_suback(mid, 1) # Client connect_packet = mosq_test.gen_connect("cid", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "anon/topic", qos=1, proto_ver=5) suback_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) suback_packet_success = mosq_test.gen_suback(mid, 1, proto_ver=5) disconnect_packet_kick = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_ADMINISTRATIVE_ACTION, proto_ver=5) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, "suback admin") # Add client command_check(sock, get_anon_group_none_command, get_anon_group_none_response) # Client is anon, there is no anon group, so subscribe should fail csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, "suback 1") # Add group, and set to anon command_check(sock, create_group_set_anon_command, create_group_set_anon_response) command_check(sock, get_anon_group_command, get_anon_group_response) # Anon group is changed, so we are kicked mosq_test.expect_packet(csock, "disconnect 1", disconnect_packet_kick) csock.close() # Reconnect, subscribe should still fail csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, "suback 2") # Add role with subscribe ACL, and apply to anon group command_check(sock, create_role_apply_command, create_role_apply_response) # Anon group is changed, so we are kicked mosq_test.expect_packet(csock, "disconnect 2", disconnect_packet_kick) csock.close() # Reconnect, subscribe should now succeed csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, "suback 3") # Try to delete anon group, this should fail command_check(sock, delete_anon_group_command, delete_anon_group_response) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-auth.py000077500000000000000000000207071450213760600211460ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous false\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) add_client_command_with_id = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "correlationData": "2" }] } add_client_response_with_id = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} add_client_command_without_id = { "commands": [{ "command": "createClient", "username": "user_two", "password": "asdfgh", "correlationData": "3" }] } add_client_response_without_id = {'responses': [{'command': 'createClient', 'correlationData': '3'}]} set_client_id_command = { "commands": [{ "command": "setClientId", "username": "user_two", "clientid": "new-cid", "correlationData": "5" }] } set_client_id_response = {'responses': [{'command': 'setClientId', 'correlationData': '5'}]} # No password defined, this client should never be able to connect. add_client_command_without_pw = { "commands": [{ "command": "createClient", "username": "user_three", "correlationData": "4" }] } add_client_response_without_pw = {'responses': [{'command': 'createClient', 'correlationData': '4'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) # Success connect_packet_with_id1 = mosq_test.gen_connect("cid", keepalive=keepalive, username="user_one", password="password", proto_ver=5) connack_packet_with_id1 = mosq_test.gen_connack(rc=0, proto_ver=5) # Fail - bad client id connect_packet_with_id2 = mosq_test.gen_connect("bad-cid", keepalive=keepalive, username="user_one", password="password", proto_ver=5) connack_packet_with_id2 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Fail - bad password connect_packet_with_id3 = mosq_test.gen_connect("cid", keepalive=keepalive, username="user_one", password="ttt", proto_ver=5) connack_packet_with_id3 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Fail - no password connect_packet_with_id4 = mosq_test.gen_connect("cid", keepalive=keepalive, username="user_one", proto_ver=5) connack_packet_with_id4 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Success connect_packet_without_id1 = mosq_test.gen_connect("no-cid", keepalive=keepalive, username="user_two", password="asdfgh", proto_ver=5) connack_packet_without_id1 = mosq_test.gen_connack(rc=0, proto_ver=5) # Fail - bad password connect_packet_without_id2 = mosq_test.gen_connect("no-cid", keepalive=keepalive, username="user_two", password="pass", proto_ver=5) connack_packet_without_id2 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Fail - no password connect_packet_without_id3 = mosq_test.gen_connect("no-cid", keepalive=keepalive, username="user_two", proto_ver=5) connack_packet_without_id3 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Success connect_packet_set_id1 = mosq_test.gen_connect("new-cid", keepalive=keepalive, username="user_two", password="asdfgh", proto_ver=5) connack_packet_set_id1 = mosq_test.gen_connack(rc=0, proto_ver=5) # Fail - bad client id connect_packet_set_id2 = mosq_test.gen_connect("bad-cid", keepalive=keepalive, username="user_two", password="asdfgh", proto_ver=5) connack_packet_set_id2 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Fail - bad password connect_packet_without_pw1 = mosq_test.gen_connect("cid2", keepalive=keepalive, username="user_three", password="pass", proto_ver=5) connack_packet_without_pw1 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Fail - no password connect_packet_without_pw2 = mosq_test.gen_connect("cid2", keepalive=keepalive, username="user_three", proto_ver=5) connack_packet_without_pw2 = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) # Fail - no username connect_packet_without_un = mosq_test.gen_connect("cid3", keepalive=keepalive, proto_ver=5) connack_packet_without_un = mosq_test.gen_connack(rc=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5, property_helper=False) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Add client command_check(sock, add_client_command_with_id, add_client_response_with_id) command_check(sock, add_client_command_without_id, add_client_response_without_id) command_check(sock, add_client_command_without_pw, add_client_response_without_pw) # Client with username, password, and client id csock = mosq_test.do_client_connect(connect_packet_with_id1, connack_packet_with_id1, timeout=5, port=port, connack_error="with id 1") csock.close() csock = mosq_test.do_client_connect(connect_packet_with_id2, connack_packet_with_id2, timeout=5, port=port, connack_error="with id 2") csock.close() csock = mosq_test.do_client_connect(connect_packet_with_id3, connack_packet_with_id3, timeout=5, port=port, connack_error="with id 3") csock.close() csock = mosq_test.do_client_connect(connect_packet_with_id4, connack_packet_with_id4, timeout=5, port=port, connack_error="with id 4") csock.close() # Client with just username and password csock = mosq_test.do_client_connect(connect_packet_without_id1, connack_packet_without_id1, timeout=5, port=port, connack_error="without id 1") csock.close() csock = mosq_test.do_client_connect(connect_packet_without_id2, connack_packet_without_id2, timeout=5, port=port, connack_error="without id 2") csock.close() csock = mosq_test.do_client_connect(connect_packet_without_id3, connack_packet_without_id3, timeout=5, port=port, connack_error="without id 3") csock.close() # Client with no password set csock = mosq_test.do_client_connect(connect_packet_without_pw1, connack_packet_without_pw1, timeout=5, port=port, connack_error="without pw 1") csock.close() csock = mosq_test.do_client_connect(connect_packet_without_pw2, connack_packet_without_pw2, timeout=5, port=port, connack_error="without pw 2") csock.close() # Add client id to "user_two" command_check(sock, set_client_id_command, set_client_id_response) csock = mosq_test.do_client_connect(connect_packet_set_id1, connack_packet_set_id1, timeout=5, port=port, connack_error="set id 1") csock.close() csock = mosq_test.do_client_connect(connect_packet_set_id2, connack_packet_set_id2, timeout=5, port=port, connack_error="set id 2") csock.close() # No username, anon disabled csock = mosq_test.do_client_connect(connect_packet_without_un, connack_packet_without_un, timeout=5, port=port, connack_error="without username") csock.close() rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-client-invalid.py000077500000000000000000000567131450213760600231150ustar00rootroot00000000000000#!/usr/bin/env python3 # Check invalid inputs for client commands from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) if msg != "": print(msg) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) # ========================================================================== # Create client # ========================================================================== # No username create_client1_command = { 'commands': [{'command': 'createClient' }] } create_client1_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing username'}]} # Username not a string create_client2_command = { 'commands': [{'command': 'createClient', 'username': 5 }] } create_client2_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 create_client3_command = { 'commands': [{'command': 'createClient', 'username': '￿LO' }] } create_client3_response = {'responses': [{'command': 'createClient', 'error': 'Username not valid UTF-8'}]} # Password not a string create_client4_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':5 }] } create_client4_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing password'}]} # Client id not a string create_client5_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'clientid':5}] } create_client5_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing client id'}]} # Client id not UTF-8 create_client6_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'clientid':'￿LO' }] } create_client6_response = {'responses': [{'command': 'createClient', 'error': 'Client ID not valid UTF-8'}]} # Text name not a string create_client7_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'textname':5}] } create_client7_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing textname'}]} # Text description not a string create_client8_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'textdescription':5}] } create_client8_response = {'responses': [{'command': 'createClient', 'error': 'Invalid/missing textdescription'}]} # Client already exists create_client9_command = { 'commands': [{'command': 'createClient', 'username': 'admin', 'password':'5'}]} create_client9_response = {'responses': [{'command': 'createClient', 'error': 'Client already exists'}]} # Roles not an array create_client10_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'roles':'bad'}] } create_client10_response = {'responses': [{'command': 'createClient', 'error': "'roles' not an array or missing/invalid rolename"}]} # Role not found create_client11_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'roles':[{'rolename':'notfound'}]}] } create_client11_response = {'responses': [{'command': 'createClient', 'error': 'Role not found'}]} # Group not found create_client12_command = { 'commands': [{'command': 'createClient', 'username': 'user', 'password':'5', 'groups':[{'groupname':'notfound'}]}] } create_client12_response = {'responses': [{'command': 'createClient', 'error': 'Group not found'}]} # ========================================================================== # Delete client # ========================================================================== # No username delete_client1_command = { 'commands': [{'command': 'deleteClient'}]} delete_client1_response = {'responses': [{'command': 'deleteClient', 'error': 'Invalid/missing username'}]} # Username not a string delete_client2_command = { 'commands': [{'command': 'deleteClient', 'username':5}]} delete_client2_response = {'responses': [{'command': 'deleteClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 delete_client3_command = { 'commands': [{'command': 'deleteClient', 'username': '￿LO' }] } delete_client3_response = {'responses': [{'command': 'deleteClient', 'error': 'Username not valid UTF-8'}]} # Client not found delete_client4_command = { 'commands': [{'command': 'deleteClient', 'username':'notfound'}]} delete_client4_response = {'responses': [{'command': 'deleteClient', 'error': 'Client not found'}]} # ========================================================================== # Disable client # ========================================================================== # No username disable_client1_command = { 'commands': [{'command': 'disableClient'}]} disable_client1_response = {'responses': [{'command': 'disableClient', 'error': 'Invalid/missing username'}]} # Username not a string disable_client2_command = { 'commands': [{'command': 'disableClient', 'username':5}]} disable_client2_response = {'responses': [{'command': 'disableClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 disable_client3_command = { 'commands': [{'command': 'disableClient', 'username': '￿LO' }] } disable_client3_response = {'responses': [{'command': 'disableClient', 'error': 'Username not valid UTF-8'}]} # Client not found disable_client4_command = { 'commands': [{'command': 'disableClient', 'username':'notfound'}]} disable_client4_response = {'responses': [{'command': 'disableClient', 'error': 'Client not found'}]} # ========================================================================== # Enable client # ========================================================================== # No username enable_client1_command = { 'commands': [{'command': 'enableClient'}]} enable_client1_response = {'responses': [{'command': 'enableClient', 'error': 'Invalid/missing username'}]} # Username not a string enable_client2_command = { 'commands': [{'command': 'enableClient', 'username':5}]} enable_client2_response = {'responses': [{'command': 'enableClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 enable_client3_command = { 'commands': [{'command': 'enableClient', 'username': '￿LO' }] } enable_client3_response = {'responses': [{'command': 'enableClient', 'error': 'Username not valid UTF-8'}]} # Client not found enable_client4_command = { 'commands': [{'command': 'enableClient', 'username':'notfound'}]} enable_client4_response = {'responses': [{'command': 'enableClient', 'error': 'Client not found'}]} # ========================================================================== # Set client id # ========================================================================== # No username set_client_id1_command = { 'commands': [{'command': 'setClientId'}]} set_client_id1_response = {'responses': [{'command': 'setClientId', 'error': 'Invalid/missing username'}]} # Username not a string set_client_id2_command = { 'commands': [{'command': 'setClientId', 'username':5}]} set_client_id2_response = {'responses': [{'command': 'setClientId', 'error': 'Invalid/missing username'}]} # Username not UTF-8 set_client_id3_command = { 'commands': [{'command': 'setClientId', 'username': '￿LO' }] } set_client_id3_response = {'responses': [{'command': 'setClientId', 'error': 'Username not valid UTF-8'}]} # No client id set_client_id4_command = { 'commands': [{'command': 'setClientId', 'username':'user'}]} set_client_id4_response = {'responses': [{'command': 'setClientId', 'error': 'Client not found'}]} # Client id not a string set_client_id5_command = { 'commands': [{'command': 'setClientId', 'username':'user', 'clientid':5}]} set_client_id5_response = {'responses': [{'command': 'setClientId', 'error': 'Invalid/missing client ID'}]} # Client id not UTF-8 set_client_id6_command = { 'commands': [{'command': 'setClientId', 'username':'user', 'clientid': '￿LO' }] } set_client_id6_response = {'responses': [{'command': 'setClientId', 'error': 'Client ID not valid UTF-8'}]} # Client not found set_client_id7_command = { 'commands': [{'command': 'setClientId', 'username':'notfound', 'clientid':'newid'}]} set_client_id7_response = {'responses': [{'command': 'setClientId', 'error': 'Client not found'}]} # ========================================================================== # Set password # ========================================================================== # No username set_password1_command = { 'commands': [{'command': 'setClientPassword'}]} set_password1_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing username'}]} # Username not a string set_password2_command = { 'commands': [{'command': 'setClientPassword', 'username':5}]} set_password2_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing username'}]} # Username not UTF-8 set_password3_command = { 'commands': [{'command': 'setClientPassword', 'username':'￿LO' }] } set_password3_response = {'responses': [{'command': 'setClientPassword', 'error': 'Username not valid UTF-8'}]} # No password set_password4_command = { 'commands': [{'command': 'setClientPassword', 'username':'user'}]} set_password4_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing password'}]} # password not a string set_password5_command = { 'commands': [{'command': 'setClientPassword', 'username':'user', 'password':5}]} set_password5_response = {'responses': [{'command': 'setClientPassword', 'error': 'Invalid/missing password'}]} # password is empty set_password6_command = { 'commands': [{'command': 'setClientPassword', 'username':'user', 'password':''}]} set_password6_response = {'responses': [{'command': 'setClientPassword', 'error': 'Empty password is not allowed'}]} # Client not found set_password7_command = { 'commands': [{'command': 'setClientPassword', 'username':'notfound', 'password':'newpw'}]} set_password7_response = {'responses': [{'command': 'setClientPassword', 'error': 'Client not found'}]} # ========================================================================== # Get client # ========================================================================== # No username get_client1_command = { 'commands': [{'command': 'getClient'}]} get_client1_response = {'responses': [{'command': 'getClient', 'error': 'Invalid/missing username'}]} # Username not a string get_client2_command = { 'commands': [{'command': 'getClient', 'username':5}]} get_client2_response = {'responses': [{'command': 'getClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 get_client3_command = { 'commands': [{'command': 'getClient', 'username':'￿LO' }] } get_client3_response = {'responses': [{'command': 'getClient', 'error': 'Username not valid UTF-8'}]} # Client not found get_client4_command = { 'commands': [{'command': 'getClient', 'username':'notfound'}]} get_client4_response = {'responses': [{'command': 'getClient', 'error': 'Client not found'}]} # ========================================================================== # Add role # ========================================================================== # No username add_role1_command = { 'commands': [{'command': 'addClientRole'}]} add_role1_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing username'}]} # Username not a string add_role2_command = { 'commands': [{'command': 'addClientRole', 'username':5}]} add_role2_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing username'}]} # Username not UTF-8 add_role3_command = { 'commands': [{'command': 'addClientRole', 'username':'￿LO' }] } add_role3_response = {'responses': [{'command': 'addClientRole', 'error': 'Username not valid UTF-8'}]} # No rolename add_role4_command = { 'commands': [{'command': 'addClientRole', 'username':'user'}]} add_role4_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing rolename'}]} # rolename not a string add_role5_command = { 'commands': [{'command': 'addClientRole', 'username':'user', 'rolename':5}]} add_role5_response = {'responses': [{'command': 'addClientRole', 'error': 'Invalid/missing rolename'}]} # rolename not UTF-8 add_role6_command = { 'commands': [{'command': 'addClientRole', 'username':'user', 'rolename':'￿LO' }] } add_role6_response = {'responses': [{'command': 'addClientRole', 'error': 'Role name not valid UTF-8'}]} # Client not found add_role7_command = { 'commands': [{'command': 'addClientRole', 'username':'notfound', 'rolename':'notfound'}]} add_role7_response = {'responses': [{'command': 'addClientRole', 'error': 'Client not found'}]} # Role not found add_role8_command = { 'commands': [{'command': 'addClientRole', 'username':'admin', 'rolename':'notfound'}]} add_role8_response = {'responses': [{'command': 'addClientRole', 'error': 'Role not found'}]} # ========================================================================== # Remove role # ========================================================================== # No username remove_role1_command = { 'commands': [{'command': 'removeClientRole'}]} remove_role1_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing username'}]} # Username not a string remove_role2_command = { 'commands': [{'command': 'removeClientRole', 'username':5}]} remove_role2_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing username'}]} # Username not UTF-8 remove_role3_command = { 'commands': [{'command': 'removeClientRole', 'username':'￿LO' }] } remove_role3_response = {'responses': [{'command': 'removeClientRole', 'error': 'Username not valid UTF-8'}]} # No rolename remove_role4_command = { 'commands': [{'command': 'removeClientRole', 'username':'user'}]} remove_role4_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing rolename'}]} # rolename not a string remove_role5_command = { 'commands': [{'command': 'removeClientRole', 'username':'user', 'rolename':5}]} remove_role5_response = {'responses': [{'command': 'removeClientRole', 'error': 'Invalid/missing rolename'}]} # rolename not UTF-8 remove_role6_command = { 'commands': [{'command': 'removeClientRole', 'username':'user', 'rolename':'￿LO' }] } remove_role6_response = {'responses': [{'command': 'removeClientRole', 'error': 'Role name not valid UTF-8'}]} # Client not found remove_role7_command = { 'commands': [{'command': 'removeClientRole', 'username':'notfound', 'rolename':'notfound'}]} remove_role7_response = {'responses': [{'command': 'removeClientRole', 'error': 'Client not found'}]} # Role not found remove_role8_command = { 'commands': [{'command': 'removeClientRole', 'username':'admin', 'rolename':'notfound'}]} remove_role8_response = {'responses': [{'command': 'removeClientRole', 'error': 'Role not found'}]} # ========================================================================== # Modify client # ========================================================================== # Create a client to modify modify_client0_command = { 'commands': [{'command': 'createClient', 'username':'user'}]} modify_client0_response = {'responses': [{'command': 'createClient'}]} # No username modify_client1_command = { 'commands': [{'command': 'modifyClient'}]} modify_client1_response = {'responses': [{'command': 'modifyClient', 'error': 'Invalid/missing username'}]} # Username not a string modify_client2_command = { 'commands': [{'command': 'modifyClient', 'username':5}]} modify_client2_response = {'responses': [{'command': 'modifyClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 modify_client3_command = { 'commands': [{'command': 'modifyClient', 'username':'￿LO' }] } modify_client3_response = {'responses': [{'command': 'modifyClient', 'error': 'Username not valid UTF-8'}]} # roles not a list modify_client4_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'password':'test', 'roles':'string'}]} modify_client4_response = {'responses': [{'command': 'modifyClient', 'error': "'roles' not an array or missing/invalid rolename"}]} # No rolename modify_client5_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'roles':[{'rolename':5}]}]} modify_client5_response = {'responses': [{'command': 'modifyClient', 'error': "'roles' not an array or missing/invalid rolename"}]} # rolename not UTF-8 #modify_client6_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'rolename':'￿LO' }] } #modify_client6_response = {'responses': [{'command': 'modifyClient', 'error': 'Username not valid UTF-8'}]} # Client not found modify_client7_command = { 'commands': [{'command': 'modifyClient', 'username':'notfound', 'rolename':'notfound'}]} modify_client7_response = {'responses': [{'command': 'modifyClient', 'error': 'Client not found'}]} # Role not found modify_client8_command = { 'commands': [{'command': 'modifyClient', 'username':'user', 'roles':[{'rolename':'notfound'}]}]} modify_client8_response = {'responses': [{'command': 'modifyClient', 'error': 'Role not found'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") command_check(sock, create_client1_command, create_client1_response, "1") command_check(sock, create_client2_command, create_client2_response, "2") command_check(sock, create_client3_command, create_client3_response, "3") command_check(sock, create_client4_command, create_client4_response, "4") command_check(sock, create_client5_command, create_client5_response, "5") command_check(sock, create_client6_command, create_client6_response, "6") command_check(sock, create_client7_command, create_client7_response, "7") command_check(sock, create_client8_command, create_client8_response, "8") command_check(sock, create_client9_command, create_client9_response, "9") command_check(sock, create_client10_command, create_client10_response, "10") command_check(sock, create_client11_command, create_client11_response, "11") command_check(sock, create_client12_command, create_client12_response, "12") command_check(sock, delete_client1_command, delete_client1_response, "1") command_check(sock, delete_client2_command, delete_client2_response, "2") #command_check(sock, delete_client3_command, delete_client3_response, "3") command_check(sock, delete_client4_command, delete_client4_response, "4") command_check(sock, disable_client1_command, disable_client1_response, "1") command_check(sock, disable_client2_command, disable_client2_response, "2") command_check(sock, disable_client3_command, disable_client3_response, "3") command_check(sock, disable_client4_command, disable_client4_response, "4") command_check(sock, enable_client1_command, enable_client1_response, "1") command_check(sock, enable_client2_command, enable_client2_response, "2") command_check(sock, enable_client3_command, enable_client3_response, "3") command_check(sock, enable_client4_command, enable_client4_response, "4") command_check(sock, set_client_id1_command, set_client_id1_response, "1") command_check(sock, set_client_id2_command, set_client_id2_response, "2") command_check(sock, set_client_id3_command, set_client_id3_response, "3") command_check(sock, set_client_id4_command, set_client_id4_response, "4") command_check(sock, set_client_id5_command, set_client_id5_response, "5") command_check(sock, set_client_id6_command, set_client_id6_response, "6") command_check(sock, set_client_id7_command, set_client_id7_response, "7") command_check(sock, set_password1_command, set_password1_response, "1") command_check(sock, set_password2_command, set_password2_response, "2") command_check(sock, set_password3_command, set_password3_response, "3") command_check(sock, set_password4_command, set_password4_response, "4") command_check(sock, set_password5_command, set_password5_response, "5") command_check(sock, set_password6_command, set_password6_response, "6") command_check(sock, set_password7_command, set_password7_response, "7") command_check(sock, get_client1_command, get_client1_response, "1") command_check(sock, get_client2_command, get_client2_response, "2") command_check(sock, get_client3_command, get_client3_response, "3") command_check(sock, get_client4_command, get_client4_response, "4") command_check(sock, add_role1_command, add_role1_response, "1") command_check(sock, add_role2_command, add_role2_response, "2") command_check(sock, add_role3_command, add_role3_response, "3") command_check(sock, add_role4_command, add_role4_response, "4") command_check(sock, add_role5_command, add_role5_response, "5") command_check(sock, add_role6_command, add_role6_response, "6") command_check(sock, add_role7_command, add_role7_response, "7") command_check(sock, add_role8_command, add_role8_response, "8") command_check(sock, remove_role1_command, remove_role1_response, "1") command_check(sock, remove_role2_command, remove_role2_response, "2") command_check(sock, remove_role3_command, remove_role3_response, "3") command_check(sock, remove_role4_command, remove_role4_response, "4") command_check(sock, remove_role5_command, remove_role5_response, "5") command_check(sock, remove_role6_command, remove_role6_response, "6") command_check(sock, remove_role7_command, remove_role7_response, "7") command_check(sock, remove_role8_command, remove_role8_response, "8") command_check(sock, modify_client0_command, modify_client0_response, "1") command_check(sock, modify_client1_command, modify_client1_response, "1") command_check(sock, modify_client2_command, modify_client2_response, "2") command_check(sock, modify_client3_command, modify_client3_response, "3") command_check(sock, modify_client4_command, modify_client4_response, "4") command_check(sock, modify_client5_command, modify_client5_response, "5") #command_check(sock, modify_client6_command, modify_client6_response, "6") command_check(sock, modify_client7_command, modify_client7_response, "7") command_check(sock, modify_client8_command, modify_client8_response, "8") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-client.py000077500000000000000000000123351450213760600214610ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) add_client_command = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "textname": "Name", "textdescription": "Description", "rolename": "", "correlationData": "2" }] } add_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} add_client_repeat_response = {'responses':[{"command":"createClient","error":"Client already exists", "correlationData":"2"}]} list_clients_command = { "commands": [{ "command": "listClients", "verbose": False, "correlationData": "10"}] } list_clients_response = {'responses': [{"command": "listClients", "data":{"totalCount":2, "clients":["admin", "user_one"]},"correlationData":"10"}]} list_clients_verbose_command = { "commands": [{ "command": "listClients", "verbose": True, "correlationData": "20"}] } list_clients_verbose_response = {'responses':[{"command": "listClients", "data":{"totalCount":2, "clients":[ {'username': 'admin', 'textname': 'Dynsec admin user', 'roles': [{'rolename': 'admin'}], 'groups': []}, {"username":"user_one", "clientid":"cid", "textname":"Name", "textdescription":"Description", "roles":[], "groups":[]}]}, "correlationData":"20"}]} get_client_command = { "commands": [{ "command": "getClient", "username": "user_one", "correlationData": "42"}]} get_client_response = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid', 'textname': 'Name', 'textdescription': 'Description', 'groups': [], 'roles': []}}, "correlationData":"42"}]} set_client_password_command = {"commands": [{ "command": "setClientPassword", "username": "user_one", "password": "password"}]} set_client_password_response = {"responses": [{"command":"setClientPassword"}]} delete_client_command = { "commands": [{ "command": "deleteClient", "username": "user_one"}]} delete_client_response = {'responses':[{'command': 'deleteClient'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Add client command_check(sock, add_client_command, add_client_response) # List clients non-verbose command_check(sock, list_clients_command, list_clients_response) # List clients verbose command_check(sock, list_clients_verbose_command, list_clients_verbose_response) # Kill broker and restart, checking whether our changes were saved. broker.terminate() broker.wait() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Get client command_check(sock, get_client_command, get_client_response) # List clients non-verbose command_check(sock, list_clients_command, list_clients_response) # List clients verbose command_check(sock, list_clients_verbose_command, list_clients_verbose_response) # Add duplicate client command_check(sock, add_client_command, add_client_repeat_response) # Set client password command_check(sock, set_client_password_command, set_client_password_response) # Delete client command_check(sock, delete_client_command, delete_client_response) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-default-access.py000077500000000000000000000167001450213760600230660ustar00rootroot00000000000000#!/usr/bin/env python3 # This tests the default ACL type access behaviour for when no ACL matches. from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous false\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print("Expected: %s" % (expected_response)) print("Received: %s" % (response)) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) add_client_command = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "correlationData": "2" }] } add_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} get_access_command = { "commands": [{"command": "getDefaultACLAccess", "correlationData": "3" }]} get_access_response = {'responses': [ { "command": "getDefaultACLAccess", 'data': {'acls': [ {'acltype': 'publishClientSend', 'allow': False}, {'acltype': 'publishClientReceive', 'allow': True}, {'acltype': 'subscribe', 'allow': False}, {'acltype': 'unsubscribe', 'allow': True} ]}, "correlationData": "3" }] } allow_subscribe_command = { "commands": [ { "command": "setDefaultACLAccess", "acls":[ { "acltype": "subscribe", "allow": True } ], "correlationData": "4" } ] } allow_subscribe_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '4'}]} allow_publish_send_command = { "commands": [ { "command": "setDefaultACLAccess", "acls":[ { "acltype": "publishClientSend", "allow": True } ], "correlationData": "5" } ] } allow_publish_send_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '5'}]} allow_publish_recv_command = { "commands": [ { "command": "setDefaultACLAccess", "acls":[ { "acltype": "publishClientReceive", "allow": False } ], "correlationData": "6" } ] } allow_publish_recv_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '6'}]} allow_unsubscribe_command = { "commands": [ { "command": "setDefaultACLAccess", "acls":[ { "acltype": "unsubscribe", "allow": False } ], "correlationData": "7" } ] } allow_unsubscribe_response = {'responses': [{'command': 'setDefaultACLAccess', 'correlationData': '7'}]} rc = 1 keepalive = 10 connect_packet_admin = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet_admin = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet_admin = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet_admin = mosq_test.gen_suback(mid, 1) connect_packet = mosq_test.gen_connect("cid", keepalive=keepalive, username="user_one", password="password", proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 3 subscribe_packet = mosq_test.gen_subscribe(mid, "topic", 0, proto_ver=5) suback_packet_fail = mosq_test.gen_suback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) suback_packet_success = mosq_test.gen_suback(mid, 0, proto_ver=5) mid = 4 unsubscribe_packet = mosq_test.gen_unsubscribe(mid, "topic", proto_ver=5) unsuback_packet_fail = mosq_test.gen_unsuback(mid, mqtt5_rc.MQTT_RC_NOT_AUTHORIZED, proto_ver=5) unsuback_packet_success = mosq_test.gen_unsuback(mid, proto_ver=5) mid = 5 publish_packet = mosq_test.gen_publish(topic="topic", mid=mid, qos=1, payload="message", proto_ver=5) puback_packet_fail = mosq_test.gen_puback(mid, proto_ver=5, reason_code=mqtt5_rc.MQTT_RC_NOT_AUTHORIZED) puback_packet_success = mosq_test.gen_puback(mid, proto_ver=5) publish_packet_recv = mosq_test.gen_publish(topic="topic", qos=0, payload="message", proto_ver=5) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet_admin, connack_packet_admin, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet_admin, suback_packet_admin, "admin suback") # Add client command_check(sock, add_client_command, add_client_response) command_check(sock, get_access_command, get_access_response) csock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) # Subscribe should fail because default access is deny mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_fail, "suback fail") # Set default subscribe access to allow command_check(sock, allow_subscribe_command, allow_subscribe_response) # Subscribe should succeed because default access is now allowed mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, "suback success") # Publish should fail because publishClientSend default is denied mosq_test.do_send_receive(csock, publish_packet, puback_packet_fail, "puback fail") # Set default publish send access to allow command_check(sock, allow_publish_send_command, allow_publish_send_response) # Publish should now succeed because publishClientSend default is allow # We also receive the message because publishClientReceive default is allow. csock.send(publish_packet) mosq_test.receive_unordered(csock, puback_packet_success, publish_packet_recv, "puback success / publish recv") # Set default publish receive access to deny command_check(sock, allow_publish_recv_command, allow_publish_recv_response) # Publish should succeed because publishClientSend default is allow # We should *not* receive the publish because it has been disabled. mosq_test.do_send_receive(csock, publish_packet, puback_packet_success, "puback success") mosq_test.do_ping(csock) # Unsubscribe should succeed because default access is allowed mosq_test.do_send_receive(csock, unsubscribe_packet, unsuback_packet_success, "unsuback success") # Set default unsubscribe access to allow command_check(sock, allow_unsubscribe_command, allow_unsubscribe_response) # Subscribe should succeed because default access is allowed mosq_test.do_send_receive(csock, subscribe_packet, suback_packet_success, "suback success 2") # Unsubscribe should fail because default access is no longer allowed mosq_test.do_send_receive(csock, unsubscribe_packet, unsuback_packet_fail, "unsuback fail") csock.close() rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-disable-client.py000077500000000000000000000107011450213760600230550ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) add_client_command = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "textname": "Name", "textdescription": "Description", "rolename": "", "correlationData": "2" }] } add_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} add_client_repeat_response = {'responses':[{"command":"createClient","error":"Client already exists", "correlationData":"2"}]} get_client_command = { "commands": [{ "command": "getClient", "username": "user_one"}]} get_client_response1 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid', 'textname': 'Name', 'textdescription': 'Description', 'groups': [], 'roles': []}}}]} get_client_response2 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid', 'textname': 'Name', 'textdescription': 'Description', 'disabled':True, 'groups': [], 'roles': []}}}]} disable_client_command = { "commands": [{ "command": "disableClient", "username": "user_one"}]} disable_client_response = {'responses':[{'command': 'disableClient'}]} enable_client_command = { "commands": [{ "command": "enableClient", "username": "user_one"}]} enable_client_response = {'responses':[{'command': 'enableClient'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) client_connect_packet = mosq_test.gen_connect("cid", keepalive=keepalive, username="user_one", password="password") client_connack_packet1 = mosq_test.gen_connack(rc=5) client_connack_packet2 = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Add client command_check(sock, add_client_command, add_client_response) # Get client command_check(sock, get_client_command, get_client_response1) # Disable client command_check(sock, disable_client_command, disable_client_response) # Get client - should be disabled command_check(sock, get_client_command, get_client_response2) # Try to log in - should fail client_sock = mosq_test.do_client_connect(client_connect_packet, client_connack_packet1, timeout=5, port=port) # Enable client command_check(sock, enable_client_command, enable_client_response) # Get client - should be enabled command_check(sock, get_client_command, get_client_response1) # Try to log in - should succeed client_sock = mosq_test.do_client_connect(client_connect_packet, client_connack_packet2, timeout=5, port=port) client_sock.close() rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-group-invalid.py000077500000000000000000000540451450213760600227670ustar00rootroot00000000000000#!/usr/bin/env python3 # Check invalid inputs for group commands from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) if msg != "": print(msg) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) # Create client for modifying create_client0_command = { 'commands': [{'command': 'createClient', 'username':'validclient' }] } create_client0_response = {'responses': [{'command': 'createClient'}]} # Create group for modifying create_group0_command = { 'commands': [{'command': 'createGroup', 'groupname':'validgroup' }] } create_group0_response = {'responses': [{'command': 'createGroup'}]} # Create role for modifying create_role0_command = { 'commands': [{'command': 'createRole', 'rolename':'validrole' }] } create_role0_response = {'responses': [{'command': 'createRole'}]} # ========================================================================== # Create group # ========================================================================== # No groupname create_group1_command = { 'commands': [{'command': 'createGroup' }] } create_group1_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not a string create_group2_command = { 'commands': [{'command': 'createGroup', 'groupname':5 }] } create_group2_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 create_group3_command = { 'commands': [{'command': 'createGroup', 'groupname': '￿LO' }] } create_group3_response = {'responses': [{'command': 'createGroup', 'error': 'Group name not valid UTF-8'}]} # textname not a string create_group4_command = { 'commands': [{'command': 'createGroup', 'groupname':'g', 'textname':5 }] } create_group4_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing textname'}]} # textdescription not a string create_group5_command = { 'commands': [{'command': 'createGroup', 'groupname':'g', 'textdescription':5 }] } create_group5_response = {'responses': [{'command': 'createGroup', 'error': 'Invalid/missing textdescription'}]} # Group already exists create_group6_command = { 'commands': [{'command': 'createGroup', 'groupname': 'validgroup'}]} create_group6_response = {'responses': [{'command': 'createGroup', 'error': 'Group already exists'}]} # Role not found create_group7_command = { 'commands': [{'command': 'createGroup', 'groupname': 'group', 'roles':[{'rolename':'notfound'}]}] } create_group7_response = {'responses': [{'command': 'createGroup', 'error': 'Role not found'}]} # ========================================================================== # Delete group # ========================================================================== # No groupname delete_group1_command = { 'commands': [{'command': 'deleteGroup' }] } delete_group1_response = {'responses': [{'command': 'deleteGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not a string delete_group2_command = { 'commands': [{'command': 'deleteGroup', 'groupname':5 }] } delete_group2_response = {'responses': [{'command': 'deleteGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 delete_group3_command = { 'commands': [{'command': 'deleteGroup', 'groupname': '￿LO' }] } delete_group3_response = {'responses': [{'command': 'deleteGroup', 'error': 'Group name not valid UTF-8'}]} # Group not found delete_group4_command = { 'commands': [{'command': 'deleteGroup', 'groupname': 'group'}]} delete_group4_response = {'responses': [{'command': 'deleteGroup', 'error': 'Group not found'}]} # ========================================================================== # Add role # ========================================================================== # No groupname add_role1_command = { 'commands': [{'command': 'addGroupRole' }] } add_role1_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing groupname'}]} # Groupname not a string add_role2_command = { 'commands': [{'command': 'addGroupRole', 'groupname':5 }] } add_role2_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 add_role3_command = { 'commands': [{'command': 'addGroupRole', 'groupname': '￿LO' }] } add_role3_response = {'responses': [{'command': 'addGroupRole', 'error': 'Group name not valid UTF-8'}]} # No rolename add_role4_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'g' }] } add_role4_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing rolename'}]} # Rolename not a string add_role5_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'g', 'rolename':5 }] } add_role5_response = {'responses': [{'command': 'addGroupRole', 'error': 'Invalid/missing rolename'}]} # Rolename not UTF-8 add_role6_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'g', 'rolename':'￿LO' }] } add_role6_response = {'responses': [{'command': 'addGroupRole', 'error': 'Role name not valid UTF-8'}]} # Group not found add_role7_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'notfound', 'rolename':'notfound' }] } add_role7_response = {'responses': [{'command': 'addGroupRole', 'error': 'Group not found'}]} # Role not found add_role8_command = { 'commands': [{'command': 'addGroupRole', 'groupname':'validgroup', 'rolename':'notfound' }] } add_role8_response = {'responses': [{'command': 'addGroupRole', 'error': 'Role not found'}]} # ========================================================================== # Remove role # ========================================================================== # No groupname remove_role1_command = { 'commands': [{'command': 'removeGroupRole' }] } remove_role1_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing groupname'}]} # Groupname not a string remove_role2_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':5 }] } remove_role2_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 remove_role3_command = { 'commands': [{'command': 'removeGroupRole', 'groupname': '￿LO' }] } remove_role3_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Group name not valid UTF-8'}]} # No rolename remove_role4_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'g' }] } remove_role4_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing rolename'}]} # Rolename not a string remove_role5_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'g', 'rolename':5 }] } remove_role5_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Invalid/missing rolename'}]} # Rolename not UTF-8 remove_role6_command = { 'commands': [{'command': 'removeGroupRole', 'groupname': 'g', 'rolename':'￿LO' }] } remove_role6_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Role name not valid UTF-8'}]} # Group not found remove_role7_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'notfound', 'rolename':'notfound' }] } remove_role7_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Group not found'}]} # Role not found remove_role8_command = { 'commands': [{'command': 'removeGroupRole', 'groupname':'validgroup', 'rolename':'notfound' }] } remove_role8_response = {'responses': [{'command': 'removeGroupRole', 'error': 'Role not found'}]} # ========================================================================== # Add client # ========================================================================== # No groupname add_client1_command = { 'commands': [{'command': 'addGroupClient', 'username':'g' }] } add_client1_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing groupname'}]} # Groupname not a string add_client2_command = { 'commands': [{'command': 'addGroupClient', 'groupname':5, 'username':'g' }] } add_client2_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 add_client3_command = { 'commands': [{'command': 'addGroupClient', 'groupname': '￿LO', 'username':'g' }] } add_client3_response = {'responses': [{'command': 'addGroupClient', 'error': 'Group name not valid UTF-8'}]} # No username add_client4_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'g' }] } add_client4_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing username'}]} # Username not a string add_client5_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'g', 'username':5 }] } add_client5_response = {'responses': [{'command': 'addGroupClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 add_client6_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'g', 'username': '￿LO' }] } add_client6_response = {'responses': [{'command': 'addGroupClient', 'error': 'Username not valid UTF-8'}]} # Group not found add_client7_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'notfound', 'username':'validclient' }] } add_client7_response = {'responses': [{'command': 'addGroupClient', 'error': 'Group not found'}]} # Client not found add_client8_command = { 'commands': [{'command': 'addGroupClient', 'groupname':'validgroup', 'username':'notfound' }] } add_client8_response = {'responses': [{'command': 'addGroupClient', 'error': 'Client not found'}]} # ========================================================================== # Remove client # ========================================================================== # No groupname remove_client1_command = { 'commands': [{'command': 'removeGroupClient', 'username':'g' }] } remove_client1_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing groupname'}]} # Groupname not a string remove_client2_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':5, 'username':'g' }] } remove_client2_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 remove_client3_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'￿LO', 'username':'g' }] } remove_client3_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Group name not valid UTF-8'}]} # No username remove_client4_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'g' }] } remove_client4_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing username'}]} # Username not a string remove_client5_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'g', 'username':5 }] } remove_client5_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Invalid/missing username'}]} # Username not UTF-8 remove_client6_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'g', 'username': '￿LO' }] } remove_client6_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Username not valid UTF-8'}]} # Group not found remove_client7_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'notfound', 'username':'validclient' }] } remove_client7_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Group not found'}]} # Client not found remove_client8_command = { 'commands': [{'command': 'removeGroupClient', 'groupname':'validgroup', 'username':'notfound' }] } remove_client8_response = {'responses': [{'command': 'removeGroupClient', 'error': 'Client not found'}]} # ========================================================================== # Get group # ========================================================================== # No groupname get_group1_command = { 'commands': [{'command': 'getGroup'}] } get_group1_response = {'responses': [{'command': 'getGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not a string get_group2_command = { 'commands': [{'command': 'getGroup', 'groupname':5}] } get_group2_response = {'responses': [{'command': 'getGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 get_group3_command = { 'commands': [{'command': 'getGroup', 'groupname':'￿LO' }] } get_group3_response = {'responses': [{'command': 'getGroup', 'error': 'Group name not valid UTF-8'}]} # Group not found get_group4_command = { 'commands': [{'command': 'getGroup', 'groupname':"missing"}] } get_group4_response = {'responses': [{'command': 'getGroup', 'error': 'Group not found'}]} # ========================================================================== # Set anon group # ========================================================================== # No groupname set_anon_group1_command = { 'commands': [{'command': 'setAnonymousGroup'}] } set_anon_group1_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not a string set_anon_group2_command = { 'commands': [{'command': 'setAnonymousGroup', 'groupname':5}] } set_anon_group2_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Invalid/missing groupname'}]} # Groupname not UTF-8 set_anon_group3_command = { 'commands': [{'command': 'setAnonymousGroup', 'groupname':'￿LO' }] } set_anon_group3_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Group name not valid UTF-8'}]} # Group not found set_anon_group4_command = { 'commands': [{'command': 'setAnonymousGroup', 'groupname':'notfound' }] } set_anon_group4_response = {'responses': [{'command': 'setAnonymousGroup', 'error': 'Group not found'}]} # ========================================================================== # Modify group # ========================================================================== # No groupname modify_group1_command = { 'commands': [{'command': 'modifyGroup'}]} modify_group1_response = {'responses': [{'command': 'modifyGroup', 'error': 'Invalid/missing groupname'}]} # Group name not a string modify_group2_command = { 'commands': [{'command': 'modifyGroup', 'groupname':5}]} modify_group2_response = {'responses': [{'command': 'modifyGroup', 'error': 'Invalid/missing groupname'}]} # Group name not UTF-8 modify_group3_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'￿LO' }] } modify_group3_response = {'responses': [{'command': 'modifyGroup', 'error': 'Group name not valid UTF-8'}]} # roles not a list modify_group4_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'password':'test', 'roles':'string'}]} modify_group4_response = {'responses': [{'command': 'modifyGroup', 'error': "'roles' not an array or missing/invalid rolename"}]} # No rolename modify_group5_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'roles':[{}]}]} modify_group5_response = {'responses': [{'command': 'modifyGroup', 'error': "'roles' not an array or missing/invalid rolename"}]} # rolename not a string modify_group6_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'roles':[{'rolename':5}]}]} modify_group6_response = {'responses': [{'command': 'modifyGroup', 'error': "'roles' not an array or missing/invalid rolename"}]} # rolename not UTF-8 #modify_group7_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup','roles':[{'rolename':'￿LO'}] }] } #modify_group7_response = {'responses': [{'command': 'modifyGroup', 'error': 'Role name not valid UTF-8'}]} # Group not found modify_group8_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'notfound', 'rolename':'notfound'}]} modify_group8_response = {'responses': [{'command': 'modifyGroup', 'error': 'Group not found'}]} # Role not found modify_group9_command = { 'commands': [{'command': 'modifyGroup', 'groupname':'validgroup', 'roles':[{'rolename':'notfound'}]}]} modify_group9_response = {'responses': [{'command': 'modifyGroup', 'error': 'Role not found'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") command_check(sock, create_client0_command, create_client0_response, "0") command_check(sock, create_group0_command, create_group0_response, "0") command_check(sock, create_role0_command, create_role0_response, "0") command_check(sock, create_group1_command, create_group1_response, "1") command_check(sock, create_group2_command, create_group2_response, "2") command_check(sock, create_group3_command, create_group3_response, "3") command_check(sock, create_group4_command, create_group4_response, "4") command_check(sock, create_group5_command, create_group5_response, "5") command_check(sock, create_group6_command, create_group6_response, "6") command_check(sock, create_group7_command, create_group7_response, "7") command_check(sock, delete_group1_command, delete_group1_response, "1") command_check(sock, delete_group2_command, delete_group2_response, "2") command_check(sock, delete_group3_command, delete_group3_response, "3") command_check(sock, delete_group4_command, delete_group4_response, "4") command_check(sock, add_role1_command, add_role1_response, "1") command_check(sock, add_role2_command, add_role2_response, "2") command_check(sock, add_role3_command, add_role3_response, "3") command_check(sock, add_role4_command, add_role4_response, "4") command_check(sock, add_role5_command, add_role5_response, "5") command_check(sock, add_role6_command, add_role6_response, "6") command_check(sock, add_role7_command, add_role7_response, "7") command_check(sock, add_role8_command, add_role8_response, "8") command_check(sock, remove_role1_command, remove_role1_response, "1") command_check(sock, remove_role2_command, remove_role2_response, "2") command_check(sock, remove_role3_command, remove_role3_response, "3") command_check(sock, remove_role4_command, remove_role4_response, "4") command_check(sock, remove_role5_command, remove_role5_response, "5") command_check(sock, remove_role6_command, remove_role6_response, "6") command_check(sock, remove_role7_command, remove_role7_response, "7") command_check(sock, remove_role8_command, remove_role8_response, "8") command_check(sock, add_client1_command, add_client1_response, "1") command_check(sock, add_client2_command, add_client2_response, "2") command_check(sock, add_client3_command, add_client3_response, "3") command_check(sock, add_client4_command, add_client4_response, "4") command_check(sock, add_client5_command, add_client5_response, "5") command_check(sock, add_client6_command, add_client6_response, "6") command_check(sock, add_client7_command, add_client7_response, "7") command_check(sock, add_client8_command, add_client8_response, "8") command_check(sock, remove_client1_command, remove_client1_response, "1") command_check(sock, remove_client2_command, remove_client2_response, "2") command_check(sock, remove_client3_command, remove_client3_response, "3") command_check(sock, remove_client4_command, remove_client4_response, "4") command_check(sock, remove_client5_command, remove_client5_response, "5") command_check(sock, remove_client6_command, remove_client6_response, "6") command_check(sock, remove_client7_command, remove_client7_response, "7") command_check(sock, remove_client8_command, remove_client8_response, "8") command_check(sock, get_group1_command, get_group1_response, "1") command_check(sock, get_group2_command, get_group2_response, "2") command_check(sock, get_group3_command, get_group3_response, "3") command_check(sock, get_group4_command, get_group4_response, "4") command_check(sock, set_anon_group1_command, set_anon_group1_response, "1") command_check(sock, set_anon_group2_command, set_anon_group2_response, "2") command_check(sock, set_anon_group3_command, set_anon_group3_response, "3") command_check(sock, set_anon_group4_command, set_anon_group4_response, "4") command_check(sock, modify_group1_command, modify_group1_response, "1") command_check(sock, modify_group2_command, modify_group2_response, "2") command_check(sock, modify_group3_command, modify_group3_response, "3") command_check(sock, modify_group4_command, modify_group4_response, "4") command_check(sock, modify_group5_command, modify_group5_response, "5") command_check(sock, modify_group6_command, modify_group6_response, "6") #command_check(sock, modify_group7_command, modify_group7_response, "7") command_check(sock, modify_group8_command, modify_group8_response, "8") command_check(sock, modify_group9_command, modify_group9_response, "9") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-group.py000077500000000000000000000211641450213760600213370ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(msg) print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) create_client_command = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "textname": "Name", "textdescription": "description", "rolename": "", "correlationData": "2" }]} create_client_response = {'responses':[{"command":"createClient","correlationData":"2"}]} create_client2_command = { "commands": [{ "command": "createClient", "username": "user_two", "password": "password", "textname": "Name", "textdescription": "description", "rolename": "", "correlationData": "1" }]} create_client2_response = {'responses':[{"command":"createClient","correlationData":"1"}]} create_group_command = { "commands": [{ "command": "createGroup", "groupname": "group_one", "textname": "Name", "textdescription": "description", "correlationData":"3"}]} create_group_response = {'responses':[{"command":"createGroup","correlationData":"3"}]} create_group_repeat_response = {'responses':[{"command":"createGroup","error":"Group already exists","correlationData":"3"}]} create_group2_command = { "commands": [{ "command": "createGroup", "groupname": "group_two", "textname": "Name", "textdescription": "description", "correlationData":"30"}]} create_group2_response = {'responses':[{"command":"createGroup","correlationData":"30"}]} list_groups_command = { "commands": [{ "command": "listGroups", "verbose": False, "correlationData": "10"}]} list_groups_response = {'responses':[{"command": "listGroups", "data":{"totalCount":2, "groups":["group_one","group_two"]},"correlationData":"10"}]} list_groups_verbose_command = { "commands": [{ "command": "listGroups", "verbose": True, "correlationData": "15"}]} list_groups_verbose_response = {'responses':[{'command': 'listGroups', 'data': {"totalCount":2, 'groups':[ {'groupname': 'group_one', 'textname': 'Name', 'textdescription': 'description', 'clients': [ {"username":"user_one"}, {"username":"user_two"}], "roles":[]}, {'groupname': 'group_two', 'textname': 'Name', 'textdescription': 'description', 'clients': [ {"username":"user_one"}], "roles":[]} ]}, 'correlationData': '15'}]} list_clients_verbose_command = { "commands": [{ "command": "listClients", "verbose": True, "correlationData": "20"}]} list_clients_verbose_response = {'responses':[{"command": "listClients", "data":{"totalCount":3, "clients":[ {'username': 'admin', 'textname': 'Dynsec admin user', 'roles': [{'rolename': 'admin'}], 'groups': []}, {"username":"user_one", "clientid":"cid", "textname":"Name", "textdescription":"description", "groups":[{"groupname":"group_one"}, {"groupname":"group_two"}], "roles":[]}, {"username":"user_two", "textname":"Name", "textdescription":"description", "groups":[{"groupname":"group_one"}], "roles":[]}, ]}, "correlationData":"20"}]} get_group_command = { "commands": [{"command": "getGroup", "groupname":"group_one"}]} get_group_response = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one', 'textname':'Name', 'textdescription':'description', 'clients': [{"username":"user_one"}, {"username":"user_two"}], 'roles': []}}}]} add_client_to_group_command = {"commands": [{"command":"addGroupClient", "username":"user_one", "groupname": "group_one", "correlationData":"1234"}]} add_client_to_group_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1234'}]} add_duplicate_client_to_group_response = {'responses':[{'command': 'addGroupClient', 'error':'Client is already in this group', 'correlationData': '1234'}]} add_client_to_group2_command = {"commands": [{"command":"addGroupClient", "username":"user_one", "groupname": "group_two", "correlationData":"1234"}]} add_client_to_group2_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1234'}]} add_client2_to_group_command = {"commands": [{"command":"addGroupClient", "username":"user_two", "groupname": "group_one", "correlationData":"1235"}]} add_client2_to_group_response = {'responses':[{'command': 'addGroupClient', 'correlationData': '1235'}]} remove_client_from_group_command = {"commands": [{"command":"removeGroupClient", "username":"user_one", "groupname": "group_one", "correlationData":"4321"}]} remove_client_from_group_response = {'responses':[{'command': 'removeGroupClient', 'correlationData': '4321'}]} delete_group_command = {"commands": [{"command":"deleteGroup", "groupname":"group_one", "correlationData":"5678"}]} delete_group_response = {'responses':[{"command":"deleteGroup", "correlationData":"5678"}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Add client command_check(sock, create_client_command, create_client_response) command_check(sock, create_client2_command, create_client2_response) # Add group command_check(sock, create_group2_command, create_group2_response) command_check(sock, create_group_command, create_group_response) # Add client to group command_check(sock, add_client_to_group_command, add_client_to_group_response) command_check(sock, add_client_to_group2_command, add_client_to_group2_response) command_check(sock, add_client2_to_group_command, add_client2_to_group_response) command_check(sock, add_client_to_group_command, add_duplicate_client_to_group_response) # Get group command_check(sock, get_group_command, get_group_response) # List groups non-verbose command_check(sock, list_groups_command, list_groups_response) # List groups verbose command_check(sock, list_groups_verbose_command, list_groups_verbose_response, "list groups") # List clients verbose command_check(sock, list_clients_verbose_command, list_clients_verbose_response) # Kill broker and restart, checking whether our changes were saved. broker.terminate() broker.wait() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Add duplicate group command_check(sock, create_group_command, create_group_repeat_response) # Remove client from group command_check(sock, remove_client_from_group_command, remove_client_from_group_response) # Add client back to group command_check(sock, add_client_to_group_command, add_client_to_group_response) # Delete group entirely command_check(sock, delete_group_command, delete_group_response) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-modify-client.py000077500000000000000000000161471450213760600227530ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(msg) print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) create_client_command = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "textname": "Name", "textdescription": "Description", "correlationData": "2" }] } create_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} create_groups_command = { "commands": [ { "command": "createGroup", "groupname": "group_one", "textname": "Name", "textdescription": "Description", "correlationData": "12" }, { "command": "createGroup", "groupname": "group_two", "textname": "Name", "textdescription": "Description", "correlationData": "13" } ] } create_groups_response = {'responses': [ {'command': 'createGroup', 'correlationData': '12'}, {'command': 'createGroup', 'correlationData': '13'} ]} create_roles_command = { "commands": [ { "command": "createRole", "rolename": "role_one", "textname": "Name", "textdescription": "Description", "acls":[], "correlationData": "21" }, { "command": "createRole", "rolename": "role_two", "textname": "Name", "textdescription": "Description", "acls":[], "correlationData": "22" }, { "command": "createRole", "rolename": "role_three", "textname": "Name", "textdescription": "Description", "acls":[], "correlationData": "23" } ] } create_roles_response = {'responses': [ {'command': 'createRole', 'correlationData': '21'}, {'command': 'createRole', 'correlationData': '22'}, {'command': 'createRole', 'correlationData': '23'} ]} modify_client_command1 = { "commands": [{ "command": "modifyClient", "username": "user_one", "textname": "Modified name", "textdescription": "Modified description", "clientid": "", "roles":[ {'rolename':'role_one', 'priority':2}, {'rolename':'role_two'}, {'rolename':'role_three', 'priority':10} ], "groups":[ {'groupname':'group_one', 'priority':3}, {'groupname':'group_two', 'priority':8} ], "correlationData": "3" }] } modify_client_response1 = {'responses': [{'command': 'modifyClient', 'correlationData': '3'}]} modify_client_command2 = { "commands": [{ "command": "modifyClient", "username": "user_one", "textname": "Modified name", "textdescription": "Modified description", "groups":[], "correlationData": "4" }] } modify_client_response2 = {'responses': [{'command': 'modifyClient', 'correlationData': '4'}]} get_client_command1 = { "commands": [{ "command": "getClient", "username": "user_one"}]} get_client_response1 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'clientid': 'cid', 'textname': 'Name', 'textdescription': 'Description', 'roles': [], 'groups': [], }}}]} get_client_command2 = { "commands": [{ "command": "getClient", "username": "user_one"}]} get_client_response2 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'textname': 'Modified name', 'textdescription': 'Modified description', 'roles': [ {'rolename':'role_three', 'priority':10}, {'rolename':'role_one', 'priority':2}, {'rolename':'role_two'} ], 'groups': [ {'groupname':'group_two', 'priority':8}, {'groupname':'group_one', 'priority':3} ]}}}]} get_client_command3 = { "commands": [{ "command": "getClient", "username": "user_one"}]} get_client_response3 = {'responses':[{'command': 'getClient', 'data': {'client': {'username': 'user_one', 'textname': 'Modified name', 'textdescription': 'Modified description', 'groups': [], 'roles': [ {'rolename':'role_three', 'priority':10}, {'rolename':'role_one', 'priority':2}, {'rolename':'role_two'} ]}}}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Create client command_check(sock, create_client_command, create_client_response) # Create groups command_check(sock, create_groups_command, create_groups_response) # Create role command_check(sock, create_roles_command, create_roles_response) # Get client command_check(sock, get_client_command1, get_client_response1, "get client 1") # Modify client - with groups command_check(sock, modify_client_command1, modify_client_response1) # Get client command_check(sock, get_client_command2, get_client_response2, "get client 2a") # Kill broker and restart, checking whether our changes were saved. broker.terminate() broker.wait() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Get client command_check(sock, get_client_command2, get_client_response2, "get client 2b") # Modify client - without groups command_check(sock, modify_client_command2, modify_client_response2) # Get client command_check(sock, get_client_command3, get_client_response3, "get client 3") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") pass except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-modify-group.py000077500000000000000000000160451450213760600226260ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(msg) print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) create_client_command = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "textname": "Name", "textdescription": "Description", "correlationData": "2" }] } create_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} create_group_command = { "commands": [{ "command": "createGroup", "groupname": "group_one", "textname": "Name", "textdescription": "Description", "rolename": "", "correlationData": "2" }] } create_group_response = {'responses': [{'command': 'createGroup', 'correlationData': '2'}]} create_role_command = { "commands": [ { "command": "createRole", "rolename": "role_one", "textname": "Name", "textdescription": "Description", "acls":[], "correlationData": "2" }, { "command": "createRole", "rolename": "role_two", "textname": "Name", "textdescription": "Description", "acls":[], "correlationData": "3" } ] } create_role_response = {'responses': [ {'command': 'createRole', 'correlationData': '2'}, {'command': 'createRole', 'correlationData': '3'} ]} modify_group_command1 = { "commands": [{ "command": "modifyGroup", "groupname": "group_one", "textname": "Modified name", "textdescription": "Modified description", "roles":[{'rolename':'role_one'}], "clients":[{'username':'user_one'}], "correlationData": "3" }] } modify_group_response1 = {'responses': [{'command': 'modifyGroup', 'correlationData': '3'}]} modify_group_command2 = { "commands": [{ "command": "modifyGroup", "groupname": "group_one", "textname": "Modified name", "textdescription": "Modified description", "roles":[ {'rolename':'role_one', 'priority':99}, {'rolename':'role_two', 'priority':87} ], "clients":[], "correlationData": "3" }] } modify_group_response2 = {'responses': [{'command': 'modifyGroup', 'correlationData': '3'}]} modify_group_command3 = { "commands": [{ "command": "modifyGroup", "groupname": "group_one", "textname": "Modified name", "textdescription": "Modified description", "roles":[], "clients":[], "correlationData": "3" }] } modify_group_response3 = {'responses': [{'command': 'modifyGroup', 'correlationData': '3'}]} get_group_command1 = { "commands": [{ "command": "getGroup", "groupname": "group_one"}]} get_group_response1 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one', 'textname': 'Name', 'textdescription': 'Description', 'clients':[], 'roles': []}}}]} get_group_command2 = { "commands": [{ "command": "getGroup", "groupname": "group_one"}]} get_group_response2 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one', 'textname': 'Modified name', 'textdescription': 'Modified description', 'clients':[{'username':'user_one'}], 'roles': [{'rolename':'role_one'}]}}}]} get_group_command3 = { "commands": [{ "command": "getGroup", "groupname": "group_one"}]} get_group_response3 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one', 'textname': 'Modified name', 'textdescription': 'Modified description', 'clients':[], 'roles': [ {'rolename':'role_one', 'priority':99}, {'rolename':'role_two', 'priority':87} ]}}}]} get_group_command4 = { "commands": [{ "command": "getGroup", "groupname": "group_one"}]} get_group_response4 = {'responses':[{'command': 'getGroup', 'data': {'group': {'groupname': 'group_one', 'textname': 'Modified name', 'textdescription': 'Modified description', 'clients':[], 'roles': []}}}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Create client command_check(sock, create_client_command, create_client_response) # Create group command_check(sock, create_group_command, create_group_response) # Create role command_check(sock, create_role_command, create_role_response) # Get group command_check(sock, get_group_command1, get_group_response1, "get group 1") # Modify group command_check(sock, modify_group_command1, modify_group_response1) # Get group command_check(sock, get_group_command2, get_group_response2, "get group 2a") # Kill broker and restart, checking whether our changes were saved. broker.terminate() broker.wait() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Get group command_check(sock, get_group_command2, get_group_response2, "get group 2b") # Modify group command_check(sock, modify_group_command2, modify_group_response2) # Get group command_check(sock, get_group_command3, get_group_response3, "get group 3") # Modify group command_check(sock, modify_group_command3, modify_group_response3) # Get group command_check(sock, get_group_command4, get_group_response4, "get group 4") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") pass except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-modify-role.py000077500000000000000000000121001450213760600224170ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) create_role_command = { "commands": [{ "command": "createRole", "rolename": "role_one", "textname": "Name", "textdescription": "Description", "acls":[ { "acltype": "publishClientSend", "allow": True, "topic": "topic/#", "priority": 8 }, { "acltype": "publishClientSend", "allow": True, "topic": "topic/2/#", "priority": 9 } ], "correlationData": "2" }] } create_role_response = {'responses': [{'command': 'createRole', 'correlationData': '2'}]} modify_role_command = { "commands": [{ "command": "modifyRole", "rolename": "role_one", "textname": "Modified name", "textdescription": "Modified description", "acls":[ { "acltype": "publishClientReceive", "allow": True, "topic": "topic/#", "priority": 2 }, { "acltype": "publishClientReceive", "allow": True, "topic": "topic/2/#", "priority": 1 } ], "correlationData": "3" }] } modify_role_response = {'responses': [{'command': 'modifyRole', 'correlationData': '3'}]} get_role_command1 = { "commands": [{"command": "getRole", "rolename": "role_one"}]} get_role_response1 = {'responses':[{'command': 'getRole', 'data': {'role': {'rolename': 'role_one', 'textname': 'Name', 'textdescription': 'Description', 'acls': [ { "acltype": "publishClientSend", "topic": "topic/2/#", "allow": True, "priority": 9 }, { "acltype": "publishClientSend", "topic": "topic/#", "allow": True, "priority": 8 } ]}}}]} get_role_command2 = { "commands": [{ "command": "getRole", "rolename": "role_one"}]} get_role_response2 = {'responses':[{'command': 'getRole', 'data': {'role': {'rolename': 'role_one', 'textname': 'Modified name', 'textdescription': 'Modified description', 'acls': [ { "acltype": "publishClientReceive", "topic": "topic/#", "allow": True, "priority": 2 }, { "acltype": "publishClientReceive", "topic": "topic/2/#", "allow": True, "priority": 1 } ]}}}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Add role command_check(sock, create_role_command, create_role_response) # Get role command_check(sock, get_role_command1, get_role_response1) # Modify role command_check(sock, modify_role_command, modify_role_response) # Get role command_check(sock, get_role_command2, get_role_response2) # Kill broker and restart, checking whether our changes were saved. broker.terminate() broker.wait() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Get role command_check(sock, get_role_command2, get_role_response2) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-plugin-invalid.py000077500000000000000000000124241450213760600231240ustar00rootroot00000000000000#!/usr/bin/env python3 # Check invalid inputs for plugin commands from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) if msg != "": print(msg) raise ValueError(response) def command_check_text(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=command_payload) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) if msg != "": print(msg) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) # ========================================================================== # Bad commands # ========================================================================== # Invalid JSON bad1_command = 'not json' bad1_response = {'responses': [{'command': 'Unknown command', 'error': 'Invalid/missing commands'}]} # No commands bad2_command = {} bad2_response = {'responses': [{'command': 'Unknown command', 'error': 'Invalid/missing commands'}]} # Commands not an array bad3_command = {'commands': 'test'} bad3_response = {'responses': [{'command': 'Unknown command', 'error': 'Invalid/missing commands'}]} # Empty commands array bad4_command = {'commands': []} bad4_response = {'responses': []} # Empty command bad5_command = {'commands': ['bad']} bad5_response = {'responses': [{'command': 'Unknown command', 'error': 'Command not an object'}]} # Bad array type bad6_command = {'commands': [{}]} bad6_response = {'responses': [{'command': 'Unknown command', 'error': 'Missing command'}]} # Bad command type bad7_command = {'commands': [{'command':6}]} bad7_response = {'responses': [{'command': 'Unknown command', 'error': 'Missing command'}]} # Bad correlationData type bad8_command = {'commands': [{'command':'command', 'correlationData':6}]} bad8_response = {'responses': [{'command': 'command', 'error': 'Invalid correlationData data type.'}]} # Unknown command bad9_command = {'commands': [{'command':'command'}]} bad9_response = {'responses': [{'command': 'command', 'error': 'Unknown command'}]} # ========================================================================== # setDefaultACLAccess # ========================================================================== # Missing actions array set_default1_command = {'commands': [{'command':'setDefaultACLAccess'}]} set_default1_response = {'responses': [{'command': 'setDefaultACLAccess', 'error': 'Missing/invalid actions array'}]} # Actions array not an array set_default2_command = {'commands': [{'command':'setDefaultACLAccess', 'actions':'bad'}]} set_default2_response = {'responses': [{'command': 'setDefaultACLAccess', 'error': 'Missing/invalid actions array'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") command_check(sock, bad1_command, bad1_response, "1") command_check(sock, bad2_command, bad2_response, "2") command_check(sock, bad3_command, bad3_response, "3") command_check(sock, bad4_command, bad4_response, "4") command_check(sock, bad5_command, bad5_response, "5") command_check(sock, bad6_command, bad6_response, "6") command_check(sock, bad7_command, bad7_response, "7") command_check(sock, bad8_command, bad8_response, "8") command_check(sock, bad9_command, bad9_response, "9") command_check(sock, set_default1_command, set_default1_response, "1") command_check(sock, set_default2_command, set_default2_response, "2") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-role-invalid.py000077500000000000000000000411201450213760600225620ustar00rootroot00000000000000#!/usr/bin/env python3 # Check invalid inputs for role commands from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(expected_response) print(response) if msg != "": print(msg) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) # Create client for modifying create_client0_command = { 'commands': [{'command': 'createClient', 'username':'validclient' }] } create_client0_response = {'responses': [{'command': 'createClient'}]} # Create group for modifying create_group0_command = { 'commands': [{'command': 'createGroup', 'groupname':'validgroup' }] } create_group0_response = {'responses': [{'command': 'createGroup'}]} # Create role for modifying create_role0_command = { 'commands': [{'command': 'createRole', 'rolename':'validrole' }] } create_role0_response = {'responses': [{'command': 'createRole'}]} # Add ACL for modifying add_role_acl0_command = { 'commands': [{'command': 'addRoleACL', 'rolename':'validrole', 'acltype':'unsubscribePattern', 'topic':'validtopic', 'allow':True }] } add_role_acl0_response = {'responses': [{'command': 'addRoleACL'}]} # ========================================================================== # Create role # ========================================================================== # No rolename create_role1_command = { 'commands': [{'command': 'createRole' }] } create_role1_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing rolename'}]} # Rolename not a string create_role2_command = { 'commands': [{'command': 'createRole', 'rolename':5 }] } create_role2_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing rolename'}]} # Rolename not UTF-8 create_role3_command = { 'commands': [{'command': 'createRole', 'rolename': '￿LO' }] } create_role3_response = {'responses': [{'command': 'createRole', 'error': 'Role name not valid UTF-8'}]} # textname not a string create_role4_command = { 'commands': [{'command': 'createRole', 'rolename':'g', 'textname':5 }] } create_role4_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing textname'}]} # textdescription not a string create_role5_command = { 'commands': [{'command': 'createRole', 'rolename':'g', 'textdescription':5 }] } create_role5_response = {'responses': [{'command': 'createRole', 'error': 'Invalid/missing textdescription'}]} # Role already exists create_role6_command = { 'commands': [{'command': 'createRole', 'rolename': 'validrole'}]} create_role6_response = {'responses': [{'command': 'createRole', 'error': 'Role already exists'}]} # Bad ACLs #create_role7_command = { 'commands': [{'command': 'createRole', 'rolename': 'role', 'roles':[{'rolename':'notfound'}]}] } #create_role7_response = {'responses': [{'command': 'createRole', 'error': 'Role not found'}]} # ========================================================================== # Delete role # ========================================================================== # No rolename delete_role1_command = { 'commands': [{'command': 'deleteRole' }] } delete_role1_response = {'responses': [{'command': 'deleteRole', 'error': 'Invalid/missing rolename'}]} # Rolename not a string delete_role2_command = { 'commands': [{'command': 'deleteRole', 'rolename':5 }] } delete_role2_response = {'responses': [{'command': 'deleteRole', 'error': 'Invalid/missing rolename'}]} # Rolename not UTF-8 delete_role3_command = { 'commands': [{'command': 'deleteRole', 'rolename': '￿LO' }] } delete_role3_response = {'responses': [{'command': 'deleteRole', 'error': 'Role name not valid UTF-8'}]} # Role not found delete_role4_command = { 'commands': [{'command': 'deleteRole', 'rolename': 'role'}]} delete_role4_response = {'responses': [{'command': 'deleteRole', 'error': 'Role not found'}]} # ========================================================================== # Get role # ========================================================================== # No rolename get_role1_command = { 'commands': [{'command': 'getRole'}] } get_role1_response = {'responses': [{'command': 'getRole', 'error': 'Invalid/missing rolename'}]} # rolename not a string get_role2_command = { 'commands': [{'command': 'getRole', 'rolename':5}] } get_role2_response = {'responses': [{'command': 'getRole', 'error': 'Invalid/missing rolename'}]} # rolename not UTF-8 get_role3_command = { 'commands': [{'command': 'getRole', 'rolename': '￿LO' }] } get_role3_response = {'responses': [{'command': 'getRole', 'error': 'Role name not valid UTF-8'}]} # role not found get_role4_command = { 'commands': [{'command': 'getRole', 'rolename':"notfound"}] } get_role4_response = {'responses': [{'command': 'getRole', 'error': 'Role not found'}]} # ========================================================================== # Add role ACL # ========================================================================== add_role_acl1_command = { 'commands': [{'command': 'addRoleACL'}]} add_role_acl1_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing rolename'}]} add_role_acl2_command = { 'commands': [{'command': 'addRoleACL', 'rolename':5}]} add_role_acl2_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing rolename'}]} add_role_acl3_command = { 'commands': [{'command': 'addRoleACL', 'rolename': '￿LO' }] } add_role_acl3_response = {'responses': [{'command': 'addRoleACL', 'error': 'Role name not valid UTF-8'}]} add_role_acl4_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'notvalid' }] } add_role_acl4_response = {'responses': [{'command': 'addRoleACL', 'error': 'Role not found'}]} add_role_acl5_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole' }] } add_role_acl5_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing acltype'}]} add_role_acl6_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'notvalid' }] } add_role_acl6_response = {'responses': [{'command': 'addRoleACL', 'error': 'Unknown acltype'}]} add_role_acl7_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern' }] } add_role_acl7_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing topic'}]} add_role_acl8_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'subscribePattern', 'topic':5 }] } add_role_acl8_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid/missing topic'}]} add_role_acl9_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribeLiteral', 'topic':'￿LO' }] } add_role_acl9_response = {'responses': [{'command': 'addRoleACL', 'error': 'Topic not valid UTF-8'}]} add_role_acl10_command = { 'commands': [{'command': 'addRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribeLiteral', 'topic':'not/#/valid' }] } add_role_acl10_response = {'responses': [{'command': 'addRoleACL', 'error': 'Invalid ACL topic'}]} # ========================================================================== # Remove role ACL # ========================================================================== remove_role_acl1_command = { 'commands': [{'command': 'removeRoleACL'}]} remove_role_acl1_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing rolename'}]} remove_role_acl2_command = { 'commands': [{'command': 'removeRoleACL', 'rolename':5}]} remove_role_acl2_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing rolename'}]} remove_role_acl3_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': '￿LO' }] } remove_role_acl3_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Role name not valid UTF-8'}]} remove_role_acl4_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'notvalid' }] } remove_role_acl4_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Role not found'}]} remove_role_acl5_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole' }] } remove_role_acl5_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing acltype'}]} remove_role_acl6_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'notvalid' }] } remove_role_acl6_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Unknown acltype'}]} remove_role_acl7_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern' }] } remove_role_acl7_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing topic'}]} remove_role_acl8_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern', 'topic':5 }] } remove_role_acl8_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Invalid/missing topic'}]} remove_role_acl9_command = { 'commands': [{'command': 'removeRoleACL', 'rolename': 'validrole', 'acltype':'unsubscribePattern', 'topic':'￿LO' }] } remove_role_acl9_response = {'responses': [{'command': 'removeRoleACL', 'error': 'Topic not valid UTF-8'}]} # ========================================================================== # Modify role # ========================================================================== # No groupname modify_group1_command = { 'commands': [{'command': 'modifyRole'}]} modify_group1_response = {'responses': [{'command': 'modifyRole', 'error': 'Invalid/missing groupname'}]} # Username not a string modify_group2_command = { 'commands': [{'command': 'modifyRole', 'groupname':5}]} modify_group2_response = {'responses': [{'command': 'modifyRole', 'error': 'Invalid/missing groupname'}]} # Username not UTF-8 modify_group3_command = { 'commands': [{'command': 'modifyRole', 'rolename': '￿LO' }] } modify_group3_response = {'responses': [{'command': 'modifyRole', 'error': 'Role name not valid UTF-8'}]} # roles not a list modify_group4_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'password':'test', 'roles':'string'}]} modify_group4_response = {'responses': [{'command': 'modifyRole', 'error': "'roles' not an array or missing/invalid rolename"}]} # No rolename modify_group5_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'roles':[{}]}]} modify_group5_response = {'responses': [{'command': 'modifyRole', 'error': "'roles' not an array or missing/invalid rolename"}]} # rolename not a string modify_group6_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'roles':[{'rolename':5}]}]} modify_group6_response = {'responses': [{'command': 'modifyRole', 'error': "'roles' not an array or missing/invalid rolename"}]} # rolename not UTF-8 modify_group3_command = { 'commands': [{'command': 'modifyRole', 'rolename': '￿LO' }] } #modify_group7_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup'}]} #modify_group7_response = {'responses': [{'command': 'modifyRole', 'error': 'Invalid/missing rolename'}]} # Role not found modify_group8_command = { 'commands': [{'command': 'modifyRole', 'groupname':'notfound', 'rolename':'notfound'}]} modify_group8_response = {'responses': [{'command': 'modifyRole', 'error': 'Role not found'}]} # Role not found modify_group9_command = { 'commands': [{'command': 'modifyRole', 'groupname':'validgroup', 'roles':[{'rolename':'notfound'}]}]} modify_group9_response = {'responses': [{'command': 'modifyRole', 'error': 'Role not found'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/dynamic-security/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") command_check(sock, create_client0_command, create_client0_response, "0") command_check(sock, create_group0_command, create_group0_response, "0") command_check(sock, create_role0_command, create_role0_response, "0") command_check(sock, add_role_acl0_command, add_role_acl0_response, "0") command_check(sock, create_role1_command, create_role1_response, "1") command_check(sock, create_role2_command, create_role2_response, "2") command_check(sock, create_role3_command, create_role3_response, "3") command_check(sock, create_role4_command, create_role4_response, "4") command_check(sock, create_role5_command, create_role5_response, "5") command_check(sock, create_role6_command, create_role6_response, "6") #command_check(sock, create_role7_command, create_role7_response, "7") command_check(sock, delete_role1_command, delete_role1_response, "1") command_check(sock, delete_role2_command, delete_role2_response, "2") command_check(sock, delete_role3_command, delete_role3_response, "3") command_check(sock, delete_role4_command, delete_role4_response, "4") command_check(sock, get_role1_command, get_role1_response, "1") command_check(sock, get_role2_command, get_role2_response, "2") command_check(sock, get_role3_command, get_role3_response, "3") command_check(sock, get_role4_command, get_role4_response, "4") command_check(sock, add_role_acl1_command, add_role_acl1_response, "1") command_check(sock, add_role_acl2_command, add_role_acl2_response, "2") command_check(sock, add_role_acl3_command, add_role_acl3_response, "3") command_check(sock, add_role_acl4_command, add_role_acl4_response, "4") command_check(sock, add_role_acl5_command, add_role_acl5_response, "5") command_check(sock, add_role_acl6_command, add_role_acl6_response, "6") command_check(sock, add_role_acl7_command, add_role_acl7_response, "7") command_check(sock, add_role_acl8_command, add_role_acl8_response, "8") command_check(sock, add_role_acl9_command, add_role_acl9_response, "9") command_check(sock, add_role_acl10_command, add_role_acl10_response, "10") command_check(sock, remove_role_acl1_command, remove_role_acl1_response, "1") command_check(sock, remove_role_acl2_command, remove_role_acl2_response, "2") command_check(sock, remove_role_acl3_command, remove_role_acl3_response, "3") command_check(sock, remove_role_acl4_command, remove_role_acl4_response, "4") command_check(sock, remove_role_acl5_command, remove_role_acl5_response, "5") command_check(sock, remove_role_acl6_command, remove_role_acl6_response, "6") command_check(sock, remove_role_acl7_command, remove_role_acl7_response, "7") command_check(sock, remove_role_acl8_command, remove_role_acl8_response, "8") command_check(sock, remove_role_acl9_command, remove_role_acl9_response, "9") #command_check(sock, modify_role1_command, modify_role1_response, "1") #command_check(sock, modify_role2_command, modify_role2_response, "2") ##command_check(sock, modify_role3_command, modify_role3_response, "3") #command_check(sock, modify_role4_command, modify_role4_response, "4") #command_check(sock, modify_role5_command, modify_role5_response, "5") #command_check(sock, modify_role6_command, modify_role6_response, "6") ##command_check(sock, modify_role7_command, modify_role7_response, "7") #command_check(sock, modify_role8_command, modify_role8_response, "8") #command_check(sock, modify_role9_command, modify_role9_response, "9") rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/14-dynsec-role.py000077500000000000000000000310751450213760600211460ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * import json import shutil def write_config(filename, port): with open(filename, 'w') as f: f.write("listener %d\n" % (port)) f.write("allow_anonymous true\n") f.write("plugin ../../plugins/dynamic-security/mosquitto_dynamic_security.so\n") f.write("plugin_opt_config_file %d/dynamic-security.json\n" % (port)) def command_check(sock, command_payload, expected_response, msg=""): command_packet = mosq_test.gen_publish(topic="$CONTROL/dynamic-security/v1", qos=0, payload=json.dumps(command_payload)) sock.send(command_packet) response = json.loads(mosq_test.read_publish(sock)) if response != expected_response: print(msg) print(expected_response) print(response) raise ValueError(response) port = mosq_test.get_port() conf_file = os.path.basename(__file__).replace('.py', '.conf') write_config(conf_file, port) create_client_command = { "commands": [{ "command": "createClient", "username": "user_one", "password": "password", "clientid": "cid", "textname": "Name", "textdescription": "Description", "rolename": "", "correlationData": "2" }] } create_client_response = {'responses': [{'command': 'createClient', 'correlationData': '2'}]} create_client2_command = { "commands": [{ "command": "createClient", "username": "user_two", "password": "password", "textname": "Name", "textdescription": "Description", "rolename": "", "correlationData": "3" }] } create_client2_response = {'responses': [{'command': 'createClient', 'correlationData': '3'}]} create_group_command = { "commands": [{ "command": "createGroup", "groupname": "group_one", "textname": "Name", "textdescription": "Description", "correlationData":"3"}]} create_group_response = {'responses':[{"command":"createGroup","correlationData":"3"}]} create_role_command = { "commands": [{'command': 'createRole', 'correlationData': '3', "rolename": "basic", "acls":[ {"acltype":"publishClientSend", "topic": "out/#", "priority":3, "allow": True}], "textname":"name", "textdescription":"desc" }]} create_role_response = {'responses': [{'command': 'createRole', 'correlationData': '3'}]} create_role2_command = { "commands": [{'command': 'createRole', 'correlationData': '3', "rolename": "basic2", "acls":[ {"acltype":"publishClientSend", "topic": "out/#", "priority":3, "allow": True}], "textname":"name", "textdescription":"desc" }]} create_role2_response = {'responses': [{'command': 'createRole', 'correlationData': '3'}]} add_role_to_client_command = {"commands": [{'command': 'addClientRole', "username": "user_one", "rolename": "basic"}]} add_role_to_client_response = {'responses': [{'command': 'addClientRole'}]} add_role_to_client2_command = {"commands": [{'command': 'addClientRole', "username": "user_one", "rolename": "basic2"}]} add_role_to_client2_response = {'responses': [{'command': 'addClientRole'}]} add_role_to_group_command = {"commands": [{'command': 'addGroupRole', "groupname": "group_one", "rolename": "basic"}]} add_role_to_group_response = {'responses': [{'command': 'addGroupRole'}]} list_roles_verbose_command1 = { "commands": [{ "command": "listRoles", "verbose": True, "correlationData": "21"}] } list_roles_verbose_response1 = {'responses': [{'command': 'listRoles', 'data': {'totalCount':3, 'roles': [ {"rolename":"admin","acls":[ {"acltype": "publishClientSend", "topic": "$CONTROL/dynamic-security/#", "priority":0, "allow": True }, {"acltype": "publishClientReceive", "topic": "$CONTROL/dynamic-security/#", "priority":0, "allow": True }, {"acltype": "publishClientReceive", "topic": "$SYS/#", "priority":0, "allow": True }, {"acltype": "publishClientReceive", "topic": "#", "priority":0, "allow": True }, {"acltype": "subscribePattern", "topic": "$CONTROL/dynamic-security/#", "priority":0, "allow": True }, {"acltype": "subscribePattern", "topic": "$SYS/#", "priority":0, "allow": True }, {"acltype": "subscribePattern", "topic": "#", "priority":0, "allow": True}, {"acltype": "unsubscribePattern", "topic": "#", "priority":0, "allow": True}]}, {'rolename': 'basic', "textname": "name", "textdescription": "desc", 'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}]}, {'rolename': 'basic2', "textname": "name", "textdescription": "desc", 'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}] }]}, 'correlationData': '21'}]} add_acl_command = {"commands": [{'command': "addRoleACL", "rolename":"basic", "acltype":"subscribeLiteral", "topic":"basic/out", "priority":1, "allow":True}]} add_acl_response = {'responses': [{'command': 'addRoleACL'}]} add_acl2_command = {"commands": [{'command': "addRoleACL", "rolename":"basic", "acltype":"subscribeLiteral", "topic":"basic/out", "priority":1, "allow":True}]} add_acl2_response = {'responses': [{'command': 'addRoleACL', 'error':'ACL with this topic already exists'}]} list_roles_verbose_command2 = { "commands": [{ "command": "listRoles", "verbose": True, "correlationData": "22"}] } list_roles_verbose_response2 = {'responses': [{'command': 'listRoles', 'data': {'totalCount':3, 'roles': [{"rolename":"admin","acls":[ {"acltype": "publishClientSend", "topic": "$CONTROL/dynamic-security/#", "priority":0, "allow": True }, {"acltype": "publishClientReceive", "topic": "$CONTROL/dynamic-security/#", "priority":0, "allow": True }, {"acltype": "publishClientReceive", "topic": "$SYS/#", "priority":0, "allow": True }, {"acltype": "publishClientReceive", "topic": "#", "priority":0, "allow": True }, {"acltype": "subscribePattern", "topic": "$CONTROL/dynamic-security/#", "priority":0, "allow": True }, {"acltype": "subscribePattern", "topic": "$SYS/#", "priority":0, "allow": True }, {"acltype": "subscribePattern", "topic": "#", "priority":0, "allow": True}, {"acltype": "unsubscribePattern", "topic": "#", "priority":0, "allow": True}]}, {'rolename': 'basic', 'textname': 'name', 'textdescription': 'desc', 'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}, {'acltype':'subscribeLiteral', 'topic': 'basic/out', 'priority': 1, 'allow': True}]}, {'rolename': 'basic2', "textname": "name", "textdescription": "desc", 'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}] }]}, 'correlationData': '22'}]} get_role_command = {"commands": [{'command': "getRole", "rolename":"basic"}]} get_role_response = {'responses': [{'command': 'getRole', 'data': {'role': {'rolename': 'basic', 'textname': 'name', 'textdescription': 'desc', 'acls': [{'acltype':'publishClientSend', 'topic': 'out/#', 'priority': 3, 'allow': True}, {'acltype':'subscribeLiteral', 'topic': 'basic/out', 'priority': 1, 'allow': True}], }}}]} remove_acl_command = {"commands": [{'command': "removeRoleACL", "rolename":"basic", "acltype":"subscribeLiteral", "topic":"basic/out"}]} remove_acl_response = {'responses': [{'command': 'removeRoleACL'}]} remove_acl2_command = {"commands": [{'command': "removeRoleACL", "rolename":"basic", "acltype":"subscribeLiteral", "topic":"basic/out"}]} remove_acl2_response = {'responses': [{'command': 'removeRoleACL', 'error':'ACL not found'}]} delete_role_command = {"commands": [{'command': "deleteRole", "rolename":"basic"}]} delete_role_response = {"responses": [{"command": "deleteRole"}]} delete_role2_command = {"commands": [{'command': "deleteRole", "rolename":"basic"}]} delete_role2_response = {"responses": [{"command": "deleteRole"}]} list_clients_verbose_command = { "commands": [{ "command": "listClients", "verbose": True, "correlationData": "20"}] } list_clients_verbose_response = {'responses':[{"command": "listClients", "data":{'totalCount':3, "clients":[ {'username': 'admin', 'textname': 'Dynsec admin user', 'roles': [{'rolename': 'admin'}], 'groups': []}, {"username":"user_one", "clientid":"cid", "textname":"Name", "textdescription":"Description", "groups":[], "roles":[{'rolename':'basic'}, {'rolename':'basic2'}]}, {"username":"user_two", "textname":"Name", "textdescription":"Description", "groups":[], "roles":[]}]}, "correlationData":"20"}]} list_groups_verbose_command = { "commands": [{ "command": "listGroups", "verbose": True, "correlationData": "20"}] } list_groups_verbose_response = {'responses':[{"command": "listGroups", "data":{'totalCount':1, "groups":[ {"groupname":"group_one", "textname":"Name", "textdescription":"Description", "clients":[], "roles":[{'rolename':'basic'}]}]}, "correlationData":"20"}]} remove_role_from_client_command = {"commands": [{'command': 'removeClientRole', "username": "user_one", "rolename": "basic"}]} remove_role_from_client_response = {'responses': [{'command': 'removeClientRole'}]} remove_role_from_group_command = {"commands": [{'command': 'removeGroupRole', "groupname": "group_one", "rolename": "basic"}]} remove_role_from_group_response = {'responses': [{'command': 'removeGroupRole'}]} rc = 1 keepalive = 10 connect_packet = mosq_test.gen_connect("ctrl-test", keepalive=keepalive, username="admin", password="admin") connack_packet = mosq_test.gen_connack(rc=0) mid = 2 subscribe_packet = mosq_test.gen_subscribe(mid, "$CONTROL/#", 1) suback_packet = mosq_test.gen_suback(mid, 1) try: os.mkdir(str(port)) shutil.copyfile("dynamic-security-init.json", "%d/dynamic-security.json" % (port)) except FileExistsError: pass broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # Create client command_check(sock, create_client2_command, create_client2_response) command_check(sock, create_client_command, create_client_response) # Create group command_check(sock, create_group_command, create_group_response) # Create role command_check(sock, create_role_command, create_role_response) command_check(sock, create_role2_command, create_role2_response) # Add role to client command_check(sock, add_role_to_client2_command, add_role_to_client2_response) command_check(sock, add_role_to_client_command, add_role_to_client_response) # Add role to group command_check(sock, add_role_to_group_command, add_role_to_group_response) # List clients verbose command_check(sock, list_clients_verbose_command, list_clients_verbose_response) # List groups verbose command_check(sock, list_groups_verbose_command, list_groups_verbose_response) # List roles verbose 1 command_check(sock, list_roles_verbose_command1, list_roles_verbose_response1, "list roles verbose 1a") # Add ACL command_check(sock, add_acl_command, add_acl_response) command_check(sock, add_acl2_command, add_acl2_response) # List roles verbose 2 command_check(sock, list_roles_verbose_command2, list_roles_verbose_response2, "list roles verbose 2a") # Kill broker and restart, checking whether our changes were saved. broker.terminate() broker.wait() broker = mosq_test.start_broker(filename=os.path.basename(__file__), use_conf=True, port=port) sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=5, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") # List roles verbose 2 command_check(sock, list_roles_verbose_command2, list_roles_verbose_response2, "list roles verbose 2b") # Get role command_check(sock, get_role_command, get_role_response) # Remove ACL command_check(sock, remove_acl_command, remove_acl_response) command_check(sock, remove_acl2_command, remove_acl2_response) # List roles verbose 1 command_check(sock, list_roles_verbose_command1, list_roles_verbose_response1, "list roles verbose 1b") # Remove role from client command_check(sock, remove_role_from_client_command, remove_role_from_client_response) # Remove role from group command_check(sock, remove_role_from_group_command, remove_role_from_group_response) # Delete role command_check(sock, delete_role_command, delete_role_response) rc = 0 sock.close() except mosq_test.TestError: pass finally: os.remove(conf_file) try: os.remove(f"{port}/dynamic-security.json") except FileNotFoundError: pass os.rmdir(f"{port}") broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/Makefile000066400000000000000000000142631450213760600175630ustar00rootroot00000000000000include ../../config.mk .PHONY: all check clean test ptest seqtest .NOTPARALLEL: all : check : test clean : -rm -f *.vglog *.db $(MAKE) -C c clean test-compile : $(MAKE) -C c ptest : test-compile msg_sequence_test ./test.py test : test-compile msg_sequence_test 01 02 03 04 05 06 07 08 09 10 11 12 13 14 msg_sequence_test: ./msg_sequence_test.py 01 : ./01-bad-initial-packets.py ./01-connect-575314.py ./01-connect-allow-anonymous.py ./01-connect-disconnect-v5.py ./01-connect-max-connections.py ./01-connect-max-keepalive.py ./01-connect-take-over.py ./01-connect-uname-no-password-denied.py ./01-connect-uname-or-anon.py ./01-connect-uname-password-denied-no-will.py ./01-connect-uname-password-denied.py ./01-connect-windows-line-endings.py ./01-connect-zero-length-id.py 02 : ./02-shared-qos0-v5.py ./02-subhier-crash.py ./02-subpub-qos0-long-topic.py ./02-subpub-qos0-oversize-payload.py ./02-subpub-qos0-queued-bytes.py ./02-subpub-qos0-retain-as-publish.py ./02-subpub-qos0-send-retain.py ./02-subpub-qos0-subscription-id.py ./02-subpub-qos0-topic-alias-unknown.py ./02-subpub-qos0-topic-alias.py ./02-subpub-qos1-message-expiry-retain.py ./02-subpub-qos1-message-expiry-will.py ./02-subpub-qos1-message-expiry.py ./02-subpub-qos1-nolocal.py ./02-subpub-qos1-oversize-payload.py ./02-subpub-qos1.py ./02-subpub-qos2-1322.py ./02-subpub-qos2-max-inflight-bytes.py ./02-subpub-qos2-pubrec-error.py ./02-subpub-qos2-receive-maximum-1.py ./02-subpub-qos2-receive-maximum-2.py ./02-subpub-qos2.py ./02-subpub-recover-subscriptions.py ./02-subscribe-dollar-v5.py ./02-subscribe-invalid-utf8.py ./02-subscribe-long-topic.py ./02-subscribe-persistence-flipflop.py 03 : #./03-publish-qos1-queued-bytes.py ./03-pattern-matching.py ./03-publish-b2c-disconnect-qos1.py ./03-publish-b2c-disconnect-qos2.py ./03-publish-b2c-qos1-len.py ./03-publish-b2c-qos2-len.py ./03-publish-c2b-disconnect-qos2.py ./03-publish-c2b-qos2-len.py ./03-publish-dollar-v5.py ./03-publish-dollar.py ./03-publish-invalid-utf8.py ./03-publish-long-topic.py ./03-publish-qos1-max-inflight-expire.py ./03-publish-qos1-no-subscribers-v5.py ./03-publish-qos1-retain-disabled.py ./03-publish-qos1.py ./03-publish-qos2-dup.py ./03-publish-qos2-max-inflight.py ./03-publish-qos2.py 04 : ./04-retain-check-source-persist-diff-port.py ./04-retain-check-source-persist.py ./04-retain-check-source.py ./04-retain-clear-multiple.py ./04-retain-qos0-clear.py ./04-retain-qos0-fresh.py ./04-retain-qos0-repeated.py ./04-retain-qos0.py ./04-retain-qos1-qos0.py ./04-retain-upgrade-outgoing-qos.py 05 : ./05-clean-session-qos1.py ./05-session-expiry-v5.py 06 : ./06-bridge-b2br-disconnect-qos1.py ./06-bridge-b2br-disconnect-qos2.py ./06-bridge-b2br-late-connection-retain.py ./06-bridge-b2br-late-connection.py ./06-bridge-b2br-remapping.py ./06-bridge-br2b-disconnect-qos1.py ./06-bridge-br2b-disconnect-qos2.py ./06-bridge-br2b-remapping.py ./06-bridge-clean-session-csF-lcsF.py ./06-bridge-clean-session-csF-lcsN.py ./06-bridge-clean-session-csF-lcsT.py ./06-bridge-clean-session-csT-lcsF.py ./06-bridge-clean-session-csT-lcsN.py ./06-bridge-clean-session-csT-lcsT.py ./06-bridge-fail-persist-resend-qos1.py ./06-bridge-fail-persist-resend-qos2.py ./06-bridge-no-local.py ./06-bridge-outgoing-retain.py ./06-bridge-per-listener-settings.py ./06-bridge-reconnect-local-out.py 07 : ./07-will-control.py ./07-will-delay-invalid-573191.py ./07-will-delay-reconnect.py ./07-will-delay-recover.py ./07-will-delay-session-expiry.py ./07-will-delay-session-expiry2.py ./07-will-delay.py ./07-will-disconnect-with-will.py ./07-will-invalid-utf8.py ./07-will-no-flag.py ./07-will-null-topic.py ./07-will-null.py ./07-will-oversize-payload.py ./07-will-per-listener.py ./07-will-properties.py ./07-will-qos0.py ./07-will-reconnect-1273.py ./07-will-takeover.py 08 : ifeq ($(WITH_TLS),yes) ./08-ssl-bridge.py ./08-ssl-connect-cert-auth-crl.py ./08-ssl-connect-cert-auth-expired.py ./08-ssl-connect-cert-auth-revoked.py ./08-ssl-connect-cert-auth-without.py ./08-ssl-connect-cert-auth.py ./08-ssl-connect-identity.py ./08-ssl-connect-no-auth-wrong-ca.py ./08-ssl-connect-no-auth.py ./08-ssl-connect-no-identity.py ./08-ssl-hup-disconnect.py ifeq ($(WITH_TLS_PSK),yes) ./08-tls-psk-pub.py ./08-tls-psk-bridge.py endif endif 09 : ./09-acl-access-variants.py ./09-acl-change.py ./09-acl-empty-file.py ./09-auth-bad-method.py ./09-extended-auth-change-username.py ./09-extended-auth-multistep-reauth.py ./09-extended-auth-multistep.py ./09-extended-auth-reauth.py ./09-extended-auth-single.py ./09-plugin-acl-change.py ./09-plugin-auth-acl-pub.py ./09-plugin-auth-acl-sub-denied.py ./09-plugin-auth-acl-sub.py ./09-plugin-auth-context-params.py ./09-plugin-auth-defer-unpwd-fail.py ./09-plugin-auth-defer-unpwd-success.py ./09-plugin-auth-msg-params.py ./09-plugin-auth-unpwd-fail.py ./09-plugin-auth-unpwd-success.py ./09-plugin-auth-v2-unpwd-fail.py ./09-plugin-auth-v2-unpwd-success.py ./09-plugin-publish.py ./09-plugin-tick.py ./09-pwfile-parse-invalid.py 10 : ./10-listener-mount-point.py 11 : ./11-message-expiry.py ./11-persistent-subscription.py ./11-persistent-subscription-v5.py ./11-persistent-subscription-no-local.py ./11-pub-props.py ./11-subscription-id.py 12 : ./12-prop-assigned-client-identifier.py ./12-prop-maximum-packet-size-broker.py ./12-prop-maximum-packet-size-publish-qos1.py ./12-prop-maximum-packet-size-publish-qos2.py ./12-prop-response-topic-correlation-data.py ./12-prop-response-topic.py ./12-prop-server-keepalive.py ./12-prop-subpub-content-type.py ./12-prop-subpub-payload-format.py 13 : ./13-malformed-publish-v5.py ./13-malformed-subscribe-v5.py ./13-malformed-unsubscribe-v5.py 14 : ifeq ($(WITH_TLS),yes) ifeq ($(WITH_CJSON),yes) ./14-dynsec-acl.py ./14-dynsec-anon-group.py ./14-dynsec-auth.py ./14-dynsec-client.py ./14-dynsec-client-invalid.py ./14-dynsec-default-access.py ./14-dynsec-disable-client.py ./14-dynsec-group.py ./14-dynsec-group-invalid.py ./14-dynsec-modify-client.py ./14-dynsec-modify-group.py ./14-dynsec-modify-role.py ./14-dynsec-plugin-invalid.py ./14-dynsec-role.py ./14-dynsec-role-invalid.py endif endif mosquitto-2.0.18/test/broker/c/000077500000000000000000000000001450213760600163375ustar00rootroot00000000000000mosquitto-2.0.18/test/broker/c/08-tls-psk-bridge.c000066400000000000000000000024071450213760600215620ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid; void on_log(struct mosquitto *mosq, void *obj, int level, const char *str) { printf("%s\n", str); } void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, &sent_mid, "psk/test", strlen("message"), "message", 1, false); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == sent_mid){ mosquitto_disconnect(mosq); run = 0; }else{ exit(1); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port; port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("08-tls-psk-bridge", true, NULL); mosquitto_tls_opts_set(mosq, 1, "tlsv1", NULL); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); mosquitto_log_callback_set(mosq, on_log); rc = mosquitto_connect(mosq, "localhost", port, 60); if(rc) return rc; while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/broker/c/08-tls-psk-pub.c000066400000000000000000000024421450213760600211130ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, &sent_mid, "psk/test", strlen("message"), "message", 0, false); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == sent_mid){ mosquitto_disconnect(mosq); run = 0; }else{ exit(1); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } int main(int argc, char *argv[]) { int rc; int port; struct mosquitto *mosq; mosquitto_lib_init(); port = atoi(argv[1]); mosq = mosquitto_new("08-tls-psk-pub", true, NULL); mosquitto_tls_opts_set(mosq, 1, "tlsv1", NULL); rc = mosquitto_tls_psk_set(mosq, "deadbeef", "psk-id", NULL); if(rc){ mosquitto_destroy(mosq); return rc; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); rc = mosquitto_connect(mosq, "localhost", port, 60); if(rc){ mosquitto_destroy(mosq); return rc; } while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/broker/c/Makefile000066400000000000000000000016111450213760600177760ustar00rootroot00000000000000.PHONY: all test clean reallyclean CFLAGS=-I../../../include -Wall -Werror PLUGIN_SRC = \ auth_plugin_acl.c \ auth_plugin_acl_change.c \ auth_plugin_acl_sub_denied.c \ auth_plugin_context_params.c \ auth_plugin_extended_multiple.c \ auth_plugin_extended_reauth.c \ auth_plugin_extended_single.c \ auth_plugin_extended_single2.c \ auth_plugin_msg_params.c \ auth_plugin_publish.c \ auth_plugin_pwd.c \ auth_plugin_v2.c \ auth_plugin_v4.c \ auth_plugin_v5.c \ auth_plugin_v5_handle_message.c \ auth_plugin_v5_handle_tick.c \ plugin_control.c PLUGINS = ${PLUGIN_SRC:.c=.so} SRC = \ 08-tls-psk-pub.c \ 08-tls-psk-bridge.c TESTS = ${SRC:.c=.test} all : ${PLUGINS} ${TESTS} ${PLUGINS} : %.so: %.c $(CC) ${CFLAGS} -fPIC -shared $< -o $@ ${TESTS} : %.test: %.c $(CC) ${CFLAGS} $< -o $@ ../../../lib/libmosquitto.so.1 reallyclean : clean -rm -f *.orig clean : rm -f *.so *.test mosquitto-2.0.18/test/broker/c/auth_plugin_acl.c000066400000000000000000000030361450213760600216430ustar00rootroot00000000000000#include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { const char *username = mosquitto_client_username(client); if(username && !strcmp(username, "readonly") && access == MOSQ_ACL_READ){ return MOSQ_ERR_SUCCESS; }else if(username && !strcmp(username, "readonly") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')) { return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { return MOSQ_ERR_PLUGIN_DEFER; } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_acl_change.c000066400000000000000000000030201450213760600231410ustar00rootroot00000000000000#include #include #include #include #include int mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data); int mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data); static mosquitto_plugin_id_t *plg_id; static int login_count = 0; int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { return 5; } int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { plg_id = identifier; mosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL, NULL); mosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL, NULL); return MOSQ_ERR_SUCCESS; } int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { mosquitto_callback_unregister(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL); mosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data) { struct mosquitto_evt_acl_check *ed = event_data; if(login_count == 2 && ed->access == MOSQ_ACL_WRITE){ return MOSQ_ERR_ACL_DENIED; }else{ return MOSQ_ERR_SUCCESS; } } int mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data) { login_count++; return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/test/broker/c/auth_plugin_acl_sub_denied.c000066400000000000000000000024131450213760600240220ustar00rootroot00000000000000#include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { if(access == MOSQ_ACL_SUBSCRIBE){ return MOSQ_ERR_ACL_DENIED; }else{ return MOSQ_ERR_SUCCESS; } } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_context_params.c000066400000000000000000000050011450213760600241250ustar00rootroot00000000000000#include #include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { return MOSQ_ERR_PLUGIN_DEFER; } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { const char *tmp; tmp = mosquitto_client_address(client); if(!tmp || strcmp(tmp, "127.0.0.1")){ return MOSQ_ERR_AUTH; } if(!mosquitto_client_clean_session(client)){ fprintf(stderr, "mosquitto_auth_unpwd_check clean_session error: %d\n", mosquitto_client_clean_session(client)); return MOSQ_ERR_AUTH; } tmp = mosquitto_client_id(client); if(!tmp || strcmp(tmp, "client-params-test")){ fprintf(stderr, "mosquitto_auth_unpwd_check client_id error: %s\n", tmp); return MOSQ_ERR_AUTH; } if(mosquitto_client_keepalive(client) != 42){ fprintf(stderr, "mosquitto_auth_unpwd_check keepalive error: %d\n", mosquitto_client_keepalive(client)); return MOSQ_ERR_AUTH; } if(!mosquitto_client_certificate(client)){ // FIXME //return MOSQ_ERR_AUTH; } if(mosquitto_client_protocol(client) != mp_mqtt){ fprintf(stderr, "mosquitto_auth_unpwd_check protocol error: %d\n", mosquitto_client_protocol(client)); return MOSQ_ERR_AUTH; } if(mosquitto_client_sub_count(client)){ fprintf(stderr, "mosquitto_auth_unpwd_check sub_count error: %d\n", mosquitto_client_sub_count(client)); return MOSQ_ERR_AUTH; } tmp = mosquitto_client_username(client); if(!tmp || strcmp(tmp, "client-username")){ fprintf(stderr, "mosquitto_auth_unpwd_check username error: %s\n", tmp); return MOSQ_ERR_AUTH; } return MOSQ_ERR_SUCCESS; } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_extended_multiple.c000066400000000000000000000040111450213760600246110ustar00rootroot00000000000000#include #include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { return MOSQ_ERR_PLUGIN_DEFER; } int 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) { int i; if(!strcmp(method, "mirror")){ if(data_len > 0){ *data_out = malloc(data_len); if(!(*data_out)){ return MOSQ_ERR_NOMEM; } for(i=0; i 0){ len = strlen("supercalifragilisticexpialidocious")>data_len?data_len:strlen("supercalifragilisticexpialidocious"); if(!memcmp(data, "supercalifragilisticexpialidocious", len)){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } }else{ return MOSQ_ERR_INVAL; } } return MOSQ_ERR_NOT_SUPPORTED; } mosquitto-2.0.18/test/broker/c/auth_plugin_extended_reauth.c000066400000000000000000000026261450213760600242600ustar00rootroot00000000000000#include #include #include #include #include #include static int auth_count = 0; int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { return MOSQ_ERR_PLUGIN_DEFER; } int 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) { if(auth_count == 0){ auth_count++; return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } } int 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) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_extended_single.c000066400000000000000000000041121450213760600242410ustar00rootroot00000000000000#include #include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { return MOSQ_ERR_PLUGIN_DEFER; } int 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) { int i; if(!strcmp(method, "error")){ return MOSQ_ERR_INVAL; }else if(!strcmp(method, "non-matching")){ return MOSQ_ERR_NOT_SUPPORTED; }else if(!strcmp(method, "single")){ data_len = data_len>strlen("data")?strlen("data"):data_len; if(!memcmp(data, "data", data_len)){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } }else if(!strcmp(method, "change")){ return mosquitto_set_username(client, "new_username"); }else if(!strcmp(method, "mirror")){ if(data_len > 0){ *data_out = malloc(data_len); if(!(*data_out)){ return MOSQ_ERR_NOMEM; } for(i=0; i #include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { return MOSQ_ERR_PLUGIN_DEFER; } int 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) { int i; if(!strcmp(method, "error2")){ return MOSQ_ERR_INVAL; }else if(!strcmp(method, "non-matching2")){ return MOSQ_ERR_NOT_SUPPORTED; }else if(!strcmp(method, "single2")){ data_len = data_len>strlen("data")?strlen("data"):data_len; if(!memcmp(data, "data", data_len)){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } }else if(!strcmp(method, "change2")){ return mosquitto_set_username(client, "new_username"); }else if(!strcmp(method, "mirror2")){ if(data_len > 0){ *data_out = malloc(data_len); if(!(*data_out)){ return MOSQ_ERR_NOMEM; } for(i=0; i #include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { if(access == MOSQ_ACL_SUBSCRIBE){ return MOSQ_ERR_SUCCESS; } if(!msg->topic || strcmp(msg->topic, "param/topic")){ abort(); return MOSQ_ERR_ACL_DENIED; } if(!msg->payload || strncmp(msg->payload, "payload contents", strlen("payload contents"))){ abort(); return MOSQ_ERR_ACL_DENIED; } if(msg->payloadlen != strlen("payload contents")){ abort(); return MOSQ_ERR_ACL_DENIED; } if(msg->qos != 1){ abort(); return MOSQ_ERR_ACL_DENIED; } if(!msg->retain){ abort(); return MOSQ_ERR_ACL_DENIED; } return MOSQ_ERR_SUCCESS; } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { return MOSQ_ERR_PLUGIN_DEFER; } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_publish.c000066400000000000000000000054011450213760600225500ustar00rootroot00000000000000#include #include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { static int count = 0; mosquitto_property *props = NULL; if(access == MOSQ_ACL_WRITE){ if(count == 0){ /* "missing-client" isn't connected, so we can check memory usage properly. */ mosquitto_broker_publish_copy("missing-client", "topic/2", strlen("test-message-2"), "test-message-2", 2, true, NULL); mosquitto_broker_publish_copy("test-client", "topic/0", strlen("test-message-0"), "test-message-0", 0, true, NULL); mosquitto_broker_publish_copy("missing-client", "topic/2", strlen("test-message-2"), "test-message-2", 2, true, NULL); mosquitto_broker_publish_copy("test-client", "topic/1", strlen("test-message-1"), "test-message-1", 1, true, NULL); mosquitto_broker_publish_copy("missing-client", "topic/2", strlen("test-message-2"), "test-message-2", 2, true, NULL); mosquitto_broker_publish_copy("test-client", "topic/2", strlen("test-message-2"), "test-message-2", 2, true, NULL); count = 1; }else{ mosquitto_property_add_byte(&props, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); mosquitto_broker_publish_copy("test-client", "topic/0", strlen("test-message-0"), "test-message-0", 0, true, props); props = NULL; mosquitto_property_add_byte(&props, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); mosquitto_broker_publish_copy("test-client", "topic/1", strlen("test-message-1"), "test-message-1", 1, true, props); props = NULL; mosquitto_property_add_byte(&props, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); mosquitto_broker_publish_copy("test-client", "topic/2", strlen("test-message-2"), "test-message-2", 2, true, props); } } return MOSQ_ERR_SUCCESS; } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_pwd.c000066400000000000000000000027311450213760600216770ustar00rootroot00000000000000#include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { return MOSQ_ERR_PLUGIN_DEFER; } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { if(!strcmp(username, "test-username") && password && !strcmp(password, "cnwTICONIURW")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(username, "readonly")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(username, "test-username@v2")){ return MOSQ_ERR_PLUGIN_DEFER; }else{ return MOSQ_ERR_AUTH; } } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_v2.c000066400000000000000000000033361450213760600214360ustar00rootroot00000000000000#include #include #include "mosquitto_plugin_v2.h" /* * Following constant come from mosquitto.h * * They are copied here to fix value of those constant at the time of MOSQ_AUTH_PLUGIN_VERSION == 2 */ enum mosq_err_t { MOSQ_ERR_SUCCESS = 0, MOSQ_ERR_AUTH = 11, MOSQ_ERR_ACL_DENIED = 12 }; int mosquitto_auth_plugin_version(void) { return 2; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access) { if(!strcmp(username, "readonly") && access == MOSQ_ACL_READ){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } int mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password) { if(!strcmp(username, "test-username") && password && !strcmp(password, "cnwTICONIURW")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(username, "readonly")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(username, "test-username@v2")){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } } int mosquitto_auth_psk_key_get(void *user_data, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_v4.c000066400000000000000000000040751450213760600214410ustar00rootroot00000000000000#include #include #include #include #include int mosquitto_auth_plugin_version(void) { return 4; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { const char *username = mosquitto_client_username(client); if(username && !strcmp(username, "readonly") && access == MOSQ_ACL_READ){ return MOSQ_ERR_SUCCESS; }else if(username && !strcmp(username, "readonly") && access == MOSQ_ACL_SUBSCRIBE &&!strchr(msg->topic, '#') && !strchr(msg->topic, '+')) { return MOSQ_ERR_SUCCESS; }else if(username && !strcmp(username, "readwrite")){ if((!strcmp(msg->topic, "readonly") && access == MOSQ_ACL_READ) || !strcmp(msg->topic, "writeable")){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } }else{ return MOSQ_ERR_ACL_DENIED; } } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { if(!strcmp(username, "test-username") && password && !strcmp(password, "cnwTICONIURW")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(username, "readonly") || !strcmp(username, "readwrite")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(username, "test-username@v2")){ return MOSQ_ERR_PLUGIN_DEFER; }else{ return MOSQ_ERR_AUTH; } } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/broker/c/auth_plugin_v5.c000066400000000000000000000045301450213760600214360ustar00rootroot00000000000000#include #include #include #include #include int mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data); int mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data); static mosquitto_plugin_id_t *plg_id; int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { return 5; } int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { plg_id = identifier; mosquitto_callback_register(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL, NULL); mosquitto_callback_register(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL, NULL); return MOSQ_ERR_SUCCESS; } int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { mosquitto_callback_unregister(plg_id, MOSQ_EVT_ACL_CHECK, mosquitto_auth_acl_check_v5, NULL); mosquitto_callback_unregister(plg_id, MOSQ_EVT_BASIC_AUTH, mosquitto_auth_unpwd_check_v5, NULL); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check_v5(int event, void *event_data, void *user_data) { struct mosquitto_evt_acl_check *ed = event_data; const char *username = mosquitto_client_username(ed->client); if(username && !strcmp(username, "readonly") && ed->access == MOSQ_ACL_READ){ return MOSQ_ERR_SUCCESS; }else if(username && !strcmp(username, "readonly") && ed->access == MOSQ_ACL_SUBSCRIBE &&!strchr(ed->topic, '#') && !strchr(ed->topic, '+')) { return MOSQ_ERR_SUCCESS; }else if(username && !strcmp(username, "readwrite")){ if((!strcmp(ed->topic, "readonly") && ed->access == MOSQ_ACL_READ) || !strcmp(ed->topic, "writeable")){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } }else{ return MOSQ_ERR_ACL_DENIED; } } int mosquitto_auth_unpwd_check_v5(int event, void *event_data, void *user_data) { struct mosquitto_evt_basic_auth *ed = event_data; if(!strcmp(ed->username, "test-username") && ed->password && !strcmp(ed->password, "cnwTICONIURW")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(ed->username, "readonly") || !strcmp(ed->username, "readwrite")){ return MOSQ_ERR_SUCCESS; }else if(!strcmp(ed->username, "test-username@v2")){ return MOSQ_ERR_PLUGIN_DEFER; }else{ return MOSQ_ERR_AUTH; } } mosquitto-2.0.18/test/broker/c/auth_plugin_v5_handle_message.c000066400000000000000000000020461450213760600244550ustar00rootroot00000000000000#include #include #include #include #include #include static int handle_publish(int event, void *event_data, void *user_data); static mosquitto_plugin_id_t *plg_id; int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { return 5; } int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { plg_id = identifier; mosquitto_callback_register(plg_id, MOSQ_EVT_MESSAGE, handle_publish, NULL, NULL); return MOSQ_ERR_SUCCESS; } int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { mosquitto_callback_unregister(plg_id, MOSQ_EVT_MESSAGE, handle_publish, NULL); return MOSQ_ERR_SUCCESS; } int handle_publish(int event, void *event_data, void *user_data) { struct mosquitto_evt_message *ed = event_data; mosquitto_free(ed->topic); ed->topic = mosquitto_strdup("fixed-topic"); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/test/broker/c/auth_plugin_v5_handle_tick.c000066400000000000000000000020231450213760600237560ustar00rootroot00000000000000#include #include #include #include #include #include static int handle_tick(int event, void *event_data, void *user_data); static mosquitto_plugin_id_t *plg_id; int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { return 5; } int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { plg_id = identifier; mosquitto_callback_register(plg_id, MOSQ_EVT_TICK, handle_tick, NULL, NULL); return MOSQ_ERR_SUCCESS; } int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { mosquitto_callback_unregister(plg_id, MOSQ_EVT_TICK, handle_tick, NULL); return MOSQ_ERR_SUCCESS; } int handle_tick(int event, void *event_data, void *user_data) { mosquitto_broker_publish_copy("plugin-tick-test", "topic/tick", strlen("test-message"), "test-message", 0, false, NULL); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/test/broker/c/mosquitto_plugin_v2.h000066400000000000000000000176361450213760600225560ustar00rootroot00000000000000/* Copyright (c) 2012-2014 Roger Light All rights reserved. This program and the accompanying materials are made available under the terms of the Eclipse Public License 2.0 and Eclipse Distribution License v1.0 which accompany this distribution. The Eclipse Public License is available at https://www.eclipse.org/legal/epl-2.0/ and the Eclipse Distribution License is available at http://www.eclipse.org/org/documents/edl-v10.php. Contributors: Roger Light - initial implementation and documentation. */ #ifndef MOSQUITTO_PLUGIN_H #define MOSQUITTO_PLUGIN_H #define MOSQ_AUTH_PLUGIN_VERSION 2 #define MOSQ_ACL_NONE 0x00 #define MOSQ_ACL_READ 0x01 #define MOSQ_ACL_WRITE 0x02 struct mosquitto_auth_opt { char *key; char *value; }; /* * To create an authentication plugin you must include this file then implement * the functions listed below. The resulting code should then be compiled as a * shared library. Using gcc this can be achieved as follows: * * gcc -I -fPIC -shared plugin.c -o plugin.so * * On Mac OS X: * * gcc -I -fPIC -shared plugin.c -undefined dynamic_lookup -o plugin.so * */ /* ========================================================================= * * Utility Functions * * Use these functions from within your plugin. * * There are also very useful functions in libmosquitto. * * ========================================================================= */ /* * Function: mosquitto_log_printf * * Write a log message using the broker configured logging. * * Parameters: * level - Log message priority. Can currently be one of: * * MOSQ_LOG_INFO * MOSQ_LOG_NOTICE * MOSQ_LOG_WARNING * MOSQ_LOG_ERR * MOSQ_LOG_DEBUG * MOSQ_LOG_SUBSCRIBE (not recommended for use by plugins) * MOSQ_LOG_UNSUBSCRIBE (not recommended for use by plugins) * * These values are defined in mosquitto.h. * * fmt, ... - printf style format and arguments. */ void mosquitto_log_printf(int level, const char *fmt, ...); /* ========================================================================= * * Plugin Functions * * You must implement these functions in your plugin. * * ========================================================================= */ /* * Function: mosquitto_auth_plugin_version * * The broker will call this function immediately after loading the plugin to * check it is a supported plugin version. Your code must simply return * MOSQ_AUTH_PLUGIN_VERSION. */ int mosquitto_auth_plugin_version(void); /* * Function: mosquitto_auth_plugin_init * * Called after the plugin has been loaded and * has been called. This will only ever be called once and can be used to * initialise the plugin. * * Parameters: * * user_data : The pointer set here will be passed to the other plugin * functions. Use to hold connection information for example. * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which * provides the plugin options defined in the configuration file. * auth_opt_count : The number of elements in the auth_opts array. * * Return value: * Return 0 on success * Return >0 on failure. */ int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count); /* * Function: mosquitto_auth_plugin_cleanup * * Called when the broker is shutting down. This will only ever be called once. * Note that will be called directly before * this function. * * Parameters: * * user_data : The pointer provided in . * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which * provides the plugin options defined in the configuration file. * auth_opt_count : The number of elements in the auth_opts array. * * Return value: * Return 0 on success * Return >0 on failure. */ int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count); /* * Function: mosquitto_auth_security_init * * Called when the broker initialises the security functions when it starts up. * If the broker is requested to reload its configuration whilst running, * will be called, followed by this function. * In this situation, the reload parameter will be true. * * Parameters: * * user_data : The pointer provided in . * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which * provides the plugin options defined in the configuration file. * auth_opt_count : The number of elements in the auth_opts array. * reload : If set to false, this is the first time the function has * been called. If true, the broker has received a signal * asking to reload its configuration. * * Return value: * Return 0 on success * Return >0 on failure. */ int mosquitto_auth_security_init(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload); /* * Function: mosquitto_auth_security_cleanup * * Called when the broker cleans up the security functions when it shuts down. * If the broker is requested to reload its configuration whilst running, * this function will be called, followed by . * In this situation, the reload parameter will be true. * * Parameters: * * user_data : The pointer provided in . * auth_opts : Pointer to an array of struct mosquitto_auth_opt, which * provides the plugin options defined in the configuration file. * auth_opt_count : The number of elements in the auth_opts array. * reload : If set to false, this is the first time the function has * been called. If true, the broker has received a signal * asking to reload its configuration. * * Return value: * Return 0 on success * Return >0 on failure. */ int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_auth_opt *auth_opts, int auth_opt_count, bool reload); /* * Function: mosquitto_auth_acl_check * * Called by the broker when topic access must be checked. access will be one * of MOSQ_ACL_READ (for subscriptions) or MOSQ_ACL_WRITE (for publish). Return * MOSQ_ERR_SUCCESS if access was granted, MOSQ_ERR_ACL_DENIED if access was * not granted, or MOSQ_ERR_UNKNOWN for an application specific error. */ int mosquitto_auth_acl_check(void *user_data, const char *clientid, const char *username, const char *topic, int access); /* * Function: mosquitto_auth_unpwd_check * * Called by the broker when a username/password must be checked. Return * MOSQ_ERR_SUCCESS if the user is authenticated, MOSQ_ERR_AUTH if * authentication failed, or MOSQ_ERR_UNKNOWN for an application specific * error. */ int mosquitto_auth_unpwd_check(void *user_data, const char *username, const char *password); /* * Function: mosquitto_psk_key_get * * Called by the broker when a client connects to a listener using TLS/PSK. * This is used to retrieve the pre-shared-key associated with a client * identity. * * Examine hint and identity to determine the required PSK (which must be a * hexadecimal string with no leading "0x") and copy this string into key. * * Parameters: * user_data : the pointer provided in . * hint : the psk_hint for the listener the client is connecting to. * identity : the identity string provided by the client * key : a string where the hex PSK should be copied * max_key_len : the size of key * * Return value: * Return 0 on success. * Return >0 on failure. * Return >0 if this function is not required. */ int mosquitto_auth_psk_key_get(void *user_data, const char *hint, const char *identity, char *key, int max_key_len); #endif mosquitto-2.0.18/test/broker/c/plugin_control.c000066400000000000000000000024351450213760600215450ustar00rootroot00000000000000#include #include #include #include #include #include static mosquitto_plugin_id_t *plg_id = NULL; int control_callback(int event, void *event_data, void *userdata) { struct mosquitto_evt_control *ed = event_data; mosquitto_broker_publish_copy(NULL, ed->topic, ed->payloadlen, ed->payload, 0, 0, NULL); return 0; } int mosquitto_plugin_version(int supported_version_count, const int *supported_versions) { return MOSQ_PLUGIN_VERSION; } int mosquitto_plugin_init(mosquitto_plugin_id_t *identifier, void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { int i; char buf[100]; plg_id = identifier; for(i=0; i<100; i++){ snprintf(buf, sizeof(buf), "$CONTROL/user-management/v%d", i); mosquitto_callback_register(plg_id, MOSQ_EVT_CONTROL, control_callback, "$CONTROL/user-management/v1", NULL); } return MOSQ_ERR_SUCCESS; } int mosquitto_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { int i; char buf[100]; for(i=0; i<100; i++){ snprintf(buf, sizeof(buf), "$CONTROL/user-management/v%d", i); mosquitto_callback_unregister(plg_id, MOSQ_EVT_CONTROL, control_callback, "$CONTROL/user-management/v1"); } return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/test/broker/data/000077500000000000000000000000001450213760600170265ustar00rootroot00000000000000mosquitto-2.0.18/test/broker/data/AUTH.json000066400000000000000000000025471450213760600204720ustar00rootroot00000000000000[ { "comment": "AUTH TESTS ARE INCOMPLETE", "group": "v3.1.1 AUTH", "tests": [ { "name": "F0 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"F0 00"}]}, { "name": "F0 long", "ver":4, "msgs": [{"type":"send", "payload":"F0 01 00"}]}, { "name": "F1", "ver":4, "msgs": [{"type":"send", "payload":"F1 00"}]}, { "name": "F2", "ver":4, "msgs": [{"type":"send", "payload":"F2 00"}]}, { "name": "F4", "ver":4, "msgs": [{"type":"send", "payload":"F4 00"}]}, { "name": "F8", "ver":4, "msgs": [{"type":"send", "payload":"F8 00"}]} ] }, { "group": "v5.0 AUTH", "tests": [ { "name": "F0 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"F0 00"}]}, { "name": "F0 long", "ver":5, "msgs": [ {"type":"send", "payload":"F0 01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "F1", "ver":5, "msgs": [ {"type":"send", "payload":"F1 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "F2", "ver":5, "msgs": [ {"type":"send", "payload":"F2 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "F4", "ver":5, "msgs": [ {"type":"send", "payload":"F4 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "F8", "ver":5, "msgs": [ {"type":"send", "payload":"F8 00"}, {"type":"recv", "payload":"E0 01 82"} ]} ] } ] mosquitto-2.0.18/test/broker/data/CONNACK.json000066400000000000000000000541161450213760600210040ustar00rootroot00000000000000[ { "group": "v3.1.1 CONNACK", "tests": [ { "name": "20 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"20 02 00 00"}]}, { "name": "20 long", "ver":4, "msgs": [{"type":"send", "payload":"20 03 00 00 00"}]}, { "name": "20 short 1", "ver":4, "msgs": [{"type":"send", "payload":"20 01 00"}]}, { "name": "20 short 0", "ver":4, "msgs": [{"type":"send", "payload":"20 00"}]}, { "name": "20", "ver":4, "msgs": [{"type":"send", "payload":"20 02 00 00"}]}, { "name": "21", "ver":4, "msgs": [{"type":"send", "payload":"21 02 00 00"}]}, { "name": "22", "ver":4, "msgs": [{"type":"send", "payload":"22 02 00 00"}]}, { "name": "24", "ver":4, "msgs": [{"type":"send", "payload":"24 02 00 00"}]}, { "name": "28", "ver":4, "msgs": [{"type":"send", "payload":"28 02 00 00"}]}, { "name": "issue 2163 v3", "ver":3, "msgs": [{"type":"send", "payload":"29 02 00 01"}]}, { "name": "issue 2163 v4", "ver":4, "msgs": [{"type":"send", "payload":"29 02 00 01"}]}, { "name": "20 CAF=0x01", "ver":4, "msgs": [{"type":"send", "payload":"20 02 01 00"}]}, { "name": "20 CAF=0x02", "ver":4, "msgs": [{"type":"send", "payload":"20 02 02 00"}]}, { "name": "20 CAF=0x04", "ver":4, "msgs": [{"type":"send", "payload":"20 02 04 00"}]}, { "name": "20 CAF=0x08", "ver":4, "msgs": [{"type":"send", "payload":"20 02 08 00"}]}, { "name": "20 CAF=0x10", "ver":4, "msgs": [{"type":"send", "payload":"20 02 10 00"}]}, { "name": "20 CAF=0x20", "ver":4, "msgs": [{"type":"send", "payload":"20 02 20 00"}]}, { "name": "20 CAF=0x40", "ver":4, "msgs": [{"type":"send", "payload":"20 02 40 00"}]}, { "name": "20 CAF=0x80", "ver":4, "msgs": [{"type":"send", "payload":"20 02 80 00"}]} ] }, { "group": "v5.0 CONNACK", "comment": "CMD RL FLAG RC PROPLEN PROPS", "tests": [ { "name": "20 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"20 03 00 00 00"}]}, { "name": "20 with properties", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03 21000A"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 long", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 short 2", "ver":5, "msgs": [ {"type":"send", "payload":"20 02 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 short 1", "ver":5, "msgs": [ {"type":"send", "payload":"20 01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 short 0", "ver":5, "msgs": [ {"type":"send", "payload":"20 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "21", "ver":5, "msgs": [ {"type":"send", "payload":"21 03 00 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "22", "ver":5, "msgs": [ {"type":"send", "payload":"22 03 00 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "24", "ver":5, "msgs": [ {"type":"send", "payload":"24 03 00 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "28", "ver":5, "msgs": [ {"type":"send", "payload":"28 03 00 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "issue 2163 v5", "ver":5, "msgs": [ {"type":"send", "payload":"29 02 00 01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x01", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 01 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x02", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 02 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x04", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 04 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x08", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 08 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x10", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 10 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x20", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 20 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x40", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 40 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 CAF=0x80", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 80 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x01 (invalid)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x80 (unspecified error)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 80 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x81 (malformed packet)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 81 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x82 (protocol error)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 82 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x83 (implementation specific error)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 83 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x84 (unsupported protocol version)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 84 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x85 (client identifier not valid)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 85 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x86 (bad user name or password)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 86 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x87 (not authorised)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 87 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x88 (server unavailable)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 88 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x89 (server busy)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 89 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x8A (banned)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 8A 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x8C (bad authentication method)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 8C 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x90 (topic name invalid)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 90 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x95 (packet too large)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 95 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x97 (quota exceeded)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 97 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x99 (payload format invalid)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 99 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x9A (retain not supported)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 9A 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x9B (qos not supported)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 9B 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x9C (use another server)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 9C 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x9D (server moved)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 9D 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0x9F (connection rate exceeded)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 9F 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 RC=0xFF (invalid)", "ver":5, "msgs": [ {"type":"send", "payload":"20 03 00 FF 00"}, {"type":"recv", "payload":"E0 01 82"} ]} ] }, { "group": "v5.0 CONNACK PROPERTIES", "comment": "CMD RL FLAG RC PROPLEN PROPS", "tests": [ { "name": "20 with reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 1F000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 1F"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with user-property", "ver":5, "msgs": [ {"type":"send", "payload":"20 0A 00 00 07 26000170000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 23000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 23"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0100"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 1700"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 2400"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 2500"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 2800"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 2900"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 2A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 17"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 24"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 25"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 28"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 29"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 2A"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 08 00 00 05 0200000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 08 00 00 05 1100000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 08 00 00 05 1800000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 08 00 00 05 2700000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 02"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 11"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 18"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 27"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 03000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 08000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 12000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 15000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 1A000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 1C000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 03"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 08"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 12"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 15"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 1A"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 1C"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 09000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 16000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 09"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 16"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 0B"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03 130101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03210101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03 220101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03 230101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 13"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 21"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 22"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"20 04 00 00 01 23"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0401"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0501"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0601"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0701"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0A01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0C01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0D01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0E01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 0F01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 1001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 1401"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 1B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 1D01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 1E01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 2001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 05 00 00 02 7F01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03 800001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03 800101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 06 00 00 03 FF7F01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 80800101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 07 00 00 04 FFFF7F01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 08 00 00 05 8080800101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "20 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"20 08 00 00 05 FFFFFF7F01"}, {"type":"recv", "payload":"E0 01 82"} ]} ] } ] mosquitto-2.0.18/test/broker/data/CONNECT.json000066400000000000000000000352571450213760600210260ustar00rootroot00000000000000[ { "comment": "CONNECT TESTS ARE INCOMPLETE", "group": "v3.1 CONNECT", "tests": [ { "name": "10 ok ", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 0F 0006 4D5149736470 03 01 000A 0001 70", "comment":"minimal valid CONNECT"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "14 ok ", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"14 0F 0006 4D5149736470 03 01 000A 0001 70", "comment":"CONNECT with QoS=1"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "10 proto ver 2", "connect":false, "msgs":[ {"type":"send", "payload":"10 0F 0006 4D5149736470 02 00 000A 0001 70", "comment":"CONNECT"}, {"type":"recv", "payload":"20 02 00 01", "comment": "CONNACK identifier rejected"} ]}, { "name": "10 proto ver 6", "connect":false, "msgs":[ {"type":"send", "payload":"10 0F 0006 4D5149736470 06 00 000A 0001 70", "comment":"CONNECT"}, {"type":"recv", "payload":"20 02 00 01", "comment": "CONNACK identifier rejected"} ]}, { "name": "10 empty client ID", "ver":3, "connect":false, "msgs":[ {"type":"send", "payload":"10 0E 0006 4D5149736470 03 02 000A 0000", "comment":"CONNECT clean session true, no client id"}, {"type":"recv", "payload":"20 02 00 02", "comment": "CONNACK"} ]}, { "name": "10 ok", "ver":3, "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 0F 0006 4D5149736470 03 02 000A 0001 70", "comment":"CONNECT clean session true, no client id"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]} ] }, { "group": "v3.1.1 CONNECT", "tests": [ { "name": "10 ok ", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 0D 0004 4D515454 04 02 000A 0001 70", "comment":"minimal valid CONNECT"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "10 [MQTT-3.1.0-2]", "connect":false, "msgs":[ {"type":"send", "payload":"10 0D 0004 4D515454 04 02 000A 0001 70", "comment":"minimal valid CONNECT"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"}, {"type":"send", "payload":"10 0D 0004 4D515454 04 02 000A 0001 70", "comment":"minimal valid CONNECT"} ]}, { "name": "10 missing client ID", "connect":false, "msgs":[{"type":"send", "payload":"10 08 0004 4D515454 04 02 000A"}]}, { "name": "10 empty client ID", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 0C 0004 4D515454 04 02 000A 0000", "comment":"CONNECT clean session true, no client id"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "10 empty client ID clean false [MQTT-3.1.3-7]", "connect":false, "expect_disconnect":true, "msgs":[ {"type":"send", "payload":"10 0C 0004 4D515454 04 00 000A 0000", "comment":"CONNECT clean session false, no client id"}, {"type":"recv", "payload":"20 02 00 02", "comment": "CONNACK"} ]}, { "name": "10 proto ver 2 [MQTT-3.1.2-2]", "connect":false, "msgs":[ {"type":"send", "payload":"10 0D 0004 4D515454 02 00 000A 0001 70", "comment":"CONNECT"}, {"type":"recv", "payload":"20 02 00 01", "comment": "v3.1.1 CONNACK identifier rejected"} ]}, { "name": "10 proto ver 6 [MQTT-3.1.2-2]", "connect":false, "msgs":[ {"type":"send", "payload":"10 0D 0004 4D515454 06 00 000A 0001 70", "comment":"CONNECT"}, {"type":"recv", "payload":"20 02 00 01", "comment": "v3.1.1 CONNACK identifier rejected"} ]}, { "name": "10 remaining length 5 bytes", "connect":false, "msgs":[ {"type":"send", "payload":"10 FFFFFFFF7F 0004 4D515454 06 00 000A 0001 70", "comment":"CONNECT"} ]}, { "name": "11", "connect":false, "msgs":[{"type":"send", "payload":"11 0D 0004 4D515454 04 02 000A 0001 70"}]}, { "name": "12", "connect":false, "msgs":[{"type":"send", "payload":"12 0D 0004 4D515454 04 02 000A 0001 70"}]}, { "name": "14", "connect":false, "msgs":[{"type":"send", "payload":"14 0D 0004 4D515454 04 02 000A 0001 70"}]}, { "name": "18", "connect":false, "msgs":[{"type":"send", "payload":"18 0D 0004 4D515454 04 02 000A 0001 70"}]}, { "name": "10 short proto", "connect":false, "msgs":[{"type":"send", "payload":"10 0C 0003 4D5154 04 02 000A 0001 70"}]}, { "name": "10 zero proto", "connect":false, "msgs":[{"type":"send", "payload":"10 09 0000 04 02 000A 0001 70"}]}, { "name": "10 long proto", "connect":false, "msgs":[{"type":"send", "payload":"10 0E 0005 4D51545454 04 02 000A 0001 70"}]}, { "name": "10 [MQTT-3.1.2-1]", "connect":false, "msgs":[{"type":"send", "payload":"10 0D 0004 4D515455 04 02 000A 0001 70"}]}, { "name": "10 [MQTT-3.1.2-3] ", "connect":false, "msgs":[{"type":"send", "payload":"10 0D 0004 4D515454 04 01 000A 0001 70"}]}, { "name": "10 Will flag 0 Will QoS 1 [MQTT-3.1.2-11]", "connect":false, "msgs":[ {"type":"send", "payload":"10 0D 0004 4D515454 04 0A 000A 0001 70"} ]}, { "name": "10 Will flag 0 Will retain 1 [MQTT-3.1.2-11]", "connect":false, "msgs":[ {"type":"send", "payload":"10 0D 0004 4D515454 04 12 000A 0001 70"} ]}, { "name": "10 Will flag 1 no Will topic no Will message [MQTT-3.1.2-9]", "connect":false, "msgs":[ {"type":"send", "payload":"10 0D 0004 4D515454 04 06 000A 0001 70"} ]}, { "name": "10 Will flag 1 no Will topic [MQTT-3.1.2-9]", "connect":false, "msgs":[ {"type":"send", "payload":"10 10 0004 4D515454 04 06 000A 0001 70 0001 70"} ]}, { "name": "10 Will flag 1 ok", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 13 0004 4D515454 04 06 000A 0001 70 0001 70 0001 70"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "10 Will flag 1 Will Qos 3 [MQTT-3.1.2-14]", "connect":false, "msgs":[ {"type":"send", "payload":"10 13 0004 4D515454 04 1E 000A 0001 70 0001 70 0001 70"} ]}, { "name": "10 Will topic with 0x0000", "connect":false, "msgs": [{"type":"send", "payload":"10 17 0004 4D515454 04 06 000A 0001 70 0005 746F700000 0001 70"}]}, { "name": "10 Will topic with U+D800", "connect":false, "msgs": [{"type":"send", "payload":"10 17 0004 4D515454 04 06 000A 0001 70 0005 746FEDA080 0001 70"}]}, { "name": "10 Will topic with U+0001", "connect":false, "msgs": [{"type":"send", "payload":"10 17 0004 4D515454 04 06 000A 0001 70 0005 746F700170 0001 70"}]}, { "name": "10 Will topic with U+001F", "connect":false, "msgs": [{"type":"send", "payload":"10 17 0004 4D515454 04 06 000A 0001 70 0005 746F701F70 0001 70"}]}, { "name": "10 Will topic with U+007F", "connect":false, "msgs": [{"type":"send", "payload":"10 17 0004 4D515454 04 06 000A 0001 70 0005 746F707F70 0001 70"}]}, { "name": "10 Will topic with U+009F", "connect":false, "msgs": [{"type":"send", "payload":"10 17 0004 4D515454 04 06 000A 0001 70 0005 746FC29F70 0001 70"}]}, { "name": "10 Will topic with U+FFFF", "connect":false, "msgs": [{"type":"send", "payload":"10 17 0004 4D515454 04 06 000A 0001 70 0005 746FEDBFBF 0001 70"}]}, { "name": "10 Client ID with 0x0000", "connect":false, "msgs": [{"type":"send", "payload":"10 11 0004 4D515454 04 02 000A 0005 746F700000"}]}, { "name": "10 Client ID with U+D800", "connect":false, "msgs": [{"type":"send", "payload":"10 11 0004 4D515454 04 02 000A 0005 746FEDA080"}]}, { "name": "10 Client ID with U+0001", "connect":false, "msgs": [{"type":"send", "payload":"10 11 0004 4D515454 04 02 000A 0005 746F700170"}]}, { "name": "10 Client ID with U+001F", "connect":false, "msgs": [{"type":"send", "payload":"10 11 0004 4D515454 04 02 000A 0005 746F701F70"}]}, { "name": "10 Client ID with U+007F", "connect":false, "msgs": [{"type":"send", "payload":"10 11 0004 4D515454 04 02 000A 0005 746F707F70"}]}, { "name": "10 Client ID with U+009F", "connect":false, "msgs": [{"type":"send", "payload":"10 11 0004 4D515454 04 02 000A 0005 746FC29F70"}]}, { "name": "10 Client ID with U+FFFF", "connect":false, "msgs": [{"type":"send", "payload":"10 11 0004 4D515454 04 02 000A 0005 746FEDBFBF"}]}, { "name": "10 [MQTT-3.1.2-18]", "connect":false, "msgs":[{"type":"send", "payload":"10 10 0004 4D515454 04 02 000A 0001 70 0001 70"}]}, { "name": "10 [MQTT-3.1.2-19]", "connect":false, "msgs":[{"type":"send", "payload":"10 0D 0004 4D515454 04 82 000A 0001 70"}]}, { "name": "10 Username with 0x0000", "connect":false, "msgs":[{"type":"send", "payload":"10 14 0004 4D515454 04 82 000A 0001 70 0005 746F700000"}]}, { "name": "10 Username with 0xD800", "connect":false, "msgs":[{"type":"send", "payload":"10 14 0004 4D515454 04 82 000A 0001 70 0005 746FEDA080"}]}, { "name": "10 Username with 0x0001", "connect":false, "msgs":[{"type":"send", "payload":"10 14 0004 4D515454 04 82 000A 0001 70 0005 746F700170"}]}, { "name": "10 Username with 0x001F", "connect":false, "msgs":[{"type":"send", "payload":"10 14 0004 4D515454 04 82 000A 0001 70 0005 746F701F70"}]}, { "name": "10 Username with 0x007F", "connect":false, "msgs":[{"type":"send", "payload":"10 14 0004 4D515454 04 82 000A 0001 70 0005 746F707F70"}]}, { "name": "10 Username with 0x009F", "connect":false, "msgs":[{"type":"send", "payload":"10 14 0004 4D515454 04 82 000A 0001 70 0005 746FC29F70"}]}, { "name": "10 Username with 0xFFFF", "connect":false, "msgs":[{"type":"send", "payload":"10 14 0004 4D515454 04 82 000A 0001 70 0005 746FEDBFBF"}]}, { "name": "10 Username zero length ok", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 0F 0004 4D515454 04 82 000A 0001 70 0000"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "10 Username flag 1 Password flag 1 ok", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 13 0004 4D515454 04 C2 000A 0001 70 0001 70 0001 70"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "10 [MQTT-3.1.2-20]", "connect":false, "msgs":[{"type":"send", "payload":"10 13 0004 4D515454 04 82 000A 0001 70 0001 70 0001 70"}]}, { "name": "10 [MQTT-3.1.2-21]", "connect":false, "msgs":[{"type":"send", "payload":"10 10 0004 4D515454 04 C2 000A 0001 70 0001 70"}]}, { "name": "10 [MQTT-3.1.2-22]", "connect":false, "msgs":[{"type":"send", "payload":"10 10 0004 4D515454 04 42 000A 0001 70 0001 70"}]}, { "name": "10 Password with 0x0000", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 17 00 04 4D515454 04 C2 000A 0001 70 0001 70 0005 746F700000"}, {"type":"recv", "payload":"20 02 00 00", "comment": "CONNACK"} ]}, { "name": "duplicate CONNECT", "msgs":[{"type":"send", "payload":"10 0D 0004 4D515454 04 02 000A 0001 70", "comment":"minimal valid duplicate CONNECT"}]}, { "name": "NanoMQ CWE-119", "msgs":[{"type":"send", "payload":"10 07 0004 4D515454 04 C2 003C 000B 746573742D707974686F6E 0005 61646d696E 0008 70617373776F7264"}]} ] }, { "group": "v5.0 CONNECT", "tests": [ { "name": "10 ok ", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 0E 0004 4D515454 05 02 000A 00 0001 70", "comment":"minimal valid CONNECT"}, {"type":"recv", "payload":"20 09 00 00 06 22000A 210014", "comment": "CONNACK"} ]}, { "name": "10 Username flag 1 ok", "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 11 0004 4D515454 05 82 000A 00 0001 70 0001 70"}, {"type":"recv", "payload":"20 09 00 00 06 22000A 210014", "comment": "CONNACK"} ]}, { "name": "10 Client ID with 0x0000", "connect":false, "msgs": [ {"type":"send", "payload":"10 12 0004 4D515454 05 02 000A 00 0005 746F700000"} ]}, { "name": "10 Client ID with U+D800", "connect":false, "msgs": [ {"type":"send", "payload":"10 12 0004 4D515454 05 02 000A 00 0005 746FEDA080"} ]}, { "name": "10 Client ID with U+0001", "connect":false, "msgs": [ {"type":"send", "payload":"10 12 0004 4D515454 05 02 000A 00 0005 746F700170"} ]}, { "name": "10 Client ID with U+001F", "connect":false, "msgs": [ {"type":"send", "payload":"10 12 0004 4D515454 05 02 000A 00 0005 746F701F70"} ]}, { "name": "10 Client ID with U+007F", "connect":false, "msgs": [ {"type":"send", "payload":"10 12 0004 4D515454 05 02 000A 00 0005 746F707F70"} ]}, { "name": "10 Client ID with U+009F", "connect":false, "msgs": [ {"type":"send", "payload":"10 12 0004 4D515454 05 02 000A 00 0005 746FC29F70"} ]}, { "name": "10 Client ID with U+FFFF", "connect":false, "msgs": [ {"type":"send", "payload":"10 12 0004 4D515454 05 02 000A 00 0005 746FEDBFBF"} ]}, { "name": "10 [MQTT-3.1.2-16]", "connect":false, "msgs":[ {"type":"send", "payload":"10 11 0004 4D515454 05 02 000A 00 0001 70 0001 70"} ]}, { "name": "10 [MQTT-3.1.2-17]", "connect":false, "msgs":[ {"type":"send", "payload":"10 0E 0004 4D515454 05 82 000A 00 0001 70"} ]}, { "name": "10 Username with 0x0000", "connect":false, "msgs":[ {"type":"send", "payload":"10 15 0004 4D515454 05 82 000A 00 0001 70 0005 746F700000"} ]}, { "name": "10 Username with 0xD800", "connect":false, "msgs":[ {"type":"send", "payload":"10 15 0004 4D515454 05 82 000A 00 0001 70 0005 746FEDA080"} ]}, { "name": "10 Username with 0x0001", "connect":false, "msgs":[ {"type":"send", "payload":"10 15 0004 4D515454 05 82 000A 00 0001 70 0005 746F700170"} ]}, { "name": "10 Username with 0x001F", "connect":false, "msgs":[ {"type":"send", "payload":"10 15 0004 4D515454 05 82 000A 00 0001 70 0005 746F701F70"} ]}, { "name": "10 Username with 0x007F", "connect":false, "msgs":[ {"type":"send", "payload":"10 15 0004 4D515454 05 82 000A 00 0001 70 0005 746F707F70"} ]}, { "name": "10 Username with 0x009F", "connect":false, "msgs":[ {"type":"send", "payload":"10 15 0004 4D515454 05 82 000A 00 0001 70 0005 746FC29F70"} ]}, { "name": "10 Username with 0xFFFF", "connect":false, "msgs":[ {"type":"send", "payload":"10 15 0004 4D515454 05 82 000A 00 0001 70 0005 746FEDBFBF"} ]}, { "name": "10 [MQTT-3.1.2-18]", "connect":false, "msgs":[ {"type":"send", "payload":"10 14 0004 4D515454 05 82 000A 00 0001 70 0001 70 0001 70"} ]}, { "name": "10 [MQTT-3.1.2-19]", "connect":false, "msgs":[ {"type":"send", "payload":"10 11 0004 4D515454 05 C2 000A 00 0001 70 0001 70"} ]}, { "name": "tiny max packet", "connect":false, "msgs":[{"type":"send", "payload":"10 13 0004 4D515454 05 02 000A 05 2700000002 0001 70"}]} ] }, { "group": "v5.0 CONNECT EXTENDED AUTH", "tests": [ { "name": "unsupported authentication method", "connect":false, "msgs":[ {"type":"send", "payload":"10 23 0004 4D515454 05 02 000A 15 15000B756E737570706F7274656416000474657374 0001 70", "comment":"auth-method:unsupported, auth-data:test"}, {"type":"recv", "payload":"20 03 00 8C 00", "comment": "CONNACK Bad authentication method"} ]} ] } ] mosquitto-2.0.18/test/broker/data/DISCONNECT.json000066400000000000000000000505441450213760600213620ustar00rootroot00000000000000[ { "group": "v3.1.1 DISCONNECT", "tests": [ { "name": "E0 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"E0 00"}]}, { "name": "E0 long", "ver":4, "msgs": [{"type":"send", "payload":"E0 01 00"}]}, { "name": "E0 valid", "ver":4, "msgs": [{"type":"send", "payload":"E0 00"}]}, { "name": "E1 [MQTT-3.14.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"E1 00"}]}, { "name": "E2 [MQTT-3.14.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"E2 00"}]}, { "name": "E4 [MQTT-3.14.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"E4 00"}]}, { "name": "E8 [MQTT-3.14.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"E8 00"}]} ] }, { "group": "v5.0 DISCONNECT", "tests": [ { "name": "E0 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"E0 00"}]}, { "name": "E0 long", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 00"}]}, { "name": "E0 valid", "ver":5, "msgs": [{"type":"send", "payload":"E0 00"}]}, { "name": "E1 [MQTT-3.14.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"E1 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E2 [MQTT-3.14.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"E2 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E4 [MQTT-3.14.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"E4 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E8 [MQTT-3.14.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"E8 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 RC=0x00 (normal disconnection)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 00"}]}, { "name": "E0 RC=0x01 (qos 1 - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 01"}]}, { "name": "E0 RC=0x04 (disconnect with will)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 04"}]}, { "name": "E0 RC=0x05 (invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 05"}]}, { "name": "E0 RC=0x80 (unspecified error)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 80"}]}, { "name": "E0 RC=0x81 (malformed packet)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 81"}]}, { "name": "E0 RC=0x82 (protocol error)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 82"}]}, { "name": "E0 RC=0x83 (implementation specific error)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 83"}]}, { "name": "E0 RC=0x87 (not authorised - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 87"}]}, { "name": "E0 RC=0x89 (server busy - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 89"}]}, { "name": "E0 RC=0x8B (server shutting down - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 8B"}]}, { "name": "E0 RC=0x8D (keep alive timeout - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 8D"}]}, { "name": "E0 RC=0x8E (session taken over - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 8E"}]}, { "name": "E0 RC=0x8F (topic filter invalid - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 8F"}]}, { "name": "E0 RC=0x90 (topic name invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 90"}]}, { "name": "E0 RC=0x93 (receive maximum exceeded)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 93"}]}, { "name": "E0 RC=0x94 (topic alias invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 94"}]}, { "name": "E0 RC=0x95 (packet too large)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 95"}]}, { "name": "E0 RC=0x96 (message rate too high)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 96"}]}, { "name": "E0 RC=0x97 (quota exceeded)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 97"}]}, { "name": "E0 RC=0x98 (administrative action)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 98"}]}, { "name": "E0 RC=0x99 (payload format invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 99"}]}, { "name": "E0 RC=0x9A (retain not supported - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 9A"}]}, { "name": "E0 RC=0x9B (qos not supported - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 9B"}]}, { "name": "E0 RC=0x9C (use another server - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 9C"}]}, { "name": "E0 RC=0x9D (server moved - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 9D"}]}, { "name": "E0 RC=0x9E (shared subs not supported - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 9E"}]}, { "name": "E0 RC=0x9F (connection rate exceeded - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 9F"}]}, { "name": "E0 RC=0xA0 (maximum connect time - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 A0"}]}, { "name": "E0 RC=0xA1 (subscription ids not supported - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 A1"}]}, { "name": "E0 RC=0xA2 (wildcard subs not supported - invalid)", "ver":5, "msgs": [{"type":"send", "payload":"E0 01 A2"}]}, { "name": "E0 RC=0x82 PL=0", "ver":5, "msgs": [{"type":"send", "payload":"E0 02 82 00"}]}, { "name": "E0 RC=0x00 PL=1 P=0", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 RC=0x00 PL=1 P=0x11", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 11"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 RC=0x00 PL=2 P=0x11", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 1100"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 RC=0x00 PL=3 P=0x11", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 110000"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 RC=0x00 PL=4 P=0x11", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 11000000"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 RC=0x00 PL=5 P=0x11", "ver":5, "msgs": [ {"type":"send", "payload":"E0 07 00 05 1100000000"} ]}, { "name": "E0 non-zero session expiry", "ver":5, "connect":false, "msgs": [ {"type":"send", "payload":"101300044D5154540502000A051100000000000170", "comment":"CONNECT with session expiry=0"}, {"type":"recv", "payload":"200900000622000A210014", "comment": "CONNACK"}, {"type":"send", "payload":"E0 07 00 05 1100000001"}, {"type":"recv", "payload":"E0 01 82"} ]} ] }, { "group": "v5.0 DISCONNECT ALLOWED PROPERTIES", "tests": [ { "name": "E0 with reason-string property", "ver":5, "msgs": [{"type":"send", "payload":"E0 06 00 04 1F000170"}]}, { "name": "E0 with 2*reason-string property (invalid)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 0A 00 08 1F000170 1F000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 1F"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with user-property", "ver":5, "msgs": [{"type":"send", "payload":"E0 09 00 07 26000170000171"}]}, { "name": "E0 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 23000170"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [{"type":"send", "payload":"E0 07 00 05 1100000000"}]}, { "name": "E0 with 2*session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 0C 00 0A 1100000000 1100000000"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 11"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with server-reference (UTF-8 string)", "ver":5, "msgs": [{"type":"send", "payload":"E0 06 00 04 1C000170"}]}, { "name": "E0 with 2*server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 0A 00 08 1C000170 1C000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 1C"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 DISCONNECT DISALLOWED PROPERTIES", "tests": [ { "name": "E0 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0100"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 1700"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 2400"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 2500"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 2800"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 2900"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 2A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 17"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 24"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 25"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 28"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 29"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 2A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 07 00 05 0200000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 07 00 05 1800000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 07 00 05 2700000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 02"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 18"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 27"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 03000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 08000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 12000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 15000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 1A000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 03"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 08"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 12"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 15"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 1A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 09000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 16000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 0109"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 0116"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 0B"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 130101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 210101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 220101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 230101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "E0 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 13"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 21"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 22"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"E0 03 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0501"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0601"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0701"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0A01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0C01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 0F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 1001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 1401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 1B01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 1D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 1E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 2001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 04 00 02 7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 800001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 05 00 03 FF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 80800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 06 00 04 FFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 07 00 05 8080800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "E0 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"E0 07 00 05 FFFFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/FLOW.json000066400000000000000000000332551450213760600205000ustar00rootroot00000000000000[ { "comment": "FLOW TESTS ARE INCOMPLETE", "group": "v3.1.1 FLOW", "tests": [ { "name": "QoS 0 self receive ok", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 03 1234 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 0A 0001 70 6d657373616765", "comment":"PUBLISH send"}, {"type":"recv", "payload":"30 0A 0001 70 6d657373616765", "comment":"PUBLISH receive"} ]}, { "name": "QoS 1 receive ok", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 03 1234 01", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":1, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"32 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"40 02 00 01", "comment":"PUBACK"} ]}, { "name": "QoS 1 PUBLISH-PUBREC", "ver":4, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 03 1234 01", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":1, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"32 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment":"PUBREC"} ]}, { "name": "QoS 1 PUBLISH-PUBCOMP", "ver":4, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 03 1234 01", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":1, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"32 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"70 02 0001", "comment":"PUBCOMP"} ]}, { "name": "QoS 2 receive ok", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 03 1234 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment":"PUBREC"}, {"type":"recv", "payload":"62 02 0001", "comment":"PUBREL"}, {"type":"send", "payload":"70 02 0001", "comment":"PUBCOMP"} ]}, { "name": "QoS 2 PUBLISH-PUBACK", "ver":4, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 03 1234 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"40 02 0001", "comment": "PUBACK (should be PUBREC)"} ]}, { "name": "QoS 2 PUBLISH-PUBCOMP", "ver":4, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 03 1234 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"70 02 0001", "comment": "PUBCOMP (should be PUBREC)"} ]}, { "name": "QoS 2 PUBLISH-PUBREC-PUBREL-PUBACK", "ver":4, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 03 1234 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment": "PUBREC)"}, {"type":"recv", "payload":"62 02 0001", "comment": "PUBREL)"}, {"type":"send", "payload":"40 02 0001", "comment": "PUBACK (should be PUBCOMP))"} ]}, { "name": "QoS 2 PUBLISH-PUBREC-PUBREL-PUBREC", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 03 1234 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0C 0001 70 0001 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment": "PUBREC)"}, {"type":"recv", "payload":"62 02 0001", "comment": "PUBREL)"}, {"type":"send", "payload":"50 02 0001", "comment": "PUBREC (should be PUBCOMP))"}, {"type":"recv", "payload":"62 02 0001", "comment": "PUBREL)"} ]} ] }, { "group": "v5.0 FLOW", "tests": [ { "name": "QoS 0 self receive ok", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 0B 0001 70 00 6d657373616765", "comment":"PUBLISH send"}, {"type":"recv", "payload":"30 0B 0001 70 00 6d657373616765", "comment":"PUBLISH receive"} ]}, { "name": "QoS 1 receive ok", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":1, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"32 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"40 02 0001", "comment":"PUBACK"} ]}, { "name": "QoS 1 PUBLISH-PUBREC", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":1, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"32 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment":"PUBREC"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "QoS 1 PUBLISH-PUBCOMP", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 01", "comment":"SUBSCRIBE, 'p' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":1, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"32 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"70 02 0001", "comment":"PUBCOMP"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "QoS 2 receive ok", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 04 1234 00 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment":"PUBREC"}, {"type":"recv", "payload":"62 02 0001", "comment":"PUBREL"}, {"type":"send", "payload":"70 02 0001", "comment":"PUBCOMP"} ]}, { "name": "QoS 2 PUBLISH-PUBACK", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 04 1234 00 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"40 02 0001", "comment": "PUBACK (should be PUBREC)"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "QoS 2 PUBLISH-PUBCOMP", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 04 1234 00 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"70 02 0001", "comment": "PUBCOMP (should be PUBREC)"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "QoS 2 PUBLISH-PUBREC-PUBREL-PUBACK", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 04 1234 00 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment": "PUBREC)"}, {"type":"recv", "payload":"62 02 0001", "comment": "PUBREL)"}, {"type":"send", "payload":"40 02 0001", "comment": "PUBACK (should be PUBCOMP))"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "QoS 2 PUBLISH-PUBREC-PUBREL-PUBREC", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 02", "comment":"SUBSCRIBE, 'p' qos2"}, {"type":"recv", "payload":"90 04 1234 00 02", "comment":"SUBACK"}, {"type":"publish", "topic":"p", "qos":2, "payload":"message", "comment":"helper"}, {"type":"recv", "payload":"34 0D 0001 70 0001 00 6d657373616765", "comment":"PUBLISH receive"}, {"type":"send", "payload":"50 02 0001", "comment": "PUBREC)"}, {"type":"recv", "payload":"62 02 0001", "comment": "PUBREL)"}, {"type":"send", "payload":"50 02 0001", "comment": "PUBREC (should be PUBCOMP))"}, {"type":"recv", "payload":"62 02 0001", "comment": "PUBREL)"} ]} ] }, { "group": "v5.0 FLOW WITH PROPERTIES", "tests": [ { "name": "payload-format-indicator=1 (byte)", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 11 0005 746F706963 02 0101 7061796C6F6164", "comment": "PUBLISH send"}, {"type":"recv", "payload":"30 11 0005 746F706963 02 0101 7061796C6F6164", "comment": "PUBLISH recv"} ]}, { "name": "message-expiry-interval=1 (four byte integer)", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 14 0005 746F706963 05 0200000001 7061796C6F6164"}, {"type":"recv", "payload":"30 14 0005 746F706963 05 0200000001 7061796C6F6164"} ]}, { "name": "topic-alias", "expect_disconnect":false, "ver":5, "comment":"broker doesn't initiate topic alias", "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 12 0005 746F706963 03 230001 7061796C6F6164", "comment":"PUBLISH with topic alias 1"}, {"type":"recv", "payload":"30 0F 0005 746F706963 00 7061796C6F6164", "comment":"PUBLISH receive 1"}, {"type":"send", "payload":"30 0D 0000 03 230001 7061796C6F6164", "comment":"PUBLISH with topic alias 1, no topic"}, {"type":"recv", "payload":"30 0F 0005 746F706963 00 7061796C6F6164", "comment":"PUBLISH receive 2"} ]}, { "name": "response-topic", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 13 0005 746F706963 04 08000170 7061796C6F6164"}, {"type":"recv", "payload":"30 13 0005 746F706963 04 08000170 7061796C6F6164"} ]}, { "name": "correlation-data", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 13 0005 746F706963 04 09000170 7061796C6F6164"}, {"type":"recv", "payload":"30 13 0005 746F706963 04 09000170 7061796C6F6164"} ]}, { "name": "user-property", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 16 0005 746F706963 07 26000170000171 7061796C6F6164"}, {"type":"recv", "payload":"30 16 0005 746F706963 07 26000170000171 7061796C6F6164"} ]}, { "name": "subscription-identifier", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"82 0D 1234 02 0B01 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 0F 0005 746F706963 00 7061796C6F6164"}, {"type":"recv", "payload":"30 11 0005 746F706963 02 0B01 7061796C6F6164"} ]}, { "name": "content-type", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 01", "comment":"SUBSCRIBE, 'topic' qos1"}, {"type":"recv", "payload":"90 04 1234 00 01", "comment":"SUBACK"}, {"type":"send", "payload":"30 13 0005 746F706963 04 03000170 7061796C6F6164"}, {"type":"recv", "payload":"30 13 0005 746F706963 04 03000170 7061796C6F6164"} ]} ] } ] mosquitto-2.0.18/test/broker/data/FORBIDDEN.json000066400000000000000000000051111450213760600212130ustar00rootroot00000000000000[ { "group": "v3.1.1 FORBIDDEN", "tests": [ { "name": "00 first packet", "ver":4, "connect": false, "msgs": [{"type":"send", "payload":"00 00"}]}, { "name": "01 first packet", "ver":4, "connect": false, "msgs": [{"type":"send", "payload":"01 00"}]}, { "name": "02 first packet", "ver":4, "connect": false, "msgs": [{"type":"send", "payload":"02 00"}]}, { "name": "04 first packet", "ver":4, "connect": false, "msgs": [{"type":"send", "payload":"04 00"}]}, { "name": "08 first packet", "ver":4, "connect": false, "msgs": [{"type":"send", "payload":"08 00"}]}, { "name": "00 long", "ver":4, "msgs": [{"type":"send", "payload":"00 01 00"}]}, { "name": "00", "ver":4, "msgs": [{"type":"send", "payload":"00 00"}]}, { "name": "01", "ver":4, "msgs": [{"type":"send", "payload":"01 00"}]}, { "name": "02", "ver":4, "msgs": [{"type":"send", "payload":"02 00"}]}, { "name": "04", "ver":4, "msgs": [{"type":"send", "payload":"04 00"}]}, { "name": "08", "ver":4, "msgs": [{"type":"send", "payload":"08 00"}]} ] }, { "group": "v5.0 FORBIDDEN", "tests": [ { "name": "00 first packet", "ver":5, "connect": false, "msgs": [{"type":"send", "payload":"00 00"}]}, { "name": "01 first packet", "ver":5, "connect": false, "msgs": [{"type":"send", "payload":"01 00"}]}, { "name": "02 first packet", "ver":5, "connect": false, "msgs": [{"type":"send", "payload":"02 00"}]}, { "name": "04 first packet", "ver":5, "connect": false, "msgs": [{"type":"send", "payload":"04 00"}]}, { "name": "08 first packet", "ver":5, "connect": false, "msgs": [{"type":"send", "payload":"08 00"}]}, { "name": "00 long", "ver":5, "msgs": [ {"type":"send", "payload":"00 01 00"}, {"type":"recv", "payload":"E0 01 82", "comment":"DISCONNECT protocol error"} ]}, { "name": "00", "ver":5, "msgs": [ {"type":"send", "payload":"00 00"}, {"type":"recv", "payload":"E0 01 82", "comment":"DISCONNECT protocol error"} ]}, { "name": "01", "ver":5, "msgs": [ {"type":"send", "payload":"01 00"}, {"type":"recv", "payload":"E0 01 82", "comment":"DISCONNECT protocol error"} ]}, { "name": "02", "ver":5, "msgs": [ {"type":"send", "payload":"02 00"}, {"type":"recv", "payload":"E0 01 82", "comment":"DISCONNECT protocol error"} ]}, { "name": "04", "ver":5, "msgs": [ {"type":"send", "payload":"04 00"}, {"type":"recv", "payload":"E0 01 82", "comment":"DISCONNECT protocol error"} ]}, { "name": "08", "ver":5, "msgs": [ {"type":"send", "payload":"08 00"}, {"type":"recv", "payload":"E0 01 82", "comment":"DISCONNECT protocol error"} ]} ] } ] mosquitto-2.0.18/test/broker/data/PINGREQ.json000066400000000000000000000030571450213760600210330ustar00rootroot00000000000000[ { "group": "v3.1.1 PINGREQ", "tests": [ { "name": "C0 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"C0 00"}]}, { "name": "C0 long", "ver":4, "msgs": [{"type":"send", "payload":"C00100"}]}, { "name": "C0 valid", "ver":4, "expect_disconnect": false, "msgs": [{"type":"send", "payload":"C0 00"}, {"type":"recv", "payload":"D0 00"}]}, { "name": "C1", "ver":4, "msgs": [{"type":"send", "payload":"C1 00"}]}, { "name": "C2", "ver":4, "msgs": [{"type":"send", "payload":"C2 00"}]}, { "name": "C4", "ver":4, "msgs": [{"type":"send", "payload":"C4 00"}]}, { "name": "C8", "ver":4, "msgs": [{"type":"send", "payload":"C8 00"}]} ] }, { "group": "v5.0 PINGREQ", "tests": [ { "name": "C0 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"C0 00"}]}, { "name": "C0 long", "ver":5, "msgs": [{"type":"send", "payload":"C0 01 00"}]}, { "name": "C0 valid", "ver":5, "expect_disconnect": false, "msgs": [{"type":"send", "payload":"C0 00"}, {"type":"recv", "payload":"D0 00"}]}, { "name": "C1", "ver":5, "msgs": [ {"type":"send", "payload":"C1 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "C2", "ver":5, "msgs": [ {"type":"send", "payload":"C2 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "C4", "ver":5, "msgs": [ {"type":"send", "payload":"C4 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "C8", "ver":5, "msgs": [ {"type":"send", "payload":"C8 00"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/PINGRESP.json000066400000000000000000000027351450213760600211570ustar00rootroot00000000000000[ { "group": "v3.1.1 PINGRESP", "tests": [ { "name": "D0 [MQTT-3.1.0-1]", "ver":4, "connect": false, "msgs": [{"type":"send", "payload":"D0 00"}]}, { "name": "D0 long", "ver":4, "msgs": [{"type":"send", "payload":"D0 01 00"}]}, { "name": "D0", "ver":4, "msgs": [{"type":"send", "payload":"D0 00"}]}, { "name": "D1", "ver":4, "msgs": [{"type":"send", "payload":"D1 00"}]}, { "name": "D2", "ver":4, "msgs": [{"type":"send", "payload":"D2 00"}]}, { "name": "D4", "ver":4, "msgs": [{"type":"send", "payload":"D4 00"}]}, { "name": "D8", "ver":4, "msgs": [{"type":"send", "payload":"D8 00"}]} ] }, { "group": "v5.0 PINGRESP", "tests": [ { "name": "D0 [MQTT-3.1.0-1]", "ver":5, "connect": false, "msgs": [{"type":"send", "payload":"D0 00"}]}, { "name": "D0 long", "ver":5, "msgs": [{"type":"send", "payload":"D0 01 00"}]}, { "name": "D0", "ver":5, "msgs": [ {"type":"send", "payload":"D0 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "D1", "ver":5, "msgs": [ {"type":"send", "payload":"D1 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "D2", "ver":5, "msgs": [ {"type":"send", "payload":"D2 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "D4", "ver":5, "msgs": [ {"type":"send", "payload":"D4 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "D8", "ver":5, "msgs": [ {"type":"send", "payload":"D8 00"}, {"type":"recv", "payload":"E0 01 82"} ]} ] } ] mosquitto-2.0.18/test/broker/data/PUBACK.json000066400000000000000000000434061450213760600206750ustar00rootroot00000000000000[ { "group": "v3.1.1 PUBACK", "tests": [ { "name": "40 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"40 02 0001"}]}, { "name": "40 unsolicited long", "ver":4, "msgs": [{"type":"send", "payload":"40 03 0001 00"}]}, { "name": "40 unsolicited mid 0", "ver":4, "msgs": [{"type":"send", "payload":"40 02 0000"}]}, { "name": "40 unsolicited short 0", "ver":4, "msgs": [{"type":"send", "payload":"40 00"}]}, { "name": "40 unsolicited short 1", "ver":4, "msgs": [{"type":"send", "payload":"40 01 01"}]}, { "name": "40 unsolicited", "ver":4, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 02 0001"}]}, { "name": "41 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"41 02 0001"}]}, { "name": "42 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"42 02 0001"}]}, { "name": "44 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"44 02 0001"}]}, { "name": "48 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"48 02 0001"}]} ] }, { "group": "v5.0 PUBACK", "tests": [ { "name": "40 [MQTT-3.1.0-1] (no reason code)", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"40 02 0001"}]}, { "name": "40 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"40 03 0001 00"}]}, { "name": "40 unsolicited long", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 unsolicited mid 0", "ver":5, "msgs": [ {"type":"send", "payload":"40 03 0000 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 unsolicited short 0", "ver":5, "msgs": [ {"type":"send", "payload":"40 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 unsolicited short 1", "ver":5, "msgs": [ {"type":"send", "payload":"40 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 unsolicited len=2", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 02 0001"}]}, { "name": "40 unsolicited len=3", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 03 0001 00"}]}, { "name": "40 unsolicited len=3 fail", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 03 0001 80"}]}, { "name": "40 unsolicited len=4 ok", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 04 0001 00 00"}]}, { "name": "40 unsolicited len=4 rc=fail", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 04 0001 80 00"}]}, { "name": "40 unsolicited len=4 rc=unknown", "ver":5, "msgs": [ {"type":"send", "payload":"40 04 0001 FF 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 unsolicited len=4 short", "ver":5, "msgs": [ {"type":"send", "payload":"40 04 0001 00 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "41 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"41 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "42 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"42 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "44 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"44 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "48 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"48 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBACK ALLOWED PROPERTIES", "tests": [ { "name": "40 with reason-string property", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 08 0001 00 04 1F000170"}]}, { "name": "40 with 2*reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"40 0C 0001 00 08 1F000170 1F000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 1F"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with user-property", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 0B 0001 00 07 26000170000171"}]}, { "name": "40 with 2*user-property", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"40 12 0001 00 0E 26000170000171 26000170000171"}]}, { "name": "40 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 23000170"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBACK DISALLOWED PROPERTIES", "tests": [ { "name": "40 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0100"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 1700"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 2400"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 2500"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 2800"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 2900"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 2A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 17"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 24"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 25"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 28"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 29"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 2A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 09 0001 00 05 0200000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 09 0001 00 05 1100000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 09 0001 00 05 1800000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 09 0001 00 05 2700000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 02"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 11"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 18"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 27"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 03000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 08000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 12000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 15000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 1A000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 1C000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 03"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 08"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 12"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 15"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 1A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 1C"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 09000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 16000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 09"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 16"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 0B"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 07 0001 00 03 130101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 07 0001 00 03 210101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 07 0001 00 03 220101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"40 07 0001 00 03 230101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "40 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 13"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 21"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 22"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"40 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0501"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0601"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0701"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0A01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0C01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 0F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 1001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 1401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 1B01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 1D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 1E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 2001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 06 0001 00 02 7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 07 0001 00 03 800001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 07 0001 00 03 800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 07 0001 00 03 FF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 80800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 08 0001 00 04 FFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 09 0001 00 05 8080800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "40 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"40 09 0001 00 05 FFFFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/PUBCOMP.json000066400000000000000000000456011450213760600210340ustar00rootroot00000000000000[ { "group": "v3.1.1 PUBCOMP", "tests": [ { "name": "70 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"70 02 0001"}]}, { "name": "70 unsolicited long", "ver":4, "msgs": [{"type":"send", "payload":"70 03 0001 00"}]}, { "name": "70 unsolicited mid 0", "ver":4, "msgs": [{"type":"send", "payload":"70 02 0000"}]}, { "name": "70 unsolicited short 0", "ver":4, "msgs": [{"type":"send", "payload":"70 00"}]}, { "name": "70 unsolicited short 1", "ver":4, "msgs": [{"type":"send", "payload":"70 01 01"}]}, { "name": "70 unsolicited", "ver":4, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 02 0001"}]}, { "name": "71 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"71 02 0001"}]}, { "name": "72 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"72 02 0001"}]}, { "name": "74 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"74 02 0001"}]}, { "name": "78 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"78 02 0001"}]} ] }, { "group": "v5.0 PUBCOMP", "tests": [ { "name": "70 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"70 02 0001"}]}, { "name": "70 unsolicited long", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 unsolicited mid 0", "ver":5, "msgs": [ {"type":"send", "payload":"70 02 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 unsolicited short 0", "ver":5, "msgs": [ {"type":"send", "payload":"70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 unsolicited short 1", "ver":5, "msgs": [ {"type":"send", "payload":"70 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 unsolicited short 3", "ver":5, "FIXME":"strictly, a short 3 should be malformed", "msgs": [ {"type":"send", "payload":"70 03 0001 80"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 unsolicited", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 02 0001"}]}, { "name": "70 unsolicited rc", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 03 0001 00"}]}, { "name": "70 unsolicited rc=92", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 03 0001 92"}]}, { "name": "70 unsolicited rc=20", "ver":5, "msgs": [ {"type":"send", "payload":"70 03 0001 20"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 unsolicited rc=FF", "ver":5, "msgs": [ {"type":"send", "payload":"70 03 0001 FF"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 unsolicited rc,properties", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 04 0001 00 00"}]}, { "name": "71 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"71 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "72 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"72 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "74 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"74 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "78 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"78 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "71 unsolicited rc", "ver":5, "msgs": [ {"type":"send", "payload":"71 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "72 unsolicited rc", "ver":5, "msgs": [ {"type":"send", "payload":"72 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "74 unsolicited rc", "ver":5, "msgs": [ {"type":"send", "payload":"74 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "78 unsolicited rc", "ver":5, "msgs": [ {"type":"send", "payload":"78 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "71 unsolicited rc,properties", "ver":5, "msgs": [ {"type":"send", "payload":"71 04 0001 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "72 unsolicited rc,properties", "ver":5, "msgs": [ {"type":"send", "payload":"72 04 0001 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "74 unsolicited rc,properties", "ver":5, "msgs": [ {"type":"send", "payload":"74 04 0001 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "78 unsolicited rc,properties", "ver":5, "msgs": [ {"type":"send", "payload":"78 04 0001 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBCOMP ALLOWED PROPERTIES", "tests": [ { "name": "70 with reason-string property", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 08 0001 00 04 1F000170"}]}, { "name": "70 with 2*reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"70 0C 0001 00 08 1F0001701 F000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 1F"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with user-property", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 0B 0001 00 07 26000170000171"}]}, { "name": "70 with 2*user-property", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"70 12 0001 00 0E 26000170000171 26000170000171"}]}, { "name": "70 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 23000170"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBCOMP DISALLOWED PROPERTIES", "tests": [ { "name": "70 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0100"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 1700"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 2400"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 2500"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 2800"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 2900"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 2A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 17"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 24"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 25"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 28"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 29"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 2A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 09 0001 00 05 0200000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 09 0001 00 05 1100000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 09 0001 00 05 1800000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 09 0001 00 05 2700000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 02"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 11"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 18"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 27"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 03000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 08000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 12000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 15000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 1A000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 1C000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 03"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 08"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 12"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 15"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 1A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 1C"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 09000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 16000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 0109"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 0116"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 0B"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 07 0001 00 03 130101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 07 0001 00 03 210101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 07 0001 00 03 220101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"70 07 0001 00 03 230101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "70 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 13"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 21"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 22"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"70 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0501"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0601"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0701"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0A01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0C01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 0F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 1001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 1401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 1B01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 1D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 1E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 2001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 06 0001 00 02 7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 07 0001 00 03 800001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 07 0001 00 03 800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 07 0001 00 03 FF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 80800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 08 0001 00 04 FFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 09 0001 00 05 8080800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "70 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"70 09 0001 00 05 FFFFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/PUBLISH.json000066400000000000000000000733271450213760600210430ustar00rootroot00000000000000[ { "group": "v3.1.1 PUBLISH", "tests": [ { "name": "30 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"30 0E 0005 746F706963 7061796C6F6164"}]}, { "name": "30", "ver":4, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"30 0E 0005 746F706963 7061796C6F6164"}]}, { "name": "31 retain 1", "ver":4, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"31 0E 0005 746F706963 7061796C6F6164"}]}, { "name": "31 retain 1 zero length", "ver":4, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"31 07 0005 746F706963"}]}, { "name": "30 topic 0", "ver":4, "msgs": [{"type":"send", "payload":"30 09 0000 7061796C6F6164"}]}, { "name": "38 QoS 0 Dup 1", "ver":4, "msgs": [{"type":"send", "payload":"38 0E 0005 746F706963 7061796C6F6164"}]}, { "name": "36 QoS 3 (no mid) [MQTT-3.3.1-4]", "ver":4, "msgs": [{"type":"send", "payload":"36 0E 0005 746F706963 7061796C6F6164"}]}, { "name": "36 QoS 3 (with mid) [MQTT-3.3.1-4]", "ver":4, "msgs": [{"type":"send", "payload":"36 10 0005 746F706963 1234 7061796C6F6164"}]}, { "name": "32 QoS 1 Mid 0", "ver":4, "msgs": [{"type":"send", "payload":"32 10 0005 746F706963 0000 7061796C6F6164"}]}, { "name": "34 QoS 2 Mid 0", "ver":4, "msgs": [{"type":"send", "payload":"34 10 0005 746F706963 0000 7061796C6F6164"}]}, { "name": "32 QoS 1 Dup 0", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 10 0005 746F706963 1234 7061796C6F6164"}, {"type":"recv", "payload":"40 02 1234"} ]}, { "name": "3A QoS 1 Dup 1", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"3A 10 0005 746F706963 1234 7061796C6F6164"}, {"type":"recv", "payload":"40 02 1234"} ]}, { "name": "34 QoS 2 Dup 0", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"34 10 0005 746F706963 1234 7061796C6F6164"}, {"type":"recv", "payload":"50 02 1234"}, {"type":"send", "payload":"62 02 1234"}, {"type":"recv", "payload":"70 02 1234"} ]}, { "name": "3C QoS 2 Dup 1", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"3C 10 0005 746F706963 1234 7061796C6F6164"}, {"type":"recv", "payload":"50 02 1234"}, {"type":"send", "payload":"62 02 1234"}, {"type":"recv", "payload":"70 02 1234"} ]}, { "name": "30 topic with 0x0000", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 746F700000 7061796C6F6164"}]}, { "name": "30 topic with U+D800", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 746FEDA080 7061796C6F6164"}]}, { "name": "30 topic with U+0001", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 746F700170 7061796C6F6164"}]}, { "name": "30 topic with U+001F", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 746F701F70 7061796C6F6164"}]}, { "name": "30 topic with U+007F", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 746F707F70 7061796C6F6164"}]}, { "name": "30 topic with U+009F", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 746FC29F70 7061796C6F6164"}]}, { "name": "30 topic with U+FFFF", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 746FEDBFBF 7061796C6F6164"}]}, { "name": "30 topic with U+2A6D4 (section 1.5.3.1)", "ver":4, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"30 0E 0005 41F0AA9B94 7061796C6F6164"}]}, { "name": "30 topic with + [MQTT-3.3.2-2]", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 2B6F706963 7061796C6F6164"}]}, { "name": "30 topic with # [MQTT-3.3.2-2]", "ver":4, "msgs": [{"type":"send", "payload":"30 0E 0005 236F706963 7061796C6F6164"}]} ] }, { "group": "v5.0 PUBLISH", "tests": [ { "name": "30 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"30 0F 0005 746F706963 00 7061796C6F6164"}]}, { "name": "30", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"30 0F 0005 746F706963 00 7061796C6F6164"}]}, { "name": "31 retain 1", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"31 0F 0005 746F706963 00 7061796C6F6164"}]}, { "name": "31 retain 1 zero length", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"31 08 0005 746F706963 00"}]}, { "name": "30 topic 0", "ver":5, "msgs": [ {"type":"send", "payload":"30 0A 0000 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "38 QoS 0 Dup 1", "ver":5, "msgs": [ {"type":"send", "payload":"38 0F 0005 746F706963 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "36 QoS 3 (no mid) [MQTT-3.3.1-4]", "ver":5, "msgs": [ {"type":"send", "payload":"36 0F 0005 746F706963 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "36 QoS 3 (with mid) [MQTT-3.3.1-4]", "ver":5, "msgs": [ {"type":"send", "payload":"3611 0005 746F706963 1234 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "32 QoS 1 Mid 0", "ver":5, "msgs": [ {"type":"send", "payload":"32 11 0005 746F706963 0000 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "34 QoS 2 Mid 0", "ver":5, "msgs": [ {"type":"send", "payload":"34 11 0005 746F706963 0000 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "32 QoS 1 Dup 0", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 11 0005 746F706963 1234 00 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "3A QoS 1 Dup 1", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"3A11 0005 746F706963 1234 00 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "34 QoS 2 Dup 0", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"34 11 0005 746F706963 1234 00 7061796C6F6164"}, {"type":"recv", "payload":"50 02 1234"}, {"type":"send", "payload":"62 02 1234"}, {"type":"recv", "payload":"70 02 1234"} ]}, { "name": "3C QoS 2 Dup 1", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"3C 11 0005 746F706963 1234 00 7061796C6F6164"}, {"type":"recv", "payload":"50 02 1234"}, {"type":"send", "payload":"62 02 1234"}, {"type":"recv", "payload":"70 02 1234"} ]}, { "name": "30 topic with 0x0000", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 746F700000 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with U+D800", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 746FEDA080 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with U+0001", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 746F700170 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with U+001F", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 746F701F70 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with U+007F", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 746F707F70 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with U+009F", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 746FC29F70 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with U+FFFF", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 746FEDBFBF 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with U+2A6D4 (section 1.5.3.1)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"30 0F 0005 41F0AA9B94 00 7061796C6F6164"} ]}, { "name": "30 topic with + [MQTT-3.3.2-2]", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 2B6F706963 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "30 topic with # [MQTT-3.3.2-2]", "ver":5, "msgs": [ {"type":"send", "payload":"30 0F 0005 236F706963 00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBLISH ALLOWED PROPERTIES", "tests": [ { "name": "maximum packet size", "ver":5, "connect":false, "expect_disconnect":false, "msgs":[ {"type":"send", "payload":"10 13 0004 4D515454 05 02 000A 05 2700000014 0001 70", "comment":"CONNECT with max-packet-size 20"}, {"type":"recv", "payload":"20 09 00 00 06 22000A210014", "comment": "CONNACK"}, {"type":"send", "payload":"82 0B 1234 00 0005 746F706963 00", "comment":"SUBSCRIBE topic"}, {"type":"recv", "payload":"90 04 1234 00 00", "comment":"SUBACK"}, {"type":"send", "payload":"30 16 0005 746F706963 00 7061796C6F61647061796C6F6164", "comment":"PUBLISH with size > 20"}, {"type":"send", "payload":"30 0F 0005 746F706963 00 7061796C6F6164", "comment":"PUBLISH with size < 20"}, {"type":"recv", "payload":"30 0F 0005 746F706963 00 7061796C6F6164", "comment":"PUBLISH with size < 20, returned"} ]}, { "name": "payload-format-indicator=0 (byte)", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0100 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "payload-format-indicator=1 (byte)", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0101 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "payload-format-indicator=2 (byte, invalid)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0102 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "2*payload-format-indicator=1 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 0101 0101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "message-expiry-interval=0 (four byte integer)", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 0200000000 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "message-expiry-interval=1 (four byte integer)", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 0200000001 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "2*message-expiry-interval=1 (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 1A 0005 746F706963 1234 0A 0200000001 0200000001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 02 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "topic alias > max topic alias", "ver":5, "msgs": [ {"type":"send", "payload":"30 12 0005 746F706963 03 23000B 7061796C6F6164", "comment":"PUBLISH with topic alias 11 (server has set max topic alias=10)"}, {"type":"recv", "payload":"E0 01 94"} ]}, { "name": "topic-alias (two byte integer)", "expect_disconnect":false, "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 230001 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "2*topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 17 0005 746F706963 1234 06 230001 230001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "2*topic-alias different (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 17 0005 746F706963 1234 06 230001 230002 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 23 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "response-topic (UTF-8 string)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 08000170 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "2*response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"32 19 0005 746F706963 1234 08 08000170 08000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 08 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "correlation-data (binary data)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 09000170 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "2*correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"32 19 0005 746F706963 1234 08 09000170 09000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 09 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 18 0005 746F706963 1234 07 26000170000171 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "2*user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 1F 0005 746F706963 1234 0E 26000170000171 26000170000171 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 26000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 26 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "subscription-identifier=1 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0B01 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0x7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0B7F 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0x8000 (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 0B8000 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "subscription-identifier=0x8001 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 0B8001 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0xFF7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 0BFF7F 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0x808001 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 0B808001 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0xFFFF7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 0BFFFF7F 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0x80808001 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 0B80808001 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0xFFFFFF7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 0BFFFFFF7F 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "subscription-identifier=0x8080808001 (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 17 0005 746F706963 1234 06 0B8080808001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "2*subscription-identifier=1 (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 0B01 0B01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 0B 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "content-type (UTF-8 string)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 03000170 7061796C6F6164"}, {"type":"recv", "payload":"40 03 1234 10"} ]}, { "name": "2*content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"32 19 0005 746F706963 1234 08 03000170 03000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 03 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBLISH DISALLOWED PROPERTIES", "tests": [ { "name": "reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 1F000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 1700 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 2400 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 2500 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 2800 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 2900 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 2A00 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 17 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 24 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 25 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 28 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 29 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 2A7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 1100000001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 1800000001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 2700000001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 04 11 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 04 18 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 04 27 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 12000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 15000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 1A000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 1C000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 12 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 15 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 1A7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 1C7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 16000170 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 16 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 130101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 210101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 220101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 13 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 21 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"32 12 0005 746F706963 1234 01 22 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0401 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0501 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0601 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0701 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0A01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0C01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0D01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0E01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 0F01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 1001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 1401 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 1B01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 1D01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 1E01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 2001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 13 0005 746F706963 1234 02 7F01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 800001 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 800101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 14 0005 746F706963 1234 03 FF7F01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 80800101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 15 0005 746F706963 1234 04 FFFF7F01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 8080800101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 16 0005 746F706963 1234 05 FFFFFF7F01 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "unknown-property 0x8080808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"32 17 0005 746F706963 1234 06 808080800101 7061796C6F6164"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/PUBREC.json000066400000000000000000000442011450213760600207020ustar00rootroot00000000000000[ { "group": "v3.1.1 PUBREC", "tests": [ { "name": "50 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"50 02 0001"}]}, { "name": "50 unsolicited", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"50 02 0001"}, {"type":"recv", "payload":"62 02 0001"} ] }, { "name": "50 unsolicited long", "ver":4, "msgs": [{"type":"send", "payload":"50 03 0001 00"}]}, { "name": "50 unsolicited mid 0", "ver":4, "msgs": [{"type":"send", "payload":"50 02 0000"}]}, { "name": "50 unsolicited short 0", "ver":4, "msgs": [{"type":"send", "payload":"50 00"}]}, { "name": "50 unsolicited short 1", "ver":4, "msgs": [{"type":"send", "payload":"50 01 01"}]}, { "name": "51 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"51 02 0001"}]}, { "name": "52 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"52 02 0001"}]}, { "name": "54 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"54 02 0001"}]}, { "name": "58 unsolicited", "ver":4, "msgs": [{"type":"send", "payload":"58 02 0001"}]} ] }, { "group": "v5.0 PUBREC", "tests": [ { "name": "50 [MQTT-3.1.0-1] (no reason code)", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"50 02 0001"}]}, { "name": "50 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"50 03 0001 00"}]}, { "name": "50 unsolicited long", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 unsolicited mid 0", "ver":5, "msgs": [ {"type":"send", "payload":"50 03 0000 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 unsolicited short 0", "ver":5, "msgs": [ {"type":"send", "payload":"50 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 unsolicited short 1", "ver":5, "msgs": [ {"type":"send", "payload":"50 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 unsolicited len=2", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"50 02 0001"}, {"type":"recv", "payload":"62 02 0001"} ]}, { "name": "50 unsolicited len=3", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"50 03 0001 00"}, {"type":"recv", "payload":"62 02 0001"} ]}, { "name": "50 unsolicited len=3 fail", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"50 03 0001 80"}]}, { "name": "50 unsolicited len=4 ok", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"50 04 0001 00 00"}, {"type":"recv", "payload":"62 02 0001"} ]}, { "name": "50 unsolicited len=4 rc=fail", "ver":5, "expect_disconnect":false, "msgs": [{"type":"send", "payload":"50 04 0001 80 00"}]}, { "name": "50 unsolicited len=4 rc=unknown", "ver":5, "msgs": [ {"type":"send", "payload":"50 04 0001 FF 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 unsolicited len=4 short", "ver":5, "msgs": [ {"type":"send", "payload":"50 04 0001 00 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "51 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"51 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "52 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"52 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "54 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"54 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "58 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"58 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBREC ALLOWED PROPERTIES", "tests": [ { "name": "50 with reason-string property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 1F000170"}, {"type":"recv", "payload":"62 02 0001"} ]}, { "name": "50 with 2*reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"50 0C 0001 00 08 1F000170 1F000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 1F"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"50 0B 0001 00 07 26000170000171"}, {"type":"recv", "payload":"62 02 0001"} ]}, { "name": "50 with 2*user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"50 12 0001 00 0E 26000170000171 26000170000171"}, {"type":"recv", "payload":"62 02 0001"} ]}, { "name": "50 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 23000170"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBREC DISALLOWED PROPERTIES", "tests": [ { "name": "50 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0100"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 1700"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 2400"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 2500"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 2800"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 2900"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 2A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 17"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 24"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 25"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 28"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 29"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 2A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 09 0001 00 05 0200000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 09 0001 00 05 1100000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 09 0001 00 05 1800000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 09 0001 00 05 2700000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 02"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 11"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 18"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 27"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 03000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 08000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 12000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 15000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 1A000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 1C000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 03"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 08"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 12"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 15"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 1A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 1C"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 09000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 16000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 09"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 16"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 0B"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 07 0001 00 03 130101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 07 0001 00 03 210101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 07 0001 00 03 220101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"50 07 0001 00 03 230101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "50 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 13"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 21"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 22"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"50 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0501"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0601"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0701"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0A01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0C01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 0F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 1001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 1401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 1B01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 1D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 1E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 2001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 06 0001 00 02 7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 07 0001 00 03 800001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 07 0001 00 03 800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 07 0001 00 03 FF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 80800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 08 0001 00 04 FFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 09 0001 00 05 8080800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "50 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"50 09 0001 00 05 FFFFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/PUBREL.json000066400000000000000000000444351450213760600207240ustar00rootroot00000000000000[ { "group": "v3.1.1 PUBREL", "tests": [ { "name": "62 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"62 02 0001"}]}, { "name": "62 unsolicited", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 02 0001"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 unsolicited long", "ver":4, "msgs": [{"type":"send", "payload":"62 03 0001 00"}]}, { "name": "62 unsolicited mid 0", "ver":4, "msgs": [{"type":"send", "payload":"62 02 0000"}]}, { "name": "62 unsolicited short 0", "ver":4, "msgs": [{"type":"send", "payload":"62 00"}]}, { "name": "62 unsolicited short 1", "ver":4, "msgs": [{"type":"send", "payload":"62 01 01"}]}, { "name": "63 unsolicited [MQTT-3.6.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"63 02 0001"}]}, { "name": "64 unsolicited [MQTT-3.6.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"64 02 0001"}]}, { "name": "66 unsolicited [MQTT-3.6.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"66 02 0001"}]}, { "name": "6A unsolicited [MQTT-3.6.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"6A 02 0001"}]} ] }, { "group": "v5.0 PUBREL", "tests": [ { "name": "62 [MQTT-3.1.0-1] (no reason code)", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"62 02 0001"}]}, { "name": "62 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"62 03 0001 00"}]}, { "name": "62 unsolicited long", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 unsolicited mid 0", "ver":5, "msgs": [ {"type":"send", "payload":"62 03 0000 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 unsolicited short 0", "ver":5, "msgs": [ {"type":"send", "payload":"62 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 unsolicited short 1", "ver":5, "msgs": [ {"type":"send", "payload":"62 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 unsolicited len=2", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 02 0001"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 unsolicited len=3", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 03 0001 00"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 unsolicited len=3 fail", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 03 0001 92"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 unsolicited len=4 ok", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 04 0001 00 00"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 unsolicited len=4 rc=fail", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 04 0001 92 00"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 unsolicited len=4 rc=unknown", "ver":5, "msgs": [ {"type":"send", "payload":"62 04 0001 FF 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 unsolicited len=4 short", "ver":5, "msgs": [ {"type":"send", "payload":"62 04 0001 00 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "63 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"6303000100"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "64 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"6403000100"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "66 unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"6603000100"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "6A unsolicited", "ver":5, "msgs": [ {"type":"send", "payload":"6A03000100"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBREL ALLOWED PROPERTIES", "tests": [ { "name": "62 with reason-string property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 1F000170"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 with 2*reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"62 0C 0001 00 081 F000170 1F000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 1F"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 0B 0001 00 07 26000170000171"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 with 2*user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"62 12 0001 00 0E 26000170000171 26000170000171"}, {"type":"recv", "payload":"70 02 0001"} ]}, { "name": "62 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 23000170"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 PUBREL DISALLOWED PROPERTIES", "tests": [ { "name": "62 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0100"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 1700"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 2400"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 2500"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 2800"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 2900"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 2A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 17"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 24"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 25"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 28"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 29"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 2A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 09 0001 00 05 0200000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 09 0001 00 05 1100000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 09 0001 00 05 1800000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 09 0001 00 05 2700000001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 02"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 11"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 18"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 27"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 03000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 08000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 12000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 15000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 1A000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 1C000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 03"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 08"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 12"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 15"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 1A"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 1C"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 09000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 16000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 09"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 16"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 0B"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 07 0001 00 03 130101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 07 0001 00 03 210101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 07 0001 00 03 220101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"62 07 0001 00 03 230101"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "62 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 13"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 21"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 22"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"62 05 0001 00 01 23"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0501"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0601"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0701"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0A01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0C01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 0F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 1001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 1401"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 1B01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 1D01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 1E01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 2001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 06 0001 00 02 7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 07 0001 00 03 800001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 07 0001 00 03 800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 07 0001 00 03 FF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 80800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 08 0001 00 04 FFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 09 0001 00 05 8080800101"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "62 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"62 09 0001 00 05 FFFFFF7F01"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/REGRESSION.json000066400000000000000000000015511450213760600214030ustar00rootroot00000000000000[ { "group": "REGRESSIONS", "tests": [ { "name": "subscribe-unsubscribe-crash part 1", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 26 1234 0009 64 72 61 73 68 2F 31 2F 23 00 0009 65 72 61 73 68 2F 32 2F 23 00 0009 63 72 61 73 68 2F 33 2F 23 00"}, {"type":"recv", "payload":"90 05 1234 00 00 00"}, {"type":"send", "payload":"A2 0D 1234 0009 64 72 61 73 68 2F 31 2F 23"}, {"type":"recv", "payload":"B0 02 1234"} ], "comment": "Must be used with part 2 immediately after", "comment2": "Requires WITH_ASAN=yes"}, { "name": "subscribe-unsubscribe-crash part 2", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0E 1234 0009 63 72 61 73 68 2F 33 2F 23 00"}, {"type":"recv", "payload":"90 03 1234 00"} ], "comment": "https://github.com/eclipse/mosquitto/issues/2885"} ] } ] mosquitto-2.0.18/test/broker/data/SUBACK.json000066400000000000000000000456031450213760600207010ustar00rootroot00000000000000[ { "group": "v3.1.1 SUBACK", "tests": [ { "name": "90 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"90 03 0001 00"}]}, { "name": "90 mid 0", "ver":4, "msgs": [{"type":"send", "payload":"90 03 0000 00"}]}, { "name": "90", "ver":4, "msgs": [{"type":"send", "payload":"90 03 0001 00"}]}, { "name": "90 short 0", "ver":4, "msgs": [{"type":"send", "payload":"90 00"}]}, { "name": "90 short 1", "ver":4, "msgs": [{"type":"send", "payload":"90 01 01"}]}, { "name": "90 short 2", "ver":4, "msgs": [{"type":"send", "payload":"90 02 0001"}]}, { "name": "91", "ver":4, "msgs": [{"type":"send", "payload":"91 03 0001 00"}]}, { "name": "92", "ver":4, "msgs": [{"type":"send", "payload":"92 03 0001 00"}]}, { "name": "94", "ver":4, "msgs": [{"type":"send", "payload":"94 03 0001 00"}]}, { "name": "98", "ver":4, "msgs": [{"type":"send", "payload":"98 03 0001 00"}]} ] }, { "group": "v5.0 SUBACK", "tests": [ { "name": "90 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"90 03 0001 00"}]}, { "name": "90 long", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 short 0", "ver":5, "msgs": [ {"type":"send", "payload":"90 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 short 1", "ver":5, "msgs": [ {"type":"send", "payload":"90 01 01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 short 2", "ver":5, "msgs": [ {"type":"send", "payload":"90 02 0001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 short 3", "ver":5, "msgs": [ {"type":"send", "payload":"90 03 0001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90", "ver":5, "msgs": [ {"type":"send", "payload":"90 03 0001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "91", "ver":5, "msgs": [ {"type":"send", "payload":"91 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "92", "ver":5, "msgs": [ {"type":"send", "payload":"92 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "94", "ver":5, "msgs": [ {"type":"send", "payload":"94 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "98", "ver":5, "msgs": [ {"type":"send", "payload":"98 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "90 with property", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 1F000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x01 qos 1", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x02 qos 2", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 02"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x11 no sub", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 11"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x80 unspecified error", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 80"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x83 implementation specific error", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 83"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x87 not authorised", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 87"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x8F topic filter invalid", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 8F"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x91 packet identifier in use", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 91"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x97 quota exceeded", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 97"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0x9E shared subs not supported", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 9E"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0xA1 sub ids not supported", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 A1"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0xA2 wildcards not supported", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 A2"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 reason code 0xFF unknown", "ver":5, "msgs": [ {"type":"send", "payload":"90 04 0001 00 FF"}, {"type":"recv", "payload":"E0 01 82"} ]} ] }, { "group": "v5.0 SUBACK PROPERTIES", "tests": [ { "name": "90 with reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 1F000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with 2*reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"90 0C 0001 08 1F00017000 1F000171"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 1F 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with user-property", "ver":5, "msgs": [ {"type":"send", "payload":"90 0B 0001 07 26000170000171 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 23000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 23 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0100 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 1700 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 2400 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 2500 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 2800 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 2900 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 2A00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 17 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 24 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 25 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 28 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 29 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 2A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 09 0001 05 0200000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 09 0001 05 1100000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 09 0001 05 1800000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 09 0001 05 2700000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 02 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 11 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 18 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 27 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 03000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 08000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 12000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 15000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 1A000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 1C000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 03 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 08 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 12 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 15 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 1A00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 1C00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 09000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 16000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 09 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 16 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0B01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 0B 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 07 0001 03 130101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 07 0001 03 210101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 07 0001 03 220101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"90 07 0001 03 230101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 13 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 21 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 22 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"90 05 0001 01 23 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0401 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0501 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0601 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0701 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0A01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0C01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0D01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0E01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 0F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 1001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 1401 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 1901 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 1D01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 1E01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 2001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 06 0001 02 7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 07 0001 03 800001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 07 0001 03 800101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 07 0001 03 FF7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 80800101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 08 0001 04 FFFF7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 09 0001 05 8080800101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "90 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"90 09 0001 05 FFFFFF7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]} ] } ] mosquitto-2.0.18/test/broker/data/SUBSCRIBE.json000066400000000000000000000563641450213760600212600ustar00rootroot00000000000000[ { "group": "v3.1.1 SUBSCRIBE", "tests": [ { "name": "82 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 00"}]}, { "name": "80", "ver":4, "msgs": [{"type":"send", "payload":"80061234 0001 70 00"}]}, { "name": "83 [MQTT-3.8.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"83 06 1234 0001 70 00"}]}, { "name": "84 [MQTT-3.8.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"84 06 1234 0001 70 00"}]}, { "name": "86 [MQTT-3.8.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"86 06 1234 0001 70 00"}]}, { "name": "8A [MQTT-3.8.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"8A 06 1234 0001 70 00"}]}, { "name": "82 QoS 3 [MQTT-3-8.3-4]", "ver":4, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 03"}]}, { "name": "82 QoS 0x04 [MQTT-3-8.3-4]", "ver":4, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 04"}]}, { "name": "82 QoS 0x08 [MQTT-3-8.3-4]", "ver":4, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 08"}]}, { "name": "82 QoS 0x10 [MQTT-3-8.3-4]", "ver":4, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 10"}]}, { "name": "82 QoS 0x20 [MQTT-3-8.3-4]", "ver":4, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 20"}]}, { "name": "82 QoS 0x40 [MQTT-3-8.3-4]", "ver":4, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 40"}]}, { "name": "82 QoS 0x80 [MQTT-3-8.3-4]", "ver":4, "msgs": [{"type":"send", "payload":"82 06 1234 0001 70 80"}]}, { "name": "82 topic with 0x0000", "ver":4, "msgs": [{"type":"send", "payload":"82 0A 1234 0005 746F700000 00"} ] }, { "name": "82 topic with U+D800", "ver":4, "msgs": [{"type":"send", "payload":"82 0A 1234 0005 746FEDA080 00"} ] }, { "name": "82 topic with U+0001", "ver":4, "msgs": [{"type":"send", "payload":"82 0A 1234 0005 746F700170 00"} ] }, { "name": "82 topic with U+001F", "ver":4, "msgs": [{"type":"send", "payload":"82 0A 1234 0005 746F701F70 00"} ] }, { "name": "82 topic with U+007F", "ver":4, "msgs": [{"type":"send", "payload":"82 0A 1234 0005 746F707F70 00"} ] }, { "name": "82 topic with U+009F", "ver":4, "msgs": [{"type":"send", "payload":"82 0A 1234 0005 746FC29F70 00"} ] }, { "name": "82 topic with U+FFFF", "ver":4, "msgs": [{"type":"send", "payload":"82 0A 1234 0005 746FEDBFBF 00"} ] }, { "name": "82 long", "ver":4, "msgs": [{"type":"send", "payload":"82 07 1234 0001 70 00 00"}]}, { "name": "82 short 5 [MQTT-3.8.3-3]", "ver":4, "msgs": [{"type":"send", "payload":"82 05 1234 0001 70"}]}, { "name": "82 short 4", "ver":4, "msgs": [{"type":"send", "payload":"82 04 1234 0000"}]}, { "name": "82 short 3", "ver":4, "msgs": [{"type":"send", "payload":"82 03 1234 00"}]}, { "name": "82 short 2", "ver":4, "msgs": [{"type":"send", "payload":"82 02 1234"}]}, { "name": "82 short 1", "ver":4, "msgs": [{"type":"send", "payload":"82 01 12"}]}, { "name": "82 short 0", "ver":4, "msgs": [{"type":"send", "payload":"82 00"}]}, { "name": "82 single topic len 0", "ver":4, "msgs": [{"type":"send", "payload":"82 05 1234 0000 00"}]}, { "name": "82 multiple topic 1 len 0", "ver":4, "msgs": [{"type":"send", "payload":"82 09 1234 0000 00 0001 71 00"}]}, { "name": "82 multiple topic 2 len 0", "ver":4, "msgs": [{"type":"send", "payload":"82 09 1234 0001 71 00 0000 00"}]}, { "name": "82 multiple topic 1,2 len 0", "ver":4, "msgs": [{"type":"send", "payload":"82 08 1234 0000 00 0000 00"}]}, { "name": "82 single ok QoS 0 [MQTT-3.8.4-1]", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 00"}, {"type":"recv", "payload":"90 03 1234 00"} ]}, { "name": "82 single ok QoS 1 [MQTT-3.8.4-1]", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 01"}, {"type":"recv", "payload":"90 03 1234 01"} ]}, { "name": "82 single ok QoS 2 [MQTT-3.8.4-1]", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 02"}, {"type":"recv", "payload":"90 03 1234 02"} ]}, { "name": "82 multiple ok [MQTT-3.8.4-4]", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0A 1234 0001 70 00 0001 71 00"}, {"type":"recv", "payload":"90 04 1234 00 00"} ]} ] }, { "group": "v5.0 SUBSCRIBE", "tests": [ { "name": "82 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"82 07 1234 00 0001 70 00"}]}, { "name": "82 single ok QoS 0 [MQTT-3.8.4-1]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 00"}, {"type":"recv", "payload":"90 04 1234 00 00"} ]}, { "name": "82 single ok QoS 1 [MQTT-3.8.4-1]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 01"}, {"type":"recv", "payload":"90 04 1234 0001"} ]}, { "name": "82 single ok QoS 2 [MQTT-3.8.4-1]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 02"}, {"type":"recv", "payload":"90 04 1234 00 02"} ]}, { "name": "80", "ver":5, "msgs": [ {"type":"send", "payload":"8007123400 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "83 [MQTT-3.8.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"8307123400 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "84 [MQTT-3.8.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"8407123400 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "86 [MQTT-3.8.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"8607123400 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "8A [MQTT-3.8.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"8A 07 1234 00 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 QoS 3 [MQTT-3-8.3-4]", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 03"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 QoS 0 no local 0x04", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 04"}, {"type":"recv", "payload":"90 04 1234 00 00"} ]}, { "name": "82 QoS 0 retain as published 0x08", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 08"}, {"type":"recv", "payload":"90 04 1234 00 00"} ]}, { "name": "82 QoS 0 retain handling=1 0x10", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 10"}, {"type":"recv", "payload":"90 04 1234 00 00"} ]}, { "name": "82 QoS 0 retain handling=2 0x20", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 20"}, {"type":"recv", "payload":"90 04 1234 00 00"} ]}, { "name": "82 QoS 0 retain handling=3 0x30 [MQTT-3-8.3-4]", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 30"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 QoS 0x40 [MQTT-3-8.3-5]", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 40"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 QoS 0x80 [MQTT-3-8.3-5]", "ver":5, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 80"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 topic with 0x0000", "ver":5, "msgs": [ {"type":"send", "payload":"82121234000005746F7000007061796C6F616400"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 topic with U+D800", "ver":5, "msgs": [ {"type":"send", "payload":"82121234000005746FEDA0807061796C6F616400"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 topic with U+0001", "ver":5, "msgs": [ {"type":"send", "payload":"82121234000005746F7001707061796C6F616400"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 topic with U+001F", "ver":5, "msgs": [ {"type":"send", "payload":"82121234000005746F701F707061796C6F616400"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 topic with U+007F", "ver":5, "msgs": [ {"type":"send", "payload":"82121234000005746F707F707061796C6F616400"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 topic with U+009F", "ver":5, "msgs": [ {"type":"send", "payload":"82121234000005746FC29F707061796C6F616400"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 topic with U+FFFF", "ver":5, "msgs": [ {"type":"send", "payload":"82121234000005746FEDBFBF7061796C6F616400"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 long", "ver":5, "msgs": [ {"type":"send", "payload":"82 08 1234 00 0001 70 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 short 5 [MQTT-3.8.3-3]", "ver":5, "msgs": [ {"type":"send", "payload":"82 06 1234 00 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 short 5", "ver":5, "msgs": [ {"type":"send", "payload":"82 05 1234 00 0000"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 short 4", "ver":5, "msgs": [ {"type":"send", "payload":"82 04 1234 00 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 short 3", "ver":5, "msgs": [ {"type":"send", "payload":"82 03 1234 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 short 2", "ver":5, "msgs": [ {"type":"send", "payload":"82 02 1234"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 short 1", "ver":5, "msgs": [ {"type":"send", "payload":"82 01 12"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 short 0", "ver":5, "msgs": [ {"type":"send", "payload":"82 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 single topic len 0", "ver":5, "msgs": [ {"type":"send", "payload":"82 06 1234 00 0000 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 multiple topic 1 len 0", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 1234 00 0000 00 0001 71 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 multiple topic 2 len 0", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 1234 00 0001 71 00 0000 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 multiple topic 1,2 len 0", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 1234 00 0000 00 0000 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 single ok QoS 0 [MQTT-3.8.4-1]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 00"}, {"type":"recv", "payload":"90 04 1234 00 00"} ]}, { "name": "82 single ok QoS 1 [MQTT-3.8.4-1]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 01"}, {"type":"recv", "payload":"90 04 1234 00 01"} ]}, { "name": "82 single ok QoS 2 [MQTT-3.8.4-1]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 02"}, {"type":"recv", "payload":"90 04 1234 00 02"} ]}, { "name": "82 multiple ok [MQTT-3.8.4-4]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0B 1234 00 0001 70 00 0001 71 00"}, {"type":"recv", "payload":"90 05 1234 00 00 00"} ]} ] }, { "group": "v5.0 SUBSCRIBE ALLOWED PROPERTIES", "tests": [ { "name": "82 with user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0E 0001 07 26000170000171 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with 2*user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 15 0001 0E 26000170000171 26000170000171 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0 (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0B00 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with subscription-identifier=1 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0B01 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with 2*subscription-identifier=1 (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 0B010B01 0001 70 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "82 with subscription-identifier=0x7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0B7F 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0x8000 (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 0B8000 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with subscription-identifier=0x8001 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 0B8001 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0xFF7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 0BFF7F 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0x808001 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 0B808001 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0xFFFF7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 0BFFFF7F 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0x80808001 (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0C 0001 05 0B80808001 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0xFFFFFF7F (variable byte integer)", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 0C 0001 05 0BFFFFFF7F 0001 70 00"}, {"type":"recv", "payload":"90 04 0001 00 00"} ]}, { "name": "82 with subscription-identifier=0x8080808001 (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0D 0001 06 0B8080808001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]} ] }, { "group": "v5.0 SUBSCRIBE DISALLOWED PROPERTIES", "tests": [ { "name": "82 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0100 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 1700 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 2400 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 2500 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 2800 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 2900 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 2A00 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0C 0001 05 0200000001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0C 0001 05 1100000001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0C 0001 05 1800000001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0C 0001 052700000001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 03000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 08000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 12000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 15000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 1A000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 1C000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 09000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 16000170 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 130101 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 210101 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 220101 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 230101 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0401 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0501 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0601 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0701 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0A01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0C01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0D01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0E01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 0F01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 1001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 1401 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 1B01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 1D01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 1E01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 2001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 09 0001 02 7F01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 800001 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 800101 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0A 0001 03 FF7F01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 80800101 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0B 0001 04 FFFF7F 01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0C 0001 05 80808001 01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "82 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"82 0C 0001 05 FFFFFF7F 01 0001 70 00"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/UNSUBACK.json000066400000000000000000000427131450213760600211430ustar00rootroot00000000000000[ { "group": "v3.1.1 UNSUBACK", "tests": [ { "name": "B0 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"B0 02 0001"}]}, { "name": "B0 long", "ver":4, "msgs": [{"type":"send", "payload":"B0 03 0001 00"}]}, { "name": "B0 short 0", "ver":4, "msgs": [{"type":"send", "payload":"B0 00"}]}, { "name": "B0 short 1", "ver":4, "msgs": [{"type":"send", "payload":"B0 01 01"}]}, { "name": "B0", "ver":4, "msgs": [{"type":"send", "payload":"B0 02 0001"}]}, { "name": "B1", "ver":4, "msgs": [{"type":"send", "payload":"B1 02 0001"}]}, { "name": "B2", "ver":4, "msgs": [{"type":"send", "payload":"B2 02 0001"}]}, { "name": "B4", "ver":4, "msgs": [{"type":"send", "payload":"B4 02 0001"}]}, { "name": "B8", "ver":4, "msgs": [{"type":"send", "payload":"B8 02 0001"}]} ] }, { "group": "v5.0 UNSUBACK", "tests": [ { "name": "B0 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"B0 03 0001 00"}]}, { "name": "B0 long", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 short 0", "ver":5, "msgs": [ {"type":"send", "payload":"B0 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 short 1", "ver":5, "msgs": [ {"type":"send", "payload":"B0 01 01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 short 2", "ver":5, "msgs": [ {"type":"send", "payload":"B0 02 0001"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0", "ver":5, "msgs": [ {"type":"send", "payload":"B0 03 0001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B1", "ver":5, "msgs": [ {"type":"send", "payload":"B1 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "B2", "ver":5, "msgs": [ {"type":"send", "payload":"B2 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "B4", "ver":5, "msgs": [ {"type":"send", "payload":"B4 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "B8", "ver":5, "msgs": [ {"type":"send", "payload":"B8 03 0001 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "B0 with property", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 04 1F000170"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 reason code 0x11 no sub", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 11"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 reason code 0x80 unspecified error", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 80"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 reason code 0x83 implementation specific error", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 83"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 reason code 0x87 not authorised", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 87"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 reason code 0x8F topic filter invalid", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 8F"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 reason code 0x91 packet identifier in use", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 91"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 reason code 0xFF unknown", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 00 FF"}, {"type":"recv", "payload":"E0 01 82"} ]} ] }, { "group": "v5.0 UNSUBACK PROPERTIES", "tests": [ { "name": "B0 with reason-string property", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 1F000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with reason-string property missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 1F 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with user-property", "ver":5, "msgs": [ {"type":"send", "payload":"B0 0B 0001 07 26000170000171 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with user-property missing value", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 23000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with user-property missing key,value", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 23 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0100 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 1700 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 2400 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 2500 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 2800 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 2900 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 2A00 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with payload-format-indicator (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with request-problem-information (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 17 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with maximum-qos (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 24 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with retain-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 25 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with wildcard-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 28 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with subscription-identifier-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 29 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with shared-subscription-available (byte) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 2A 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 09 0001 05 0200000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 09 0001 05 1100000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 09 0001 05 1800000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 09 0001 05 2700000001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with message-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 02 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with session-expiry-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 11 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with will-delay-interval (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 18 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with maximum-packet-size (four byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 27 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 03000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 08000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 12000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 15000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 1A000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 1C000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with content-type (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 03 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with response-topic (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 08 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with assigned-client-identifier (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 12 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with authentication-method (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 15 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with response-information (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 1A 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with server-reference (UTF-8 string) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 1C 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 09000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 16000170 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with correlation-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 09 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with authentication-data (binary data) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 16 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 02 0B01"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with subscription-identifier (variable byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 04 0001 01 0B"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 03 130101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 03 210101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 03 220101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 03 230101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with server-keep-alive (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 13 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with receive-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 21 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with topic-alias-maximum (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 22 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with topic-alias (two byte integer) missing", "ver":5, "msgs": [ {"type":"send", "payload":"B0 05 0001 01 23 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0401 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0501 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0601 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0701 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0A01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0C01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0D01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0E01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 0F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 1001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 1401 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 1B01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 1D01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 1E01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 2001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 06 0001 02 7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 03 800001 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 03 800101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 07 0001 03 FF7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 80800101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 08 0001 04 FFFF7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 09 0001 05 8080800101 00"}, {"type":"recv", "payload":"E0 01 82"} ]}, { "name": "B0 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"B0 09 0001 05 FFFFFF7F01 00"}, {"type":"recv", "payload":"E0 01 82"} ]} ] } ] mosquitto-2.0.18/test/broker/data/UNSUBSCRIBE.json000066400000000000000000000403411450213760600215070ustar00rootroot00000000000000[ { "group": "v3.1.1 UNSUBSCRIBE", "tests": [ { "name": "A2 [MQTT-3.1.0-1]", "ver":4, "connect":false, "msgs": [{"type":"send", "payload":"A2 05 1234 0001 70"}]}, { "name": "A2 (no subscribe) [MQTT-3.10.4-5]", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"A2 05 1234 0001 70"}, {"type":"recv", "payload":"B0 02 1234"} ]}, { "name": "A2 (with subscribe) [MQTT-3.10.4-5]", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 06 1234 0001 70 00"}, {"type":"recv", "payload":"90 03 1234 00"}, {"type":"send", "payload":"A2 05 1234 0001 70"}, {"type":"recv", "payload":"B0 02 1234"} ]}, { "name": "A2 multiple [MQTT-3.10.4-6]", "ver":4, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"A2 08 1234 0001 70 0001 71"}, {"type":"recv", "payload":"B0 02 1234"} ]}, { "name": "A2 multiple zero 1st", "ver":4, "msgs": [{"type":"send", "payload":"A2 07 1234 0000 0001 71"}]}, { "name": "A2 multiple zero 2nd", "ver":4, "msgs": [{"type":"send", "payload":"A2 07 1234 0001 71 0000"}]}, { "name": "A2 short 4", "ver":4, "msgs": [{"type":"send", "payload":"A2 04 1234 0001"}]}, { "name": "A2 short 3", "ver":4, "msgs": [{"type":"send", "payload":"A2 03 1234 01"}]}, { "name": "A2 short 2 [MQTT-3.10.3-2]", "ver":4, "msgs": [{"type":"send", "payload":"A2 02 1234"}]}, { "name": "A2 short 1", "ver":4, "msgs": [{"type":"send", "payload":"A2 01 12"}]}, { "name": "A2 short 0", "ver":4, "msgs": [{"type":"send", "payload":"A2 00"}]}, { "name": "A0 [MQTT-3.10.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"A0 05 1234 0001 70"}]}, { "name": "A3 [MQTT-3.10.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"A3 05 1234 0001 70"}]}, { "name": "A4 [MQTT-3.10.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"A4 05 1234 0001 70"}]}, { "name": "A6 [MQTT-3.10.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"A6 05 1234 0001 70"}]}, { "name": "AA [MQTT-3.10.1-1]", "ver":4, "msgs": [{"type":"send", "payload":"AA 05 1234 0001 70"}]}, { "name": "A2 topic with 0x0000", "ver":4, "msgs": [{"type":"send", "payload":"A2 09 1234 0005 746F700000"}]}, { "name": "A2 topic with U+D800", "ver":4, "msgs": [{"type":"send", "payload":"A2 09 1234 0005 746FEDA080"}]}, { "name": "A2 topic with U+0001", "ver":4, "msgs": [{"type":"send", "payload":"A2 09 1234 0005 746F700170"}]}, { "name": "A2 topic with U+001F", "ver":4, "msgs": [{"type":"send", "payload":"A2 09 1234 0005 746F701F70"}]}, { "name": "A2 topic with U+007F", "ver":4, "msgs": [{"type":"send", "payload":"A2 09 1234 0005 746F707F70"}]}, { "name": "A2 topic with U+009F", "ver":4, "msgs": [{"type":"send", "payload":"A2 09 1234 0005 746FC29F70"}]}, { "name": "A2 topic with U+FFFF", "ver":4, "msgs": [{"type":"send", "payload":"A2 09 1234 0005 746FEDBFBF"}]} ] }, { "group": "v5.0 UNSUBSCRIBE", "tests": [ { "name": "A2 [MQTT-3.1.0-1]", "ver":5, "connect":false, "msgs": [{"type":"send", "payload":"A2 06 1234 00 0001 70"}]}, { "name": "A2 (no subscribe) [MQTT-3.10.4-5]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"A2 06 1234 00 0001 70"}, {"type":"recv", "payload":"B0 04 1234 00 11"} ]}, { "name": "A2 (with subscribe) [MQTT-3.10.4-5]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"82 07 1234 00 0001 70 00"}, {"type":"recv", "payload":"90 04 1234 00 00"}, {"type":"send", "payload":"A2 06 1234 00 0001 70"}, {"type":"recv", "payload":"B0 04 1234 00 00"} ]}, { "name": "A2 multiple zero 1st", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 1234 00 0000 0001 71"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 multiple zero 2nd", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 1234 00 0001 71 0000"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 short 5", "ver":5, "msgs": [ {"type":"send", "payload":"A2 05 1234 00 0001"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 short 4", "ver":5, "msgs": [ {"type":"send", "payload":"A2 04 1234 00 01"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 short 3 [MQTT-3.10.3-2]", "ver":5, "msgs": [ {"type":"send", "payload":"A2 03 1234 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 short 2", "ver":5, "msgs": [ {"type":"send", "payload":"A2 01 1234"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 short 1", "ver":5, "msgs": [ {"type":"send", "payload":"A2 01 12"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 short 0", "ver":5, "msgs": [ {"type":"send", "payload":"A2 00"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A0 [MQTT-3.10.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"A0 06 1234 00 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A3 [MQTT-3.10.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"A3 06 1234 00 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A4 [MQTT-3.10.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"A4 06 1234 00 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A6 [MQTT-3.10.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"A6 06 1234 00 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "AA [MQTT-3.10.1-1]", "ver":5, "msgs": [ {"type":"send", "payload":"AA 06 1234 00 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 topic with 0x0000", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 1234 00 0005 746F700000"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 topic with U+D800", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 1234 00 0005 746FEDA080"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 topic with U+0001", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 1234 00 0005 746F700170"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 topic with U+001F", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 1234 00 0005 746F701F70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 topic with U+007F", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 1234 00 0005 746F707F70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 topic with U+009F", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 1234 00 0005 746FC29F70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 topic with U+FFFF", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 1234 00 0005 746FEDBFBF"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 multiple [MQTT-3.10.4-6]", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"A2 09 1234 00 0001 70 0001 71"}, {"type":"recv", "payload":"B0 05 1234 00 11 11"} ]} ] }, { "group": "v5.0 UNSUBSCRIBE ALLOWED PROPERTIES", "tests": [ { "name": "A2 with user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"A2 0D 0001 07 26000170000171 0001 70"}, {"type":"recv", "payload":"B0 04 0001 00 11"} ]}, { "name": "A2 with 2*user-property", "ver":5, "expect_disconnect":false, "msgs": [ {"type":"send", "payload":"A2 14 0001 0E 26000170000171 26000170000171 0001 70"}, {"type":"recv", "payload":"B0 04 0001 00 11"} ]} ] }, { "group": "v5.0 UNSUBSCRIBE DISALLOWED PROPERTIES", "tests": [ { "name": "A2 with payload-format-indicator (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0100 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with request-problem-information (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 1700 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with maximum-qos (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 2400 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with retain-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 2500 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with wildcard-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 2800 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with subscription-identifier-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 2900 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with shared-subscription-available (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 2A00 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with message-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 05 0200000001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with session-expiry-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 05 1100000001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with will-delay-interval (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 05 1800000001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with maximum-packet-size (four byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 05 2700000001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with content-type (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 0001 04 03000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with response-topic (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 00 04 08000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with assigned-client-identifier (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 00 04 12000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with authentication-method (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 00 04 15000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with response-information (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 00 04 1A000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with server-reference (UTF-8 string)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 00 04 1C000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with correlation-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 0001 04 09000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with authentication-data (binary data)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 0001 04 16000170 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with subscription-identifier (variable byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0B01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with server-keep-alive (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 09 0001 03 130101 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with receive-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 09 0001 03 210101 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with topic-alias-maximum (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 09 0001 03 220101 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with topic-alias (two byte integer)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 09 0001 03 230101 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with invalid-property 0x00 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x04 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0401 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x05 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0501 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x06 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0601 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x07 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0701 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x0A (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0A01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x0C (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0C01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x0D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0D01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x0E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0E01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x0F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 0F01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x10 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 1001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x14 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 1401 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x1B (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 1B01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x1D (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 1D01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x1E (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 1E01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x20 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 2001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 08 0001 02 7F01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with invalid-property 0x8000 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 09 0001 03 800001 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x8001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 09 0001 03 800101 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0xFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 09 0001 03 FF7F01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 0001 04 80800101 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0xFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0A 0001 04 FFFF7F01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0x80808001 (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 05 8080800101 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]}, { "name": "A2 with unknown-property 0xFFFFFF7F (byte)", "ver":5, "msgs": [ {"type":"send", "payload":"A2 0B 0001 05 FFFFFF7F01 0001 70"}, {"type":"recv", "payload":"E0 01 81"} ]} ] } ] mosquitto-2.0.18/test/broker/data/ZZ-broker-check.json000066400000000000000000000002031450213760600226140ustar00rootroot00000000000000[ { "group": "BROKER CHECK", "tests": [ { "name": "END OF TEST", "ver":4, "expect_disconnect": false, "msgs": []} ] } ] mosquitto-2.0.18/test/broker/dynamic-security-init.json000066400000000000000000000023351450213760600232450ustar00rootroot00000000000000{ "clients": [{ "username": "admin", "textName": "Dynsec admin user", "password": "Rko31yHY12ryMoyZTBNIUsCPb5SDa4WmUP3Xe2+V6P+QOSW3Gj6IDmpl6zQsAjutb476zEYdBeTw9tU7WZ1new==", "salt": "Ezuo4G1TqYtTQDL/", "iterations": 101, "roles": [{ "rolename": "admin" }] }], "roles": [{ "rolename": "admin", "acls": [{ "acltype": "publishClientSend", "topic": "$CONTROL/dynamic-security/#", "allow": true }, { "acltype": "publishClientReceive", "topic": "$CONTROL/dynamic-security/#", "allow": true }, { "acltype": "subscribePattern", "topic": "$CONTROL/dynamic-security/#", "allow": true }, { "acltype": "publishClientReceive", "topic": "$SYS/#", "allow": true }, { "acltype": "subscribePattern", "topic": "$SYS/#", "allow": true }, { "acltype": "publishClientReceive", "topic": "#", "allow": true }, { "acltype": "subscribePattern", "topic": "#", "allow": true }, { "acltype": "unsubscribePattern", "topic": "#", "allow": true }] }], "defaultACLAccess": { "publishClientSend": false, "publishClientReceive": true, "subscribe": false, "unsubscribe": true } }mosquitto-2.0.18/test/broker/mosq_test_helper.py000066400000000000000000000007231450213760600220460ustar00rootroot00000000000000import inspect, os, sys # From http://stackoverflow.com/questions/279237/python-import-a-module-from-a-folder cmd_subfolder = os.path.realpath(os.path.abspath(os.path.join(os.path.split(inspect.getfile( inspect.currentframe() ))[0],".."))) if cmd_subfolder not in sys.path: sys.path.insert(0, cmd_subfolder) import mosq_test import mqtt5_opts import mqtt5_props import mqtt5_rc import socket import ssl import struct import subprocess import time import errno mosquitto-2.0.18/test/broker/msg_sequence_test.py000077500000000000000000000164161450213760600222170ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a valid CONNECT results in the correct CONNACK packet. from mosq_test_helper import * import importlib from os import walk import socket import json from collections import deque import mosq_test send = 1 recv = 2 disconnected_check = 3 connected_check = 4 publish = 5 class SingleMsg(object): __slots__ = 'action', 'message', 'comment' def __init__(self, action, message, comment=''): self.action = action self.message = message self.comment = comment class MsgSequence(object): __slots__ = 'name', 'msgs', 'expect_disconnect' def __init__(self, name, default_connect=True, proto_ver=4, expect_disconnect=True): self.name = name self.msgs = deque() self.expect_disconnect = expect_disconnect if default_connect: self.add_default_connect(proto_ver=proto_ver) def add_default_connect(self, proto_ver): self.add_send(mosq_test.gen_connect(self.name, keepalive=60, proto_ver=proto_ver)) self.add_recv(mosq_test.gen_connack(rc=0, proto_ver=proto_ver), "default connack") def add_send(self, message): self._add(send, message) def add_recv(self, message, comment): self._add(recv, message, comment) def add_publish(self, message, comment): self._add(publish, message, comment) def add_connected_check(self): self._add(connected_check, b"") def add_disconnected_check(self): self._add(disconnected_check, b"") def _add(self, action, message, comment=""): msg = SingleMsg(action, message, comment) self.msgs.append(msg) def _connected_check(self, sock): try: mosq_test.do_ping(sock) except mosq_test.TestError: raise ValueError("connection failed") def _send_message(self, sock, msg): sock.send(msg.message) def _publish_message(self, msg): sock = mosq_test.client_connect_only(hostname="localhost", port=1888, timeout=2) sock.send(mosq_test.gen_connect("helper", keepalive=60)) mosq_test.expect_packet(sock, "connack", mosq_test.gen_connack(rc=0)) m = msg.message if m['qos'] == 0: sock.send(mosq_test.gen_publish(topic=m['topic'], payload=m['payload'])) elif m['qos'] == 1: sock.send(mosq_test.gen_publish(mid=1, qos=1, topic=m['topic'], payload=m['payload'])) mosq_test.expect_packet(sock, "helper puback", mosq_test.gen_puback(mid=1)) elif m['qos'] == 2: sock.send(mosq_test.gen_publish(mid=1, qos=2, topic=m['topic'], payload=m['payload'])) mosq_test.expect_packet(sock, "helper pubrec", mosq_test.gen_pubrec(mid=1)) sock.send(mosq_test.gen_pubrel(mid=1)) mosq_test.expect_packet(sock, "helper pubcomp", mosq_test.gen_pubcomp(mid=1)) sock.close() def _recv_message(self, sock, msg): data = sock.recv(len(msg.message)) if data != msg.message: raise ValueError("Receive message %s | %s | %s" % (msg.comment, data, msg.message)) def _disconnected_check(self, sock): try: data = sock.recv(1) if len(data) == 1 and self.expect_disconnect: raise ValueError("Still connected") except ConnectionResetError: if self.expect_disconnect: pass else: raise def _process_message(self, sock, msg): if msg.action == send: self._send_message(sock, msg) elif msg.action == recv: self._recv_message(sock, msg) elif msg.action == publish: self._publish_message(msg) elif msg.action == disconnected_check: self._disconnected_check(sock) elif msg.action == connected_check: self._connected_check(sock) def process_next(self, sock): msg = self.msgs.popleft() self._process_message(sock, msg) def process_all(self, sock): while len(self.msgs): self.process_next(sock) if self.expect_disconnect: self._disconnected_check(sock) else: self._connected_check(sock) def do_test(hostname, port): rc = 0 sequences = [] for (_, _, filenames) in walk("data"): sequences.extend(filenames) break total = 0 succeeded = 0 test = None for seq in sorted(sequences): if seq[-5:] != ".json": continue with open("data/"+seq, "r") as f: test_file = json.load(f) for g in test_file: group_name = g["group"] try: disabled = g["disable"] if disabled: continue except KeyError: pass tests = g["tests"] for t in tests: tname = group_name + " " + t["name"] try: proto_ver = t["ver"] except KeyError: proto_ver = 4 try: connect = t["connect"] except KeyError: connect = True try: expect_disconnect = t["expect_disconnect"] except KeyError: expect_disconnect = True this_test = MsgSequence(tname, proto_ver=proto_ver, expect_disconnect=expect_disconnect, default_connect=connect) for m in t["msgs"]: try: c = m["comment"] except KeyError: c = "" if m["type"] == "send": this_test.add_send(bytes.fromhex(m["payload"].replace(" ", ""))) elif m["type"] == "recv": this_test.add_recv(bytes.fromhex(m["payload"].replace(" ", "")), c) elif m["type"] == "publish": this_test.add_publish(m, c) total += 1 try: sock = mosq_test.client_connect_only(hostname=hostname, port=port, timeout=2) this_test.process_all(sock) print("\033[32m" + tname + "\033[0m") succeeded += 1 except ValueError as e: print("\033[31m" + tname + " failed: " + str(e) + "\033[0m") rc = 1 except ConnectionResetError as e: print("\033[31m" + tname + " failed: " + str(e) + "\033[0m") rc = 1 except socket.timeout as e: print("\033[31m" + tname + " failed: " + str(e) + "\033[0m") rc = 1 except mosq_test.TestError as e: print("\033[31m" + tname + " failed: " + str(e) + "\033[0m") rc = 1 print("%d tests total\n%d tests succeeded" % (total, succeeded)) return rc hostname = "localhost" port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port, nolog=True) rc = 0 try: rc = do_test(hostname=hostname, port=port) finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: #print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/prop_subpub_helper.py000077500000000000000000000033721450213760600223760ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client subscribed to a topic receives its own message sent to that topic. # Does a given property get sent through? # MQTT v5 from mosq_test_helper import * def prop_subpub_helper(props_out, props_in, expect_proto_error=False): rc = 1 mid = 53 keepalive = 60 connect_packet = mosq_test.gen_connect("subpub-qos0-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) subscribe_packet = mosq_test.gen_subscribe(mid, "subpub/qos0", 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) publish_packet_out = mosq_test.gen_publish("subpub/qos0", qos=0, payload="message", proto_ver=5, properties=props_out) publish_packet_expected = mosq_test.gen_publish("subpub/qos0", qos=0, payload="message", proto_ver=5, properties=props_in) disconnect_packet = mosq_test.gen_disconnect(reason_code=mqtt5_rc.MQTT_RC_PROTOCOL_ERROR, proto_ver=5) port = mosq_test.get_port() broker = mosq_test.start_broker(filename=os.path.basename(__file__), port=port) try: sock = mosq_test.do_client_connect(connect_packet, connack_packet, timeout=20, port=port) mosq_test.do_send_receive(sock, subscribe_packet, suback_packet, "suback") if expect_proto_error: mosq_test.do_send_receive(sock, publish_packet_out, disconnect_packet, "publish") else: mosq_test.do_send_receive(sock, publish_packet_out, publish_packet_expected, "publish") rc = 0 sock.close() except mosq_test.TestError: pass finally: broker.terminate() broker.wait() (stdo, stde) = broker.communicate() if rc: print(stde.decode('utf-8')) exit(rc) mosquitto-2.0.18/test/broker/readme.txt000066400000000000000000000006301450213760600201120ustar00rootroot00000000000000----- Broker Tests ----- This folder contains a number of tests to exercise the functionality of the broker. Feel free to add more. Numbering is as follows: 01: Connection tests 02: Subscribe/unsubscribe tests 03: Publish tests 04: Retained message tests 05: Session management tests 06: Bridge tests 07: Will tests 08: TLS tests 09: Auth tests 10: Listener tests 11: Persistence tests 12: Property tests mosquitto-2.0.18/test/broker/test.py000077500000000000000000000173631450213760600174630ustar00rootroot00000000000000#!/usr/bin/env python3 import mosq_test_helper import ptest tests = [ #(ports required, 'path'), (1, './01-bad-initial-packets.py'), (1, './01-connect-575314.py'), (1, './01-connect-allow-anonymous.py'), (1, './01-connect-disconnect-v5.py'), (1, './01-connect-max-connections.py'), (1, './01-connect-max-keepalive.py'), (1, './01-connect-take-over.py'), (1, './01-connect-uname-no-password-denied.py'), (1, './01-connect-uname-or-anon.py'), (1, './01-connect-uname-password-denied-no-will.py'), (1, './01-connect-uname-password-denied.py'), (1, './01-connect-windows-line-endings.py'), (2, './01-connect-zero-length-id.py'), (1, './02-shared-qos0-v5.py'), (1, './02-subhier-crash.py'), (1, './02-subpub-qos0-long-topic.py'), (1, './02-subpub-qos0-oversize-payload.py'), (1, './02-subpub-qos0-queued-bytes.py'), (1, './02-subpub-qos0-retain-as-publish.py'), (1, './02-subpub-qos0-send-retain.py'), (1, './02-subpub-qos0-subscription-id.py'), (1, './02-subpub-qos0-topic-alias-unknown.py'), (1, './02-subpub-qos0-topic-alias.py'), (1, './02-subpub-qos1-message-expiry-retain.py'), (1, './02-subpub-qos1-message-expiry-will.py'), (1, './02-subpub-qos1-message-expiry.py'), (1, './02-subpub-qos1-nolocal.py'), (1, './02-subpub-qos1-oversize-payload.py'), (1, './02-subpub-qos1.py'), (1, './02-subpub-qos2-1322.py'), (1, './02-subpub-qos2-max-inflight-bytes.py'), (1, './02-subpub-qos2-pubrec-error.py'), (1, './02-subpub-qos2-receive-maximum-1.py'), (1, './02-subpub-qos2-receive-maximum-2.py'), (1, './02-subpub-qos2.py'), (1, './02-subpub-recover-subscriptions.py'), (1, './02-subscribe-dollar-v5.py'), (1, './02-subscribe-invalid-utf8.py'), (1, './02-subscribe-long-topic.py'), (1, './02-subscribe-persistence-flipflop.py'), #(1, './03-publish-qos1-queued-bytes.py'), (1, './03-pattern-matching.py'), (1, './03-publish-b2c-disconnect-qos1.py'), (1, './03-publish-b2c-disconnect-qos2.py'), (1, './03-publish-b2c-qos1-len.py'), (1, './03-publish-b2c-qos2-len.py'), (1, './03-publish-c2b-disconnect-qos2.py'), (1, './03-publish-c2b-qos2-len.py'), (1, './03-publish-dollar-v5.py'), (1, './03-publish-dollar.py'), (1, './03-publish-invalid-utf8.py'), (1, './03-publish-long-topic.py'), (1, './03-publish-qos1-max-inflight-expire.py'), (1, './03-publish-qos1-max-inflight.py'), (1, './03-publish-qos1-no-subscribers-v5.py'), (1, './03-publish-qos1-retain-disabled.py'), (1, './03-publish-qos1.py'), (1, './03-publish-qos2-dup.py'), (1, './03-publish-qos2-max-inflight.py'), (1, './03-publish-qos2.py'), (1, './04-retain-check-source-persist.py'), (1, './04-retain-check-source.py'), (1, './04-retain-clear-multiple.py'), (1, './04-retain-qos0-clear.py'), (1, './04-retain-qos0-fresh.py'), (1, './04-retain-qos0-repeated.py'), (1, './04-retain-qos0.py'), (1, './04-retain-qos1-qos0.py'), (1, './04-retain-upgrade-outgoing-qos.py'), (2, './04-retain-check-source-persist-diff-port.py'), (1, './05-clean-session-qos1.py'), (1, './05-session-expiry-v5.py'), (2, './06-bridge-b2br-disconnect-qos1.py'), (2, './06-bridge-b2br-disconnect-qos2.py'), (2, './06-bridge-b2br-late-connection-retain.py'), (2, './06-bridge-b2br-late-connection.py'), (2, './06-bridge-b2br-remapping.py'), (2, './06-bridge-br2b-disconnect-qos1.py'), (2, './06-bridge-br2b-disconnect-qos2.py'), (2, './06-bridge-br2b-remapping.py'), (2, './06-bridge-clean-session-csF-lcsF.py'), (2, './06-bridge-clean-session-csF-lcsN.py'), (2, './06-bridge-clean-session-csF-lcsT.py'), (2, './06-bridge-clean-session-csT-lcsF.py'), (2, './06-bridge-clean-session-csT-lcsN.py'), (2, './06-bridge-clean-session-csT-lcsT.py'), (2, './06-bridge-fail-persist-resend-qos1.py'), (2, './06-bridge-fail-persist-resend-qos2.py'), (1, './06-bridge-no-local.py'), (2, './06-bridge-outgoing-retain.py'), (3, './06-bridge-per-listener-settings.py'), (2, './06-bridge-reconnect-local-out.py'), (1, './07-will-control.py'), (1, './07-will-delay-invalid-573191.py'), (1, './07-will-delay-reconnect.py'), (1, './07-will-delay-recover.py'), (1, './07-will-delay-session-expiry.py'), (1, './07-will-delay-session-expiry2.py'), (1, './07-will-delay.py'), (1, './07-will-disconnect-with-will.py'), (1, './07-will-invalid-utf8.py'), (1, './07-will-no-flag.py'), (1, './07-will-null-topic.py'), (1, './07-will-null.py'), (1, './07-will-oversize-payload.py'), (1, './07-will-per-listener.py'), (1, './07-will-properties.py'), (1, './07-will-qos0.py'), (1, './07-will-reconnect-1273.py'), (1, './07-will-takeover.py'), (2, './08-ssl-bridge.py'), (2, './08-ssl-connect-cert-auth-crl.py'), (2, './08-ssl-connect-cert-auth-expired.py'), (2, './08-ssl-connect-cert-auth-revoked.py'), (2, './08-ssl-connect-cert-auth-without.py'), (2, './08-ssl-connect-cert-auth.py'), (2, './08-ssl-connect-identity.py'), (2, './08-ssl-connect-no-auth-wrong-ca.py'), (2, './08-ssl-connect-no-auth.py'), (2, './08-ssl-connect-no-identity.py'), (1, './08-ssl-hup-disconnect.py'), (2, './08-tls-psk-pub.py'), (3, './08-tls-psk-bridge.py'), (1, './09-acl-access-variants.py'), (1, './09-acl-change.py'), (1, './09-acl-empty-file.py'), (1, './09-auth-bad-method.py'), (1, './09-extended-auth-change-username.py'), (1, './09-extended-auth-multistep-reauth.py'), (1, './09-extended-auth-multistep.py'), (1, './09-extended-auth-reauth.py'), (1, './09-extended-auth-single.py'), (1, './09-plugin-acl-change.py'), (1, './09-plugin-auth-acl-pub.py'), (1, './09-plugin-auth-acl-sub-denied.py'), (1, './09-plugin-auth-acl-sub.py'), (1, './09-plugin-auth-context-params.py'), (1, './09-plugin-auth-defer-unpwd-fail.py'), (1, './09-plugin-auth-defer-unpwd-success.py'), (1, './09-plugin-auth-msg-params.py'), (1, './09-plugin-auth-unpwd-fail.py'), (1, './09-plugin-auth-unpwd-success.py'), (1, './09-plugin-auth-v2-unpwd-fail.py'), (1, './09-plugin-auth-v2-unpwd-success.py'), (1, './09-plugin-publish.py'), (1, './09-plugin-tick.py'), (1, './09-pwfile-parse-invalid.py'), (2, './10-listener-mount-point.py'), (1, './11-message-expiry.py'), (1, './11-persistent-subscription.py'), (1, './11-persistent-subscription-v5.py'), (1, './11-persistent-subscription-no-local.py'), (1, './11-pub-props.py'), (1, './11-subscription-id.py'), (1, './12-prop-assigned-client-identifier.py'), (1, './12-prop-maximum-packet-size-broker.py'), (1, './12-prop-maximum-packet-size-publish-qos1.py'), (1, './12-prop-maximum-packet-size-publish-qos2.py'), (1, './12-prop-response-topic-correlation-data.py'), (1, './12-prop-response-topic.py'), (1, './12-prop-server-keepalive.py'), (1, './12-prop-subpub-content-type.py'), (1, './12-prop-subpub-payload-format.py'), (1, './13-malformed-publish-v5.py'), (1, './13-malformed-subscribe-v5.py'), (1, './13-malformed-unsubscribe-v5.py'), (1, './14-dynsec-acl.py'), (1, './14-dynsec-anon-group.py'), (1, './14-dynsec-auth.py'), (1, './14-dynsec-client.py'), (1, './14-dynsec-client-invalid.py'), (1, './14-dynsec-default-access.py'), (1, './14-dynsec-disable-client.py'), (1, './14-dynsec-group.py'), (1, './14-dynsec-group-invalid.py'), (1, './14-dynsec-modify-client.py'), (1, './14-dynsec-modify-group.py'), (1, './14-dynsec-modify-role.py'), (1, './14-dynsec-plugin-invalid.py'), (1, './14-dynsec-role.py'), (1, './14-dynsec-role-invalid.py'), ] ptest.run_tests(tests) mosquitto-2.0.18/test/client/000077500000000000000000000000001450213760600161075ustar00rootroot00000000000000mosquitto-2.0.18/test/client/Makefile000066400000000000000000000001371450213760600175500ustar00rootroot00000000000000.PHONY: all check test ptest clean all : check : test ptest : test test : ./test.sh clean: mosquitto-2.0.18/test/client/test.sh000077500000000000000000000030721450213760600174270ustar00rootroot00000000000000#!/bin/bash # Very basic client testing. set -e export BASE_PATH=../../ export LD_LIBRARY_PATH=${BASE_PATH}/lib export PORT=1888 export SUB_TIMEOUT=1 # Start broker ../../src/mosquitto -p ${PORT} 2>/dev/null & export MOSQ_PID=$! sleep 0.5 # Kill broker on exit trap "kill $MOSQ_PID" EXIT # Simple subscribe test - single message from $SYS ${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t '$SYS/broker/uptime' >/dev/null echo "Simple subscribe ok" # Simple publish/subscribe test - single message from mosquitto_pub ${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C 1 -t 'single/test' >/dev/null & export SUB_PID=$! ${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'single/test' -m 'single-test' kill ${SUB_PID} 2>/dev/null || true echo "Simple publish/subscribe ok" # Publish a file and subscribe, do we get at least that many lines? export TEST_LINES=$(wc -l test.sh | cut -d' ' -f1) ${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null & export SUB_PID=$! ${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'file-publish' -f ./test.sh kill ${SUB_PID} 2>/dev/null || true echo "File publish ok" # Publish a file from stdin and subscribe, do we get at least that many lines? export TEST_LINES=$(wc -l test.sh | cut -d' ' -f1) ${BASE_PATH}/client/mosquitto_sub -p ${PORT} -W ${SUB_TIMEOUT} -C ${TEST_LINES} -t 'file-publish' >/dev/null & export SUB_PID=$! ${BASE_PATH}/client/mosquitto_pub -p ${PORT} -t 'file-publish' -l < ./test.sh kill ${SUB_PID} 2>/dev/null || true echo "stdin publish ok" mosquitto-2.0.18/test/lib/000077500000000000000000000000001450213760600153775ustar00rootroot00000000000000mosquitto-2.0.18/test/lib/01-con-discon-success.py000077500000000000000000000030131450213760600216710ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect and subsequent disconnect. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id 01-con-discon-success # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("01-con-discon-success", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: #client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/01-keepalive-pingreq.py000077500000000000000000000030561450213760600216060ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a pingreq after the keepalive time # The client should connect to port 1888 with keepalive=4, clean session set, # and client id 01-keepalive-pingreq # The client should send a PINGREQ message after the appropriate amount of time # (4 seconds after no traffic). from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 5 connect_packet = mosq_test.gen_connect("01-keepalive-pingreq", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) pingreq_packet = mosq_test.gen_pingreq() pingresp_packet = mosq_test.gen_pingresp() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(keepalive+10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "pingreq", pingreq_packet) time.sleep(1.0) conn.send(pingresp_packet) mosq_test.expect_packet(conn, "pingreq", pingreq_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/01-no-clean-session.py000077500000000000000000000022311450213760600213450ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect with clean session not set. # The client should connect to port 1888 with keepalive=60, clean session not # set, and client id 01-no-clean-session. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("01-no-clean-session", clean_session=False, keepalive=keepalive) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.expect_packet(conn, "connect", connect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/01-server-keepalive-pingreq.py000077500000000000000000000031351450213760600231100ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a pingreq after the keepalive time # Client sets a keepalive of 60 seconds, but receives a server keepalive to set # it back to 4 seconds. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 server_keepalive = 4 connect_packet = mosq_test.gen_connect("01-server-keepalive-pingreq", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_SERVER_KEEP_ALIVE, server_keepalive) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) pingreq_packet = mosq_test.gen_pingreq() pingresp_packet = mosq_test.gen_pingresp() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(server_keepalive+10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "pingreq", pingreq_packet) time.sleep(1.0) conn.send(pingresp_packet) mosq_test.expect_packet(conn, "pingreq", pingreq_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/01-unpwd-set.py000077500000000000000000000023141450213760600201200ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect with a username and password. # The client should connect to port 1888 with keepalive=60, clean session set, # client id 01-unpwd-set, username set to uname and password set to ;'[08gn=# from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("01-unpwd-set", keepalive=keepalive, username="uname", password=";'[08gn=#") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.expect_packet(conn, "connect", connect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/01-will-set.py000077500000000000000000000025421450213760600177350ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect with a will. # Will QoS=1, will retain=1. # The client should connect to port 1888 with keepalive=60, clean session set, # client id 01-will-set will topic set to topic/on/unexpected/disconnect , will # payload set to "will message", will qos set to 1 and will retain set. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("01-will-set", keepalive=keepalive, will_topic="topic/on/unexpected/disconnect", will_qos=1, will_retain=True, will_payload=b"will message") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.expect_packet(conn, "connect", connect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/01-will-unpwd-set.py000077500000000000000000000026621450213760600210730ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect with a will, username and password. # The client should connect to port 1888 with keepalive=60, clean session set, # client id 01-will-unpwd-set , will topic set to "will-topic", will payload # set to "will message", will qos=2, will retain not set, username set to # "oibvvwqw" and password set to "#'^2hg9a&nm38*us". from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("01-will-unpwd-set", keepalive=keepalive, username="oibvvwqw", password="#'^2hg9a&nm38*us", will_topic="will-topic", will_qos=2, will_payload=b"will message") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.expect_packet(conn, "connect", connect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/02-subscribe-qos0.py000077500000000000000000000037011450213760600210350ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct SUBSCRIBE to a topic with QoS 0. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id subscribe-qos0-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE # message to subscribe to topic "qos0/test" with QoS=0. If rc!=0, the client # should exit with an error. # Upon receiving the correct SUBSCRIBE message, the test will reply with a # SUBACK message with the accepted QoS set to 0. On receiving the SUBACK # message, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subscribe-qos0-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "qos0/test", 0) suback_packet = mosq_test.gen_suback(mid, 0) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, "subscribe") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/02-subscribe-qos1.py000077500000000000000000000037011450213760600210360ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct SUBSCRIBE to a topic with QoS 1. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id subscribe-qos1-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE # message to subscribe to topic "qos1/test" with QoS=1. If rc!=0, the client # should exit with an error. # Upon receiving the correct SUBSCRIBE message, the test will reply with a # SUBACK message with the accepted QoS set to 1. On receiving the SUBACK # message, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subscribe-qos1-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "qos1/test", 1) suback_packet = mosq_test.gen_suback(mid, 1) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, "subscribe") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/02-subscribe-qos2.py000077500000000000000000000037011450213760600210370ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct SUBSCRIBE to a topic with QoS 2. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id subscribe-qos2-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a SUBSCRIBE # message to subscribe to topic "qos2/test" with QoS=2. If rc!=0, the client # should exit with an error. # Upon receiving the correct SUBSCRIBE message, the test will reply with a # SUBACK message with the accepted QoS set to 2. On receiving the SUBACK # message, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("subscribe-qos2-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "qos2/test", 2) suback_packet = mosq_test.gen_suback(mid, 2) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, "subscribe") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/02-unsubscribe-multiple-v5.py000077500000000000000000000034031450213760600227000ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a v5 client sends a correct UNSUBSCRIBE packet with multiple # topics, and handles the UNSUBACK. from mosq_test_helper import * port = mosq_test.get_lib_port() keepalive = 60 connect_packet = mosq_test.gen_connect("unsubscribe-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 subscribe_packet = mosq_test.gen_subscribe(mid, "unsubscribe/test", 2, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 2, proto_ver=5) mid = 2 unsubscribe_packet = mosq_test.gen_unsubscribe_multiple(mid, ["unsubscribe/test", "no-sub"], proto_ver=5) unsuback_packet = mosq_test.gen_unsuback(mid, reason_code=[0, 17], proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) rc = 1 try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, subscribe_packet, suback_packet, "subscribe") mosq_test.do_receive_send(conn, unsubscribe_packet, unsuback_packet, "unsubscribe") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/02-unsubscribe-v5.py000077500000000000000000000027221450213760600210520ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a v5 client sends a correct UNSUBSCRIBE packet, and handles the UNSUBACK. from mosq_test_helper import * port = mosq_test.get_lib_port() keepalive = 60 connect_packet = mosq_test.gen_connect("unsubscribe-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 unsubscribe_packet = mosq_test.gen_unsubscribe(mid, "unsubscribe/test", proto_ver=5) unsuback_packet = mosq_test.gen_unsuback(mid, proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, unsubscribe_packet, unsuback_packet, "unsubscribe") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/02-unsubscribe.py000077500000000000000000000025751450213760600205300ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct UNSUBSCRIBE packet. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("unsubscribe-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 unsubscribe_packet = mosq_test.gen_unsubscribe(mid, "unsubscribe/test") unsuback_packet = mosq_test.gen_unsuback(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, unsubscribe_packet, unsuback_packet, "unsubscribe") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-publish-b2c-qos1-unexpected-puback.py000077500000000000000000000026701450213760600246010ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 5 connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 13423 puback_packet = mosq_test.gen_puback(mid) pingreq_packet = mosq_test.gen_pingreq() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) if mosq_test.expect_packet(conn, "connect", connect_packet): conn.send(connack_packet) conn.send(puback_packet) if mosq_test.expect_packet(conn, "pingreq", pingreq_packet): rc = 0 conn.close() finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if rc != 0 or client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-b2c-qos1.py000077500000000000000000000040041450213760600211650ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client responds correctly to a PUBLISH with QoS 1. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos1-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK the client should verify that rc==0. # The test will send the client a PUBLISH message with topic # "pub/qos1/receive", payload of "message", QoS=1 and mid=123. The client # should handle this as per the spec by sending a PUBACK message. # The client should then exit with return code==0. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 123 publish_packet = mosq_test.gen_publish("pub/qos1/receive", qos=1, mid=mid, payload="message") puback_packet = mosq_test.gen_puback(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_send_receive(conn, publish_packet, puback_packet, "puback") rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-b2c-qos2-len.py000077500000000000000000000050211450213760600217420ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether a v5 client handles a v5 PUBREL with all combinations # of with/without reason code and properties. from mosq_test_helper import * mid = 56 def len_test(test, pubrel_packet): port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) publish_packet = mosq_test.gen_publish("len/qos2/test", qos=2, mid=mid, payload="message", proto_ver=5) pubrec_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubcomp_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(15) mosq_test.expect_packet(conn, "connect", connect_packet) conn.send(connack_packet) mosq_test.do_send_receive(conn, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(conn, pubrel_packet, pubcomp_packet, "pubcomp") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() if rc != 0: print(test) exit(rc) # No reason code, no properties pubrel_packet = mosq_test.gen_pubrel(mid) len_test("qos2 len 2", pubrel_packet) # Reason code, no properties pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5, reason_code=0x00) len_test("qos2 len 3", pubrel_packet) # Reason code, empty properties pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5, reason_code=0x00, properties="") len_test("qos2 len 4", pubrel_packet) # Reason code, one property props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5, reason_code=0x00, properties=props) len_test("qos2 len >5", pubrel_packet) mosquitto-2.0.18/test/lib/03-publish-b2c-qos2-unexpected-pubcomp.py000077500000000000000000000026731450213760600250050ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 5 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 13423 pubcomp_packet = mosq_test.gen_pubcomp(mid) pingreq_packet = mosq_test.gen_pingreq() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) if mosq_test.expect_packet(conn, "connect", connect_packet): conn.send(connack_packet) conn.send(pubcomp_packet) if mosq_test.expect_packet(conn, "pingreq", pingreq_packet): rc = 0 conn.close() finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if rc != 0 or client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-b2c-qos2-unexpected-pubrel.py000077500000000000000000000040171450213760600246230ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() pubrel_unexpected = mosq_test.gen_pubrel(1000) pubcomp_unexpected = mosq_test.gen_pubcomp(1000) mid = 13423 publish_packet = mosq_test.gen_publish("pub/qos2/receive", qos=2, mid=mid, payload="message") pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) publish_quit_packet = mosq_test.gen_publish("quit", qos=0, payload="quit") sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) if mosq_test.expect_packet(conn, "connect", connect_packet): conn.send(connack_packet) conn.send(pubrel_unexpected) if mosq_test.expect_packet(conn, "pubcomp", pubcomp_unexpected): conn.send(publish_packet) if mosq_test.expect_packet(conn, "pubrec", pubrec_packet): conn.send(pubrel_packet) if mosq_test.expect_packet(conn, "pubcomp", pubcomp_packet): conn.send(publish_quit_packet) rc = 0 conn.close() finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-b2c-qos2.py000077500000000000000000000050471450213760600211760ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client responds correctly to a PUBLISH with QoS 1. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos2-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK the client should verify that rc==0. # The test will send the client a PUBLISH message with topic # "pub/qos2/receive", payload of "message", QoS=2 and mid=13423. The client # should handle this as per the spec by sending a PUBREC message. # The test will not respond to the first PUBREC message, so the client must # resend the PUBREC message with dup=1. Note that to keep test durations low, a # message retry timeout of less than 10 seconds is required for this test. # On receiving the second PUBREC with dup==1, the test will send the correct # PUBREL message. The client should respond to this with the correct PUBCOMP # message and then exit with return code=0. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 13423 publish_packet = mosq_test.gen_publish("pub/qos2/receive", qos=2, mid=mid, payload="message") pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_send_receive(conn, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(conn, pubrel_packet, pubcomp_packet, "pubcomp") rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos1-disconnect.py000077500000000000000000000035031450213760600233170ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 1, then responds correctly to a disconnect. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message") publish_packet_dup = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", dup=True) puback_packet = mosq_test.gen_puback(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(15) mosq_test.expect_packet(conn, "connect", connect_packet) conn.send(connack_packet) mosq_test.expect_packet(conn, "publish", publish_packet) # Disconnect client. It should reconnect. conn.close() (conn, address) = sock.accept() conn.settimeout(15) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_packet_dup, puback_packet, "retried publish") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos1-len.py000077500000000000000000000044611450213760600217500ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether a v5 client handles a v5 PUBACK with all combinations # of with/without reason code and properties. from mosq_test_helper import * def len_test(test, puback_packet): port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(15) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_packet, puback_packet, "publish") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() if rc != 0: print(test) exit(rc) # No reason code, no properties puback_packet = mosq_test.gen_puback(1) len_test("qos1 len 2", puback_packet) # Reason code, no properties puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00) len_test("qos1 len 3", puback_packet) # Reason code, empty properties puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties="") len_test("qos1 len 4", puback_packet) # Reason code, one property props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") puback_packet = mosq_test.gen_puback(1, proto_ver=5, reason_code=0x00, properties=props) len_test("qos1 len >5", puback_packet) mosquitto-2.0.18/test/lib/03-publish-c2b-qos1-receive-maximum.py000077500000000000000000000060071450213760600242650ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client responds correctly to multiple PUBLISH with QoS 1, with # receive maximum set to 3. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 3) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 publish_1_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) puback_1_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 2 publish_2_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) puback_2_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 3 publish_3_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) puback_3_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 4 publish_4_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) puback_4_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 5 publish_5_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) puback_5_packet = mosq_test.gen_puback(mid, proto_ver=5) mid = 6 publish_6_packet = mosq_test.gen_publish("topic", qos=1, mid=mid, payload="12345", proto_ver=5) puback_6_packet = mosq_test.gen_puback(mid, proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish 1", publish_1_packet) mosq_test.expect_packet(conn, "publish 2", publish_2_packet) mosq_test.expect_packet(conn, "publish 3", publish_3_packet) conn.send(puback_1_packet) conn.send(puback_2_packet) mosq_test.expect_packet(conn, "publish 4", publish_4_packet) mosq_test.expect_packet(conn, "publish 5", publish_5_packet) conn.send(puback_3_packet) mosq_test.expect_packet(conn, "publish 6", publish_6_packet) conn.send(puback_4_packet) conn.send(puback_5_packet) conn.send(puback_6_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos1-timeout.py000077500000000000000000000047071450213760600226630ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 1 and responds to a delay. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos1-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK the client should verify that rc==0. If not, it should exit with # return code=1. # On a successful CONNACK, the client should send a PUBLISH message with topic # "pub/qos1/test", payload "message" and QoS=1. # The test will not respond to the first PUBLISH message, so the client must # resend the PUBLISH message with dup=1. Note that to keep test durations low, a # message retry timeout of less than 10 seconds is required for this test. # On receiving the second PUBLISH message, the test will send the correct # PUBACK response. On receiving the correct PUBACK response, the client should # send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos1-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 publish_packet = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message") publish_packet_dup = mosq_test.gen_publish("pub/qos1/test", qos=1, mid=mid, payload="message", dup=True) puback_packet = mosq_test.gen_puback(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) # Delay for > 3 seconds (message retry time) mosq_test.do_receive_send(conn, publish_packet_dup, puback_packet, "dup publish") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-disconnect.py000077500000000000000000000044121450213760600233200ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 2 and responds to a disconnect. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message") publish_dup_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", dup=True) pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) # Disconnect client. It should reconnect. conn.close() (conn, address) = sock.accept() conn.settimeout(15) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_dup_packet, pubrec_packet, "retried publish") mosq_test.expect_packet(conn, "pubrel", pubrel_packet) # Disconnect client. It should reconnect. conn.close() (conn, address) = sock.accept() conn.settimeout(15) # Complete connection and message flow. mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, pubrel_packet, pubcomp_packet, "retried pubrel") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-len.py000077500000000000000000000056251450213760600217540ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether a v5 client handles a v5 PUBREC, PUBCOMP with all combinations # of with/without reason code and properties. from mosq_test_helper import * def len_test(test, pubrec_packet, pubcomp_packet): port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", proto_ver=5) pubrel_packet = mosq_test.gen_pubrel(mid, proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(15) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_packet, pubrec_packet, "publish") mosq_test.do_receive_send(conn, pubrel_packet, pubcomp_packet, "pubrel") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() if rc != 0: print(test) exit(rc) # No reason code, no properties pubrec_packet = mosq_test.gen_pubrec(1) pubcomp_packet = mosq_test.gen_pubcomp(1) len_test("qos2 len 2", pubrec_packet, pubcomp_packet) # Reason code, no properties pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00) pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00) len_test("qos2 len 3", pubrec_packet, pubcomp_packet) # Reason code, empty properties pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties="") pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties="") len_test("qos2 len 4", pubrec_packet, pubcomp_packet) # Reason code, one property props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") pubrec_packet = mosq_test.gen_pubrec(1, proto_ver=5, reason_code=0x00, properties=props) props = mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "key", "value") pubcomp_packet = mosq_test.gen_pubcomp(1, proto_ver=5, reason_code=0x00, properties=props) len_test("qos2 len >5", pubrec_packet, pubcomp_packet) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-maximum-qos-0.py000077500000000000000000000033461450213760600236060ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client correctly handles sending a message with QoS > maximum QoS. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_MAXIMUM_QOS, 0) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) publish_1_packet = mosq_test.gen_publish("maximum/qos/qos0", qos=0, payload="message", proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish 1", publish_1_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-maximum-qos-1.py000077500000000000000000000037521450213760600236100ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client correctly handles sending a message with QoS > maximum QoS. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_MAXIMUM_QOS, 1) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 publish_1_packet = mosq_test.gen_publish("maximum/qos/qos1", qos=1, mid=mid, payload="message", proto_ver=5) puback_1_packet = mosq_test.gen_puback(mid, proto_ver=5) publish_2_packet = mosq_test.gen_publish("maximum/qos/qos0", qos=0, payload="message", proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_1_packet, puback_1_packet, "publish 1") mosq_test.expect_packet(conn, "publish 2", publish_2_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-pubrec-error.py000077500000000000000000000045111450213760600235760ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client responds correctly when sending multiple PUBLISH with # QoS 2, with the broker rejecting the first PUBLISH by setting the reason code # in PUBACK to >= 0x80. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 publish_1_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="rejected", proto_ver=5) pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5, reason_code=0x80) mid = 2 publish_2_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="accepted", proto_ver=5) pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_1_packet, pubrec_1_packet, "publish 1") mosq_test.do_receive_send(conn, publish_2_packet, pubrec_2_packet, "publish 2") mosq_test.do_receive_send(conn, pubrel_2_packet, pubcomp_2_packet, "pubrel 2") rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-receive-maximum-1.py000077500000000000000000000072571450213760600244340ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client responds correctly to multiple PUBLISH with QoS 2, with # receive maximum set to 1. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 1) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 publish_1_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_1_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 2 publish_2_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 3 publish_3_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_3_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_3_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_3_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 4 publish_4_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_4_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_4_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_4_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 5 publish_5_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_5_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_5_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_5_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_1_packet, pubrec_1_packet, "publish 1") mosq_test.do_receive_send(conn, pubrel_1_packet, pubcomp_1_packet, "pubrel 1") mosq_test.do_receive_send(conn, publish_2_packet, pubrec_2_packet, "publish 2") mosq_test.do_receive_send(conn, pubrel_2_packet, pubcomp_2_packet, "pubrel 2") mosq_test.do_receive_send(conn, publish_3_packet, pubrec_3_packet, "publish 3") mosq_test.do_receive_send(conn, pubrel_3_packet, pubcomp_3_packet, "pubrel 3") mosq_test.do_receive_send(conn, publish_4_packet, pubrec_4_packet, "publish 4") mosq_test.do_receive_send(conn, pubrel_4_packet, pubcomp_4_packet, "pubrel 4") mosq_test.do_receive_send(conn, publish_5_packet, pubrec_5_packet, "publish 5") mosq_test.do_receive_send(conn, pubrel_5_packet, pubcomp_5_packet, "pubrel 5") rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-receive-maximum-2.py000077500000000000000000000074471450213760600244360ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client responds correctly to multiple PUBLISH with QoS 2, with # receive maximum set to 2. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 2) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props, property_helper=False) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) mid = 1 publish_1_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_1_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_1_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_1_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 2 publish_2_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_2_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_2_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_2_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 3 publish_3_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_3_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_3_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_3_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 4 publish_4_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_4_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_4_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_4_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) mid = 5 publish_5_packet = mosq_test.gen_publish("topic", qos=2, mid=mid, payload="12345", proto_ver=5) pubrec_5_packet = mosq_test.gen_pubrec(mid, proto_ver=5) pubrel_5_packet = mosq_test.gen_pubrel(mid, proto_ver=5) pubcomp_5_packet = mosq_test.gen_pubcomp(mid, proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish 1", publish_1_packet) mosq_test.expect_packet(conn, "publish 2", publish_2_packet) conn.send(pubrec_1_packet) conn.send(pubrec_2_packet) mosq_test.expect_packet(conn, "pubrel 1", pubrel_1_packet) mosq_test.expect_packet(conn, "pubrel 2", pubrel_2_packet) conn.send(pubcomp_1_packet) conn.send(pubcomp_2_packet) mosq_test.expect_packet(conn, "publish 3", publish_3_packet) mosq_test.expect_packet(conn, "publish 4", publish_4_packet) conn.send(pubrec_3_packet) conn.send(pubrec_4_packet) mosq_test.expect_packet(conn, "pubrel 3", pubrel_3_packet) mosq_test.expect_packet(conn, "pubrel 4", pubrel_4_packet) conn.send(pubcomp_3_packet) conn.send(pubcomp_4_packet) mosq_test.expect_packet(conn, "publish 5", publish_5_packet) conn.send(pubrec_5_packet) mosq_test.expect_packet(conn, "pubrel 5", pubrel_5_packet) conn.send(pubcomp_5_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: for i in range(0, 5): if client.returncode != None: break time.sleep(0.1) try: client.terminate() except OSError: pass client.wait() sock.close() if client.returncode != 0: exit(1) exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2-timeout.py000077500000000000000000000057471450213760600226710ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 1 and responds to a delay. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos2-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK the client should verify that rc==0. If not, it should exit with # return code=1. # On a successful CONNACK, the client should send a PUBLISH message with topic # "pub/qos2/test", payload "message" and QoS=2. # The test will not respond to the first PUBLISH message, so the client must # resend the PUBLISH message with dup=1. Note that to keep test durations low, a # message retry timeout of less than 10 seconds is required for this test. # On receiving the second PUBLISH message, the test will send the correct # PUBREC response. On receiving the correct PUBREC response, the client should # send a PUBREL message. # The test will not respond to the first PUBREL message, so the client must # resend the PUBREL message with dup=1. On receiving the second PUBREL message, # the test will send the correct PUBCOMP response. On receiving the correct # PUBCOMP response, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message") publish_dup_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", dup=True) pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) # Delay for > 3 seconds (message retry time) mosq_test.expect_packet(conn, "dup publish", publish_dup_packet) conn.send(pubrec_packet) mosq_test.expect_packet(conn, "pubrel", pubrel_packet) mosq_test.expect_packet(conn, "dup pubrel", pubrel_packet) conn.send(pubcomp_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-publish-c2b-qos2.py000077500000000000000000000053761450213760600212030ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 2. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos2-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK the client should verify that rc==0. If not, it should exit with # return code=1. # On a successful CONNACK, the client should send a PUBLISH message with topic # "pub/qos2/test", payload "message" and QoS=2. # The test will not respond to the first PUBLISH message, so the client must # resend the PUBLISH message with dup=1. Note that to keep test durations low, a # message retry timeout of less than 10 seconds is required for this test. # On receiving the second PUBLISH message, the test will send the correct # PUBREC response. On receiving the correct PUBREC response, the client should # send a PUBREL message. # The test will not respond to the first PUBREL message, so the client must # resend the PUBREL message with dup=1. On receiving the second PUBREL message, # the test will send the correct PUBCOMP response. On receiving the correct # PUBCOMP response, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos2-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() mid = 1 publish_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message") publish_dup_packet = mosq_test.gen_publish("pub/qos2/test", qos=2, mid=mid, payload="message", dup=True) pubrec_packet = mosq_test.gen_pubrec(mid) pubrel_packet = mosq_test.gen_pubrel(mid) pubcomp_packet = mosq_test.gen_pubcomp(mid) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_receive_send(conn, publish_packet, pubrec_packet, "publish") mosq_test.do_receive_send(conn, pubrel_packet, pubcomp_packet, "pubrel") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-publish-qos0-no-payload.py000077500000000000000000000034571450213760600225740ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 0 and no payload. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos0-test-np # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a PUBLISH message # to topic "pub/qos0/no-payload/test" with zero length payload and QoS=0. If # rc!=0, the client should exit with an error. # After sending the PUBLISH message, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos0-test-np", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish("pub/qos0/no-payload/test", qos=0) disconnect_packet = mosq_test.gen_disconnect() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-publish-qos0.py000077500000000000000000000035401450213760600205240ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 0. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos0-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a PUBLISH message # to topic "pub/qos0/test" with payload "message" and QoS=0. If rc!=0, the # client should exit with an error. # After sending the PUBLISH message, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos0-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish("pub/qos0/test", qos=0, payload="message") disconnect_packet = mosq_test.gen_disconnect() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() if rc: (stdo, stde) = client.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-request-response-correlation.py000077500000000000000000000060541450213760600240440ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_lib_port() resp_topic = "response/topic" pub_topic = "request/topic" rc = 1 keepalive = 60 connect1_packet = mosq_test.gen_connect("request-test", keepalive=keepalive, proto_ver=5) connect2_packet = mosq_test.gen_connect("response-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, resp_topic, 0, proto_ver=5) subscribe2_packet = mosq_test.gen_subscribe(mid, pub_topic, 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, resp_topic) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "corridor") publish1_packet_incoming = mosq_test.gen_publish(pub_topic, qos=0, payload="action", proto_ver=5, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, resp_topic) props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "corridor") props += mqtt5_props.gen_string_pair_prop(mqtt5_props.PROP_USER_PROPERTY, "user", "data") publish1_packet_outgoing = mosq_test.gen_publish(pub_topic, qos=0, payload="action", proto_ver=5, properties=props) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CORRELATION_DATA, "corridor") publish2_packet = mosq_test.gen_publish(resp_topic, qos=0, payload="a response", proto_ver=5, properties=props) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client1 = mosq_test.start_client(filename="03-request-response-correlation-1.log", cmd=["c/03-request-response-correlation-1.test"], env=env, port=port) try: (conn1, address) = sock.accept() conn1.settimeout(10) client2 = mosq_test.start_client(filename="03-request-response-2.log", cmd=["c/03-request-response-2.test"], env=env, port=port) (conn2, address) = sock.accept() conn2.settimeout(10) mosq_test.do_receive_send(conn1, connect1_packet, connack_packet, "connect1") mosq_test.do_receive_send(conn2, connect2_packet, connack_packet, "connect2") mosq_test.do_receive_send(conn1, subscribe1_packet, suback_packet, "subscribe1") mosq_test.do_receive_send(conn2, subscribe2_packet, suback_packet, "subscribe2") mosq_test.expect_packet(conn1, "publish1", publish1_packet_incoming) conn2.send(publish1_packet_outgoing) mosq_test.expect_packet(conn2, "publish2", publish2_packet) rc = 0 conn1.close() conn2.close() except mosq_test.TestError: pass finally: client1.terminate() client1.wait() client2.terminate() client2.wait() if rc: (stdo, stde) = client1.communicate() print(stde) (stdo, stde) = client2.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/03-request-response.py000077500000000000000000000047141450213760600215260ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_lib_port() resp_topic = "response/topic" pub_topic = "request/topic" rc = 1 keepalive = 60 connect1_packet = mosq_test.gen_connect("request-test", keepalive=keepalive, proto_ver=5) connect2_packet = mosq_test.gen_connect("response-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 subscribe1_packet = mosq_test.gen_subscribe(mid, resp_topic, 0, proto_ver=5) subscribe2_packet = mosq_test.gen_subscribe(mid, pub_topic, 0, proto_ver=5) suback_packet = mosq_test.gen_suback(mid, 0, proto_ver=5) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, resp_topic) publish1_packet = mosq_test.gen_publish(pub_topic, qos=0, payload="action", proto_ver=5, properties=props) publish2_packet = mosq_test.gen_publish(resp_topic, qos=0, payload="a response", proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client1 = mosq_test.start_client(filename="03-request-response-1.log", cmd=["c/03-request-response-1.test"], env=env, port=port) try: (conn1, address) = sock.accept() conn1.settimeout(10) client2 = mosq_test.start_client(filename="03-request-response-2.log", cmd=["c/03-request-response-2.test"], env=env, port=port) (conn2, address) = sock.accept() conn2.settimeout(10) mosq_test.do_receive_send(conn1, connect1_packet, connack_packet, "connect1") mosq_test.do_receive_send(conn2, connect2_packet, connack_packet, "connect2") mosq_test.do_receive_send(conn1, subscribe1_packet, suback_packet, "subscribe1") mosq_test.do_receive_send(conn2, subscribe2_packet, suback_packet, "subscribe2") mosq_test.expect_packet(conn1, "publish1", publish1_packet) conn2.send(publish1_packet) mosq_test.expect_packet(conn2, "publish2", publish2_packet) rc = 0 conn1.close() conn2.close() except mosq_test.TestError: pass finally: client1.terminate() client1.wait() client2.terminate() client2.wait() if rc: (stdo, stde) = client1.communicate() print(stde) (stdo, stde) = client2.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/04-retain-qos0.py000077500000000000000000000023721450213760600203430ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct retained PUBLISH to a topic with QoS 0. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 mid = 16 connect_packet = mosq_test.gen_connect("retain-qos0-test", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) publish_packet = mosq_test.gen_publish("retain/qos0/test", qos=0, payload="retained message", retain=True) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() sock.close() exit(rc) mosquitto-2.0.18/test/lib/08-ssl-bad-cacert.py000077500000000000000000000007731450213760600207740ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) rc = 1 client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env) client.wait() rc = client.returncode exit(rc) mosquitto-2.0.18/test/lib/08-ssl-connect-cert-auth-enc.py000077500000000000000000000037761450213760600231030ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect and subsequent disconnect when using SSL. # Client must provide a certificate. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id 08-ssl-connect-crt-auth # It should use the CA certificate ssl/test-root-ca.crt for verifying the server. # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. from mosq_test_helper import * port = mosq_test.get_lib_port() if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("08-ssl-connect-crt-auth-enc", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt") context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key") context.verify_mode = ssl.CERT_REQUIRED ssock = context.wrap_socket(sock, server_side=True) ssock.settimeout(10) ssock.bind(('', port)) ssock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = ssock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() ssock.close() exit(rc) mosquitto-2.0.18/test/lib/08-ssl-connect-cert-auth.py000077500000000000000000000037721450213760600223340ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect and subsequent disconnect when using SSL. # Client must provide a certificate. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id 08-ssl-connect-crt-auth # It should use the CA certificate ssl/test-root-ca.crt for verifying the server. # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. from mosq_test_helper import * port = mosq_test.get_lib_port() if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("08-ssl-connect-crt-auth", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt") context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key") context.verify_mode = ssl.CERT_REQUIRED ssock = context.wrap_socket(sock, server_side=True) ssock.settimeout(10) ssock.bind(('', port)) ssock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = ssock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() ssock.close() exit(rc) mosquitto-2.0.18/test/lib/08-ssl-connect-no-auth.py000077500000000000000000000036501450213760600220060ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client produces a correct connect and subsequent disconnect when using SSL. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id 08-ssl-connect-no-auth # It should use the CA certificate ssl/test-ca.crt for verifying the server. # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a DISCONNECT # message. If rc!=0, the client should exit with an error. from mosq_test_helper import * port = mosq_test.get_lib_port() if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("08-ssl-connect-no-auth", keepalive=keepalive) connack_packet = mosq_test.gen_connack(rc=0) disconnect_packet = mosq_test.gen_disconnect() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt") context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key") ssock = context.wrap_socket(sock, server_side=True) ssock.settimeout(10) ssock.bind(('', port)) ssock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = ssock.accept() conn.settimeout(100) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() ssock.close() exit(rc) mosquitto-2.0.18/test/lib/08-ssl-fake-cacert.py000077500000000000000000000024341450213760600211500ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_lib_port() if sys.version < '2.7': print("WARNING: SSL not supported on Python 2.6") exit(0) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH, cafile="../ssl/all-ca.crt") context.load_cert_chain(certfile="../ssl/server.crt", keyfile="../ssl/server.key") context.verify_mode = ssl.CERT_REQUIRED ssock = context.wrap_socket(sock, server_side=True) ssock.settimeout(10) ssock.bind(('', port)) ssock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = ssock.accept() conn.close() except ssl.SSLError: # Expected error due to ca certs not matching. pass except mosq_test.TestError: pass finally: time.sleep(1.0) try: client.terminate() except OSError: pass client.wait() ssock.close() if client.returncode == 0: exit(0) else: exit(1) mosquitto-2.0.18/test/lib/09-util-topic-tokenise.py000077500000000000000000000006261450213760600221160ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * rc = 1 client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env) client.wait() exit(client.returncode) mosquitto-2.0.18/test/lib/11-prop-oversize-packet.py000077500000000000000000000034271450213760600222720ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client publishing an oversize packet correctly. # The client should try to publish a message that is too big, then the one below which is ok. # It should also try to subscribe with a too large topic from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("publish-qos0-test", keepalive=keepalive, proto_ver=5) props = mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_MAXIMUM_PACKET_SIZE, 30) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5, properties=props) bad_publish_packet = mosq_test.gen_publish("pub/test", qos=0, payload="0123456789012345678", proto_ver=5) publish_packet = mosq_test.gen_publish("pub/test", qos=0, payload="012345678901234567", proto_ver=5) disconnect_packet = mosq_test.gen_disconnect() sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() if rc: (stdo, stde) = client.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/11-prop-recv-qos0.py000077500000000000000000000031541450213760600207730ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether the v5 message callback gets the properties from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("prop-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "plain/text") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "msg/123") publish_packet = mosq_test.gen_publish("prop/test", qos=0, payload="message", proto_ver=5, properties=props) ok_packet = mosq_test.gen_publish("ok", qos=0, payload="ok", proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_send_receive(conn, publish_packet, ok_packet, "ok") rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() if rc: (stdo, stde) = client.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/11-prop-recv-qos1.py000077500000000000000000000034011450213760600207670ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether the v5 message callback gets the properties from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("prop-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "plain/text") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "msg/123") publish_packet = mosq_test.gen_publish("prop/test", mid=mid, qos=1, payload="message", proto_ver=5, properties=props) puback_packet = mosq_test.gen_puback(mid=mid, proto_ver=5) ok_packet = mosq_test.gen_publish("ok", qos=0, payload="ok", proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") conn.send(publish_packet) mosq_test.expect_packet(conn, "puback", puback_packet) mosq_test.expect_packet(conn, "ok", ok_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() if rc: (stdo, stde) = client.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/11-prop-recv-qos2.py000077500000000000000000000036721450213760600210020ustar00rootroot00000000000000#!/usr/bin/env python3 # Check whether the v5 message callback gets the properties from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("prop-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) mid = 1 props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "plain/text") props += mqtt5_props.gen_string_prop(mqtt5_props.PROP_RESPONSE_TOPIC, "msg/123") publish_packet = mosq_test.gen_publish("prop/test", mid=mid, qos=2, payload="message", proto_ver=5, properties=props) pubrec_packet = mosq_test.gen_pubrec(mid=mid, proto_ver=5) pubrel_packet = mosq_test.gen_pubrel(mid=mid, proto_ver=5) pubcomp_packet = mosq_test.gen_pubcomp(mid=mid, proto_ver=5) ok_packet = mosq_test.gen_publish("ok", qos=0, payload="ok", proto_ver=5) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.do_send_receive(conn, publish_packet, pubrec_packet, "pubrec") mosq_test.do_send_receive(conn, pubrel_packet, pubcomp_packet, "pubcomp") mosq_test.expect_packet(conn, "ok", ok_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() if rc: (stdo, stde) = client.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/11-prop-send-content-type.py000077500000000000000000000027251450213760600225370ustar00rootroot00000000000000#!/usr/bin/env python3 from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("prop-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) props = mqtt5_props.gen_string_prop(mqtt5_props.PROP_CONTENT_TYPE, "application/json") publish_packet = mosq_test.gen_publish("prop/qos0", qos=0, payload="message", proto_ver=5, properties=props) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() if rc: (stdo, stde) = client.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/11-prop-send-payload-format.py000077500000000000000000000037531450213760600230270ustar00rootroot00000000000000#!/usr/bin/env python3 # Test whether a client sends a correct PUBLISH to a topic with QoS 0. # The client should connect to port 1888 with keepalive=60, clean session set, # and client id publish-qos0-test # The test will send a CONNACK message to the client with rc=0. Upon receiving # the CONNACK and verifying that rc=0, the client should send a PUBLISH message # to topic "pub/qos0/test" with payload "message" and QoS=0. If rc!=0, the # client should exit with an error. # After sending the PUBLISH message, the client should send a DISCONNECT message. from mosq_test_helper import * port = mosq_test.get_lib_port() rc = 1 keepalive = 60 connect_packet = mosq_test.gen_connect("prop-test", keepalive=keepalive, proto_ver=5) connack_packet = mosq_test.gen_connack(rc=0, proto_ver=5) props = mqtt5_props.gen_byte_prop(mqtt5_props.PROP_PAYLOAD_FORMAT_INDICATOR, 0x01) publish_packet = mosq_test.gen_publish("prop/qos0", qos=0, payload="message", proto_ver=5, properties=props) disconnect_packet = mosq_test.gen_disconnect(proto_ver=5) sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) sock.settimeout(10) sock.bind(('', port)) sock.listen(5) client_args = sys.argv[1:] env = dict(os.environ) env['LD_LIBRARY_PATH'] = '../../lib:../../lib/cpp' try: pp = env['PYTHONPATH'] except KeyError: pp = '' env['PYTHONPATH'] = '../../lib/python:'+pp client = mosq_test.start_client(filename=sys.argv[1].replace('/', '-'), cmd=client_args, env=env, port=port) try: (conn, address) = sock.accept() conn.settimeout(10) mosq_test.do_receive_send(conn, connect_packet, connack_packet, "connect") mosq_test.expect_packet(conn, "publish", publish_packet) mosq_test.expect_packet(conn, "disconnect", disconnect_packet) rc = 0 conn.close() except mosq_test.TestError: pass finally: client.terminate() client.wait() if rc: (stdo, stde) = client.communicate() print(stde) sock.close() exit(rc) mosquitto-2.0.18/test/lib/Makefile000066400000000000000000000071131450213760600170410ustar00rootroot00000000000000include ../../config.mk .PHONY: all check test test-compile test-compile-c test-compile-cpp c cpp .NOTPARALLEL: LD_LIBRARY_PATH=../../lib all : check : test ptest : test-compile ./test.py test : c cpp test-compile : test-compile-c test-compile-cpp test-compile-c : $(MAKE) -C c test-compile-cpp : $(MAKE) -C cpp c : test-compile ./01-con-discon-success.py $@/01-con-discon-success.test ./01-keepalive-pingreq.py $@/01-keepalive-pingreq.test ./01-no-clean-session.py $@/01-no-clean-session.test ./01-server-keepalive-pingreq.py $@/01-server-keepalive-pingreq.test ./01-unpwd-set.py $@/01-unpwd-set.test ./01-will-set.py $@/01-will-set.test ./01-will-unpwd-set.py $@/01-will-unpwd-set.test ./02-subscribe-qos0.py $@/02-subscribe-qos0.test ./02-subscribe-qos1.py $@/02-subscribe-qos1.test ./02-subscribe-qos1.py $@/02-subscribe-qos1-async1.test ./02-subscribe-qos1.py $@/02-subscribe-qos1-async2.test ./02-subscribe-qos2.py $@/02-subscribe-qos2.test ./02-unsubscribe-multiple-v5.py $@/02-unsubscribe-multiple-v5.test ./02-unsubscribe-v5.py $@/02-unsubscribe-v5.test ./02-unsubscribe.py $@/02-unsubscribe.test ./03-publish-b2c-qos1.py $@/03-publish-b2c-qos1.test ./03-publish-b2c-qos1-unexpected-puback.py $@/03-publish-b2c-qos1-unexpected-puback.test ./03-publish-b2c-qos2-len.py $@/03-publish-b2c-qos2-len.test ./03-publish-b2c-qos2.py $@/03-publish-b2c-qos2.test ./03-publish-b2c-qos2-unexpected-pubrel.py $@/03-publish-b2c-qos2-unexpected-pubrel.test ./03-publish-b2c-qos2-unexpected-pubcomp.py $@/03-publish-b2c-qos2-unexpected-pubcomp.test ./03-publish-c2b-qos1-disconnect.py $@/03-publish-c2b-qos1-disconnect.test ./03-publish-c2b-qos1-len.py $@/03-publish-c2b-qos1-len.test ./03-publish-c2b-qos1-receive-maximum.py $@/03-publish-c2b-qos1-receive-maximum.test ./03-publish-c2b-qos2-disconnect.py $@/03-publish-c2b-qos2-disconnect.test ./03-publish-c2b-qos2-len.py $@/03-publish-c2b-qos2-len.test ./03-publish-c2b-qos2-maximum-qos-0.py $@/03-publish-c2b-qos2-maximum-qos-0.test ./03-publish-c2b-qos2-maximum-qos-1.py $@/03-publish-c2b-qos2-maximum-qos-1.test ./03-publish-c2b-qos2-pubrec-error.py $@/03-publish-c2b-qos2-pubrec-error.test ./03-publish-c2b-qos2-receive-maximum-1.py $@/03-publish-c2b-qos2-receive-maximum-1.test ./03-publish-c2b-qos2-receive-maximum-2.py $@/03-publish-c2b-qos2-receive-maximum-2.test ./03-publish-c2b-qos2.py $@/03-publish-c2b-qos2.test ./03-publish-qos0-no-payload.py $@/03-publish-qos0-no-payload.test ./03-publish-qos0.py $@/03-publish-qos0.test ./03-request-response-correlation.py $@/03-request-response-correlation.test ./03-request-response.py $@/03-request-response.test ./04-retain-qos0.py $@/04-retain-qos0.test ifeq ($(WITH_TLS),yes) #./08-ssl-fake-cacert.py $@/08-ssl-fake-cacert.test ./08-ssl-bad-cacert.py $@/08-ssl-bad-cacert.test ./08-ssl-connect-cert-auth-enc.py $@/08-ssl-connect-cert-auth-enc.test ./08-ssl-connect-cert-auth.py $@/08-ssl-connect-cert-auth.test ./08-ssl-connect-cert-auth.py $@/08-ssl-connect-cert-auth-custom-ssl-ctx.test ./08-ssl-connect-cert-auth.py $@/08-ssl-connect-cert-auth-custom-ssl-ctx-default.test ./08-ssl-connect-no-auth.py $@/08-ssl-connect-no-auth.test endif ./09-util-topic-tokenise.py $@/09-util-topic-tokenise.test ./11-prop-oversize-packet.py $@/11-prop-oversize-packet.test ./11-prop-send-content-type.py $@/11-prop-send-content-type.test ./11-prop-send-payload-format.py $@/11-prop-send-payload-format.test ./11-prop-recv-qos0.py $@/11-prop-recv-qos0.test ./11-prop-recv-qos1.py $@/11-prop-recv-qos1.test ./11-prop-recv-qos2.py $@/11-prop-recv-qos2.test clean : $(MAKE) -C c clean $(MAKE) -C cpp clean mosquitto-2.0.18/test/lib/c/000077500000000000000000000000001450213760600156215ustar00rootroot00000000000000mosquitto-2.0.18/test/lib/c/01-con-discon-success.c000066400000000000000000000014531450213760600217100ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("01-con-discon-success", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/01-keepalive-pingreq.c000066400000000000000000000012551450213760600216160ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("01-keepalive-pingreq", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); rc = mosquitto_connect(mosq, "localhost", port, 5); if(rc != 0) return rc; while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); if(rc != 0) break; } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/01-no-clean-session.c000066400000000000000000000010051450213760600213540ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("01-no-clean-session", false, NULL); if(mosq == NULL){ return 1; } rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/01-server-keepalive-pingreq.c000066400000000000000000000013151450213760600231170ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("01-server-keepalive-pingreq", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/01-unpwd-set.c000066400000000000000000000010651450213760600201330ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("01-unpwd-set", true, NULL); if(mosq == NULL){ return 1; } mosquitto_username_pw_set(mosq, "uname", ";'[08gn=#"); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/01-will-set.c000066400000000000000000000011521450213760600177420ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("01-will-set", true, NULL); if(mosq == NULL){ return 1; } mosquitto_will_set(mosq, "topic/on/unexpected/disconnect", strlen("will message"), "will message", 1, true); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/01-will-unpwd-set.c000066400000000000000000000012371450213760600211010ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("01-will-unpwd-set", true, NULL); if(mosq == NULL){ return 1; } mosquitto_username_pw_set(mosq, "oibvvwqw", "#'^2hg9a&nm38*us"); mosquitto_will_set(mosq, "will-topic", strlen("will message"), "will message", 2, false); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-subscribe-qos0.c000066400000000000000000000017731450213760600210550ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "qos0/test", 0); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { mosquitto_disconnect(mosq); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("subscribe-qos0-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_subscribe_callback_set(mosq, on_subscribe); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-subscribe-qos1-async1.c000066400000000000000000000030121450213760600222360ustar00rootroot00000000000000#include #include #include #include #include /* mosquitto_connect_async() test, with mosquitto_loop_start() called before mosquitto_connect_async(). */ static int run = -1; static bool should_run = true; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "qos1/test", 1); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { //mosquitto_disconnect(mosq); should_run = false; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("subscribe-qos1-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_subscribe_callback_set(mosq, on_subscribe); rc = mosquitto_loop_start(mosq); if(rc){ printf("loop_start failed: %s\n", mosquitto_strerror(rc)); return rc; } rc = mosquitto_connect_async(mosq, "localhost", port, 60); if(rc){ printf("connect_async failed: %s\n", mosquitto_strerror(rc)); return rc; } /* 50 millis to be system polite */ struct timespec tv = { 0, 50e6 }; while(should_run){ nanosleep(&tv, NULL); } mosquitto_disconnect(mosq); mosquitto_loop_stop(mosq, false); mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-subscribe-qos1-async2.c000066400000000000000000000027571450213760600222560ustar00rootroot00000000000000#include #include #include #include #include /* mosquitto_connect_async() test, with mosquitto_loop_start() called after mosquitto_connect_async(). */ static int run = -1; static bool should_run = true; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "qos1/test", 1); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { //mosquitto_disconnect(mosq); should_run = false; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("subscribe-qos1-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_subscribe_callback_set(mosq, on_subscribe); rc = mosquitto_connect_async(mosq, "localhost", port, 60); if(rc){ printf("connect_async failed: %s\n", mosquitto_strerror(rc)); } rc = mosquitto_loop_start(mosq); if(rc){ printf("loop_start failed: %s\n", mosquitto_strerror(rc)); } /* 50 millis to be system polite */ struct timespec tv = { 0, 50e6 }; while(should_run){ nanosleep(&tv, NULL); } mosquitto_disconnect(mosq); mosquitto_loop_stop(mosq, false); mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-subscribe-qos1.c000066400000000000000000000017731450213760600210560ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "qos1/test", 1); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { mosquitto_disconnect(mosq); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("subscribe-qos1-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_subscribe_callback_set(mosq, on_subscribe); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-subscribe-qos2.c000066400000000000000000000017731450213760600210570ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "qos2/test", 2); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { mosquitto_disconnect(mosq); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("subscribe-qos2-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_subscribe_callback_set(mosq, on_subscribe); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-unsubscribe-multiple-v5.c000066400000000000000000000024641450213760600227170ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "unsubscribe/test", 2); } } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int sub_count, const int *subs) { char *unsubs[] = {"unsubscribe/test", "no-sub"}; mosquitto_unsubscribe_multiple(mosq, NULL, 2, unsubs, NULL); } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid) { mosquitto_disconnect(mosq); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("unsubscribe-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_subscribe_callback_set(mosq, on_subscribe); mosquitto_unsubscribe_callback_set(mosq, on_unsubscribe); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-unsubscribe-v5.c000066400000000000000000000020471450213760600210630ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_unsubscribe(mosq, NULL, "unsubscribe/test"); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid) { mosquitto_disconnect(mosq); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("unsubscribe-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_unsubscribe_callback_set(mosq, on_unsubscribe); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/02-unsubscribe.c000066400000000000000000000017351450213760600205360ustar00rootroot00000000000000#include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_unsubscribe(mosq, NULL, "unsubscribe/test"); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } void on_unsubscribe(struct mosquitto *mosq, void *obj, int mid) { mosquitto_disconnect(mosq); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("unsubscribe-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_unsubscribe_callback_set(mosq, on_unsubscribe); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-b2c-qos1-unexpected-puback.c000066400000000000000000000012651450213760600246110ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ printf("Connect error: %d\n", rc); exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos1-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); rc = mosquitto_connect(mosq, "localhost", port, 5); while(run == -1){ rc = mosquitto_loop(mosq, 300, 1); if(rc){ exit(0); } } mosquitto_lib_cleanup(); return 0; } mosquitto-2.0.18/test/lib/c/03-publish-b2c-qos1.c000066400000000000000000000025331450213760600212030ustar00rootroot00000000000000#include #include #include #include #include void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); } } void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) { if(msg->mid != 123){ printf("Invalid mid (%d)\n", msg->mid); exit(1); } if(msg->qos != 1){ printf("Invalid qos (%d)\n", msg->qos); exit(1); } if(strcmp(msg->topic, "pub/qos1/receive")){ printf("Invalid topic (%s)\n", msg->topic); exit(1); } if(strcmp(msg->payload, "message")){ printf("Invalid payload (%s)\n", (char *)msg->payload); exit(1); } if(msg->payloadlen != 7){ printf("Invalid payloadlen (%d)\n", msg->payloadlen); exit(1); } if(msg->retain != false){ printf("Invalid retain (%d)\n", msg->retain); exit(1); } exit(0); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos1-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_message_callback_set(mosq, on_message); mosquitto_message_retry_set(mosq, 3); rc = mosquitto_connect(mosq, "localhost", port, 60); while(1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return 1; } mosquitto-2.0.18/test/lib/c/03-publish-b2c-qos2-len.c000066400000000000000000000030631450213760600217570ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); } } void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) { if(msg->mid != 56){ printf("Invalid mid (%d)\n", msg->mid); exit(1); } if(msg->qos != 2){ printf("Invalid qos (%d)\n", msg->qos); exit(1); } if(strcmp(msg->topic, "len/qos2/test")){ printf("Invalid topic (%s)\n", msg->topic); exit(1); } if(strcmp(msg->payload, "message")){ printf("Invalid payload (%s)\n", (char *)msg->payload); exit(1); } if(msg->payloadlen != 7){ printf("Invalid payloadlen (%d)\n", msg->payloadlen); exit(1); } if(msg->retain != false){ printf("Invalid retain (%d)\n", msg->retain); exit(1); } mosquitto_disconnect(mosq); } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_message_callback_set(mosq, on_message); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 100, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-b2c-qos2-unexpected-pubcomp.c000066400000000000000000000012651450213760600250120ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ printf("Connect error: %d\n", rc); exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); rc = mosquitto_connect(mosq, "localhost", port, 5); while(run == -1){ rc = mosquitto_loop(mosq, 300, 1); if(rc){ exit(0); } } mosquitto_lib_cleanup(); return 0; } mosquitto-2.0.18/test/lib/c/03-publish-b2c-qos2-unexpected-pubrel.c000066400000000000000000000027761450213760600246460ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); } } void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) { if(!strcmp(msg->topic, "quit")){ run = 0; return; } if(msg->mid != 13423){ printf("Invalid mid (%d)\n", msg->mid); exit(1); } if(msg->qos != 2){ printf("Invalid qos (%d)\n", msg->qos); exit(1); } if(strcmp(msg->topic, "pub/qos2/receive")){ printf("Invalid topic (%s)\n", msg->topic); exit(1); } if(strcmp(msg->payload, "message")){ printf("Invalid payload (%s)\n", (char *)msg->payload); exit(1); } if(msg->payloadlen != 7){ printf("Invalid payloadlen (%d)\n", msg->payloadlen); exit(1); } if(msg->retain != false){ printf("Invalid retain (%d)\n", msg->retain); exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_message_callback_set(mosq, on_message); mosquitto_message_retry_set(mosq, 5); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, 300, 1); if(rc){ printf("%d:%s\n", rc, mosquitto_strerror(rc)); exit(1); } } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-b2c-qos2.c000066400000000000000000000025751450213760600212120ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); } } void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) { if(msg->mid != 13423){ printf("Invalid mid (%d)\n", msg->mid); exit(1); } if(msg->qos != 2){ printf("Invalid qos (%d)\n", msg->qos); exit(1); } if(strcmp(msg->topic, "pub/qos2/receive")){ printf("Invalid topic (%s)\n", msg->topic); exit(1); } if(strcmp(msg->payload, "message")){ printf("Invalid payload (%s)\n", (char *)msg->payload); exit(1); } if(msg->payloadlen != 7){ printf("Invalid payloadlen (%d)\n", msg->payloadlen); exit(1); } if(msg->retain != false){ printf("Invalid retain (%d)\n", msg->retain); exit(1); } run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_message_callback_set(mosq, on_message); mosquitto_message_retry_set(mosq, 5); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos1-disconnect.c000066400000000000000000000022741450213760600233340ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; static int first_connection = 1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ if(first_connection == 1){ mosquitto_publish(mosq, NULL, "pub/qos1/test", strlen("message"), "message", 1, false); first_connection = 0; } } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { mosquitto_disconnect(mosq); } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ mosquitto_reconnect(mosq); }else{ run = 0; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos1-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); mosquitto_message_retry_set(mosq, 3); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos1-len.c000066400000000000000000000021701450213760600217540ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, NULL, "pub/qos1/test", strlen("message"), "message", 1, false); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { mosquitto_disconnect(mosq); } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos1-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); mosquitto_message_retry_set(mosq, 3); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos1-receive-maximum.c000066400000000000000000000022301450213760600242700ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) { int i; if(rc){ exit(1); } for(i=0; i<6; i++){ mosquitto_publish_v5(mosq, NULL, "topic", 5, "12345", 1, false, NULL); } } void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { if(mid == 6){ mosquitto_disconnect(mosq); run = 0; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; mosquitto_property *props = NULL; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos1-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_v5_callback_set(mosq, on_connect); mosquitto_publish_v5_callback_set(mosq, on_publish); rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2-disconnect.c000066400000000000000000000022741450213760600233350ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; static int first_connection = 1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ if(first_connection == 1){ mosquitto_publish(mosq, NULL, "pub/qos2/test", strlen("message"), "message", 2, false); first_connection = 0; } } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { mosquitto_disconnect(mosq); } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ mosquitto_reconnect(mosq); }else{ run = 0; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); mosquitto_message_retry_set(mosq, 3); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2-len.c000066400000000000000000000022621450213760600217570ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, NULL, "pub/qos2/test", strlen("message"), "message", 2, false); } } void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { mosquitto_disconnect(mosq); } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_v5_callback_set(mosq, on_publish); mosquitto_message_retry_set(mosq, 3); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2-maximum-qos-0.c000066400000000000000000000026651450213760600236220ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos2", strlen("message"), "message", 2, false); if(rc != MOSQ_ERR_QOS_NOT_SUPPORTED) run = 1; rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos1", strlen("message"), "message", 1, false); if(rc != MOSQ_ERR_QOS_NOT_SUPPORTED) run = 1; rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos0", strlen("message"), "message", 0, false); if(rc != MOSQ_ERR_SUCCESS) run = 1; } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == 1){ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 50, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2-maximum-qos-1.c000066400000000000000000000027111450213760600236130ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos2", strlen("message"), "message", 2, false); if(rc != MOSQ_ERR_QOS_NOT_SUPPORTED) run = 1; rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos1", strlen("message"), "message", 1, false); if(rc != MOSQ_ERR_SUCCESS) run = 1; rc = mosquitto_publish(mosq, NULL, "maximum/qos/qos0", strlen("message"), "message", 0, false); if(rc != MOSQ_ERR_SUCCESS) run = 1; } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == 2){ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 50, 1); } mosquitto_loop(mosq, 50, 1); mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2-pubrec-error.c000066400000000000000000000023071450213760600236100ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) { if(rc){ exit(1); } mosquitto_publish_v5(mosq, NULL, "topic", strlen("rejected"), "rejected", 2, false, NULL); mosquitto_publish_v5(mosq, NULL, "topic", strlen("accepted"), "accepted", 2, false, NULL); } void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { if(mid == 2){ run = 0; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; mosquitto_property *props = NULL; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_v5_callback_set(mosq, on_connect); mosquitto_publish_v5_callback_set(mosq, on_publish); rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); while(run == -1){ mosquitto_loop(mosq, 100, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2-receive-maximum-1.c000066400000000000000000000022301450213760600244270ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) { int i; if(rc){ exit(1); } for(i=0; i<5; i++){ mosquitto_publish_v5(mosq, NULL, "topic", 5, "12345", 2, false, NULL); } } void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { if(mid == 5){ mosquitto_disconnect(mosq); run = 0; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; mosquitto_property *props = NULL; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_v5_callback_set(mosq, on_connect); mosquitto_publish_v5_callback_set(mosq, on_publish); rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2-receive-maximum-2.c000066400000000000000000000022301450213760600244300ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc, int flags, const mosquitto_property *properties) { int i; if(rc){ exit(1); } for(i=0; i<5; i++){ mosquitto_publish_v5(mosq, NULL, "topic", 5, "12345", 2, false, NULL); } } void on_publish(struct mosquitto *mosq, void *obj, int mid, int reason_code, const mosquitto_property *properties) { if(mid == 5){ mosquitto_disconnect(mosq); run = 0; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; mosquitto_property *props = NULL; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, &run); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_v5_callback_set(mosq, on_connect); mosquitto_publish_v5_callback_set(mosq, on_publish); rc = mosquitto_connect_bind_v5(mosq, "localhost", port, 60, NULL, NULL); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-c2b-qos2.c000066400000000000000000000020071450213760600212000ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, NULL, "pub/qos2/test", strlen("message"), "message", 2, false); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { mosquitto_disconnect(mosq); } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos2-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_publish_callback_set(mosq, on_publish); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, 300, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-qos0-no-payload.c000066400000000000000000000017201450213760600225740ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, &sent_mid, "pub/qos0/no-payload/test", 0, NULL, 0, false); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == sent_mid){ mosquitto_disconnect(mosq); run = 0; }else{ exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos0-test-np", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_publish_callback_set(mosq, on_publish); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-publish-qos0.c000066400000000000000000000017341450213760600205400ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, &sent_mid, "pub/qos0/test", strlen("message"), "message", 0, false); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == sent_mid){ mosquitto_disconnect(mosq); run = 0; }else{ exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos0-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_publish_callback_set(mosq, on_publish); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-request-response-1.c000066400000000000000000000027051450213760600216730ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "response/topic", 0); } } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { mosquitto_property *props = NULL; mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); mosquitto_publish_v5(mosq, NULL, "request/topic", 6, "action", 0, 0, props); mosquitto_property_free_all(&props); } void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) { if(!strcmp(msg->payload, "a response")){ run = 0; }else{ run = 1; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int ver = PROTOCOL_VERSION_v5; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("request-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_subscribe_callback_set(mosq, on_subscribe); mosquitto_message_callback_set(mosq, on_message); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-request-response-2.c000066400000000000000000000031611450213760600216710ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "request/topic", 0); } } void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *props) { const mosquitto_property *p_resp, *p_corr = NULL; char *resp_topic = NULL; int rc; if(!strcmp(msg->topic, "request/topic")){ p_resp = mosquitto_property_read_string(props, MQTT_PROP_RESPONSE_TOPIC, &resp_topic, false); if(p_resp){ p_corr = mosquitto_property_read_binary(props, MQTT_PROP_CORRELATION_DATA, NULL, NULL, false); rc = mosquitto_publish_v5(mosq, NULL, resp_topic, strlen("a response"), "a response", 0, false, p_corr); free(resp_topic); } } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { run = 0; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int ver = PROTOCOL_VERSION_v5; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("response-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_publish_callback_set(mosq, on_publish); mosquitto_message_v5_callback_set(mosq, on_message_v5); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/03-request-response-correlation-1.c000066400000000000000000000030301450213760600242020ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_subscribe(mosq, NULL, "response/topic", 0); } } void on_subscribe(struct mosquitto *mosq, void *obj, int mid, int qos_count, const int *granted_qos) { mosquitto_property *props = NULL; mosquitto_property_add_string(&props, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); mosquitto_property_add_binary(&props, MQTT_PROP_CORRELATION_DATA, "corridor", 8); mosquitto_publish_v5(mosq, NULL, "request/topic", 6, "action", 0, 0, props); mosquitto_property_free_all(&props); } void on_message(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) { if(!strcmp(msg->payload, "a response")){ run = 0; }else{ run = 1; } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int ver = PROTOCOL_VERSION_v5; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("request-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &ver); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_subscribe_callback_set(mosq, on_subscribe); mosquitto_message_callback_set(mosq, on_message); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/04-retain-qos0.c000066400000000000000000000014021450213760600203450ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_publish(mosq, NULL, "retain/qos0/test", strlen("retained message"), "retained message", 0, true); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("retain-qos0-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/08-ssl-bad-cacert.c000066400000000000000000000007241450213760600210010ustar00rootroot00000000000000#include #include #include #include #include int main(int argc, char *argv[]) { int rc = 1; struct mosquitto *mosq; mosquitto_lib_init(); mosq = mosquitto_new("08-ssl-bad-cacert", true, NULL); if(mosq == NULL){ return 1; } if(mosquitto_tls_set(mosq, "this/file/doesnt/exist", NULL, NULL, NULL, NULL) == MOSQ_ERR_INVAL){ rc = 0; } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return rc; } mosquitto-2.0.18/test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx-default.c000066400000000000000000000026001450213760600266570ustar00rootroot00000000000000#include #include #include #include #include #include #include static int run = -1; void handle_sigint(int signal) { run = 0; } void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; SSL_CTX *ssl_ctx; int port = atoi(argv[1]); mosquitto_lib_init(); OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ | OPENSSL_INIT_ADD_ALL_DIGESTS \ | OPENSSL_INIT_LOAD_CONFIG, NULL); ssl_ctx = SSL_CTX_new(TLS_client_method()); mosq = mosquitto_new("08-ssl-connect-crt-auth", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 1); mosquitto_void_option(mosq, MOSQ_OPT_SSL_CTX, ssl_ctx); mosquitto_tls_set(mosq, "../ssl/test-root-ca.crt", "../ssl/certs", "../ssl/client.crt", "../ssl/client.key", NULL); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); rc = mosquitto_connect(mosq, "localhost", port, 60); signal(SIGINT, handle_sigint); while(run == -1){ mosquitto_loop(mosq, -1, 1); } SSL_CTX_free(ssl_ctx); mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/08-ssl-connect-cert-auth-custom-ssl-ctx.c000066400000000000000000000032321450213760600252370ustar00rootroot00000000000000#include #include #include #include #include #include #include static int run = -1; void handle_sigint(int signal) { run = 0; } void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; SSL_CTX *ssl_ctx; int port = atoi(argv[1]); mosquitto_lib_init(); OPENSSL_init_crypto(OPENSSL_INIT_ADD_ALL_CIPHERS \ | OPENSSL_INIT_ADD_ALL_DIGESTS \ | OPENSSL_INIT_LOAD_CONFIG, NULL); ssl_ctx = SSL_CTX_new(TLS_client_method()); SSL_CTX_set_verify(ssl_ctx, SSL_VERIFY_PEER, NULL); SSL_CTX_use_certificate_chain_file(ssl_ctx, "../ssl/client.crt"); SSL_CTX_use_PrivateKey_file(ssl_ctx, "../ssl/client.key", SSL_FILETYPE_PEM); SSL_CTX_load_verify_locations(ssl_ctx, "../ssl/test-root-ca.crt", "../ssl/certs"); mosq = mosquitto_new("08-ssl-connect-crt-auth", true, NULL); if(mosq == NULL){ return 1; } mosquitto_tls_set(mosq, "../ssl/test-root-ca.crt", "../ssl/certs", "../ssl/client.crt", "../ssl/client.key", NULL); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); mosquitto_int_option(mosq, MOSQ_OPT_SSL_CTX_WITH_DEFAULTS, 0); mosquitto_void_option(mosq, MOSQ_OPT_SSL_CTX, ssl_ctx); rc = mosquitto_connect(mosq, "localhost", port, 60); signal(SIGINT, handle_sigint); while(run == -1){ mosquitto_loop(mosq, -1, 1); } SSL_CTX_free(ssl_ctx); mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/08-ssl-connect-cert-auth-enc.c000066400000000000000000000022141450213760600230760ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } static int password_callback(char* buf, int size, int rwflag, void* userdata) { strncpy(buf, "password", size); buf[size-1] = '\0'; return strlen(buf); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("08-ssl-connect-crt-auth-enc", true, NULL); if(mosq == NULL){ return 1; } mosquitto_tls_set(mosq, "../ssl/test-root-ca.crt", "../ssl/certs", "../ssl/client-encrypted.crt", "../ssl/client-encrypted.key", password_callback); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/08-ssl-connect-cert-auth.c000066400000000000000000000016641450213760600223430ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("08-ssl-connect-crt-auth", true, NULL); if(mosq == NULL){ return 1; } mosquitto_tls_set(mosq, "../ssl/test-root-ca.crt", "../ssl/certs", "../ssl/client.crt", "../ssl/client.key", NULL); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/08-ssl-connect-no-auth.c000066400000000000000000000016051450213760600220150ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ mosquitto_disconnect(mosq); } } void on_disconnect(struct mosquitto *mosq, void *obj, int rc) { run = rc; } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("08-ssl-connect-no-auth", true, NULL); if(mosq == NULL){ return 1; } mosquitto_tls_set(mosq, "../ssl/all-ca.crt", NULL, NULL, NULL, NULL); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_disconnect_callback_set(mosq, on_disconnect); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/08-ssl-fake-cacert.c000066400000000000000000000014571450213760600211650ustar00rootroot00000000000000#include #include #include #include #include static int run = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { exit(1); } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("08-ssl-connect-crt-auth", true, NULL); if(mosq == NULL){ return 1; } mosquitto_tls_set(mosq, "../ssl/test-fake-root-ca.crt", NULL, "../ssl/client.crt", "../ssl/client.key", NULL); mosquitto_connect_callback_set(mosq, on_connect); rc = mosquitto_connect(mosq, "localhost", port, 60); rc = mosquitto_loop_forever(mosq, -1, 1); mosquitto_destroy(mosq); mosquitto_lib_cleanup(); if(rc == MOSQ_ERR_ERRNO && errno == EPROTO){ return 0; }else{ return 1; } } mosquitto-2.0.18/test/lib/c/09-util-topic-tokenise.c000066400000000000000000000065021450213760600221260ustar00rootroot00000000000000#include #include #include void print_error(const char *topic, char **topics, int topic_count) { int i; printf("TOPIC: %s\n", topic); printf("TOKENS: "); for(i=0; i #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { if(rc){ exit(1); }else{ rc = mosquitto_subscribe(mosq, NULL, "0123456789012345678901234567890", 0); if(rc != MOSQ_ERR_OVERSIZE_PACKET){ printf("Fail on subscribe\n"); exit(1); } rc = mosquitto_unsubscribe(mosq, NULL, "0123456789012345678901234567890"); if(rc != MOSQ_ERR_OVERSIZE_PACKET){ printf("Fail on unsubscribe\n"); exit(1); } rc = mosquitto_publish(mosq, &sent_mid, "pub/test", strlen("0123456789012345678"), "0123456789012345678", 0, false); if(rc != MOSQ_ERR_OVERSIZE_PACKET){ printf("Fail on publish 1\n"); exit(1); } rc = mosquitto_publish(mosq, &sent_mid, "pub/test", strlen("012345678901234567"), "012345678901234567", 0, false); if(rc != MOSQ_ERR_SUCCESS){ printf("Fail on publish 2\n"); exit(1); } } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == sent_mid){ mosquitto_disconnect(mosq); run = 0; }else{ exit(1); } } int main(int argc, char *argv[]) { int rc; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("publish-qos0-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); mosquitto_connect_callback_set(mosq, on_connect); mosquitto_publish_callback_set(mosq, on_publish); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/11-prop-recv-qos0.c000066400000000000000000000031611450213760600210020ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { int rc2; mosquitto_property *proplist = NULL; if(rc){ exit(1); } } void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties) { int rc; char *str; if(properties){ if(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &str, false)){ rc = strcmp(str, "plain/text"); free(str); if(rc == 0){ if(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &str, false)){ rc = strcmp(str, "msg/123"); free(str); if(rc == 0){ if(msg->qos == 0){ mosquitto_publish(mosq, NULL, "ok", 2, "ok", 0, 0); return; } } } } } } /* No matching message, so quit with an error */ exit(1); } void on_publish(struct mosquitto *mosq, void *obj, int mid) { run = 0; } int main(int argc, char *argv[]) { int rc; int tmp; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("prop-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_message_v5_callback_set(mosq, on_message_v5); mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/11-prop-recv-qos1.c000066400000000000000000000031611450213760600210030ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { int rc2; mosquitto_property *proplist = NULL; if(rc){ exit(1); } } void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties) { int rc; char *str; if(properties){ if(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &str, false)){ rc = strcmp(str, "plain/text"); free(str); if(rc == 0){ if(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &str, false)){ rc = strcmp(str, "msg/123"); free(str); if(rc == 0){ if(msg->qos == 1){ mosquitto_publish(mosq, NULL, "ok", 2, "ok", 0, 0); return; } } } } } } /* No matching message, so quit with an error */ exit(1); } void on_publish(struct mosquitto *mosq, void *obj, int mid) { run = 0; } int main(int argc, char *argv[]) { int rc; int tmp; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("prop-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_message_v5_callback_set(mosq, on_message_v5); mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/11-prop-recv-qos2.c000066400000000000000000000031611450213760600210040ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { int rc2; mosquitto_property *proplist = NULL; if(rc){ exit(1); } } void on_message_v5(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg, const mosquitto_property *properties) { int rc; char *str; if(properties){ if(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &str, false)){ rc = strcmp(str, "plain/text"); free(str); if(rc == 0){ if(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &str, false)){ rc = strcmp(str, "msg/123"); free(str); if(rc == 0){ if(msg->qos == 2){ mosquitto_publish(mosq, NULL, "ok", 2, "ok", 0, 0); return; } } } } } } /* No matching message, so quit with an error */ exit(1); } void on_publish(struct mosquitto *mosq, void *obj, int mid) { run = 0; } int main(int argc, char *argv[]) { int rc; int tmp; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("prop-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_message_v5_callback_set(mosq, on_message_v5); mosquitto_int_option(mosq, MOSQ_OPT_PROTOCOL_VERSION, MQTT_PROTOCOL_V5); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/11-prop-send-content-type.c000066400000000000000000000024201450213760600225400ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { int rc2; mosquitto_property *proplist = NULL; if(rc){ exit(1); }else{ rc2 = mosquitto_property_add_string(&proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); mosquitto_publish_v5(mosq, &sent_mid, "prop/qos0", strlen("message"), "message", 0, false, proplist); mosquitto_property_free_all(&proplist); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == sent_mid){ mosquitto_disconnect(mosq); run = 0; }else{ exit(1); } } int main(int argc, char *argv[]) { int rc; int tmp; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("prop-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_publish_callback_set(mosq, on_publish); tmp = MQTT_PROTOCOL_V5; mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &tmp); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/11-prop-send-payload-format.c000066400000000000000000000024111450213760600230260ustar00rootroot00000000000000#include #include #include #include #include #include static int run = -1; static int sent_mid = -1; void on_connect(struct mosquitto *mosq, void *obj, int rc) { int rc2; mosquitto_property *proplist = NULL; if(rc){ exit(1); }else{ rc2 = mosquitto_property_add_byte(&proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); mosquitto_publish_v5(mosq, &sent_mid, "prop/qos0", strlen("message"), "message", 0, false, proplist); mosquitto_property_free_all(&proplist); } } void on_publish(struct mosquitto *mosq, void *obj, int mid) { if(mid == sent_mid){ mosquitto_disconnect(mosq); run = 0; }else{ exit(1); } } int main(int argc, char *argv[]) { int rc; int tmp; struct mosquitto *mosq; int port = atoi(argv[1]); mosquitto_lib_init(); mosq = mosquitto_new("prop-test", true, NULL); if(mosq == NULL){ return 1; } mosquitto_connect_callback_set(mosq, on_connect); mosquitto_publish_callback_set(mosq, on_publish); tmp = MQTT_PROTOCOL_V5; mosquitto_opts_set(mosq, MOSQ_OPT_PROTOCOL_VERSION, &tmp); rc = mosquitto_connect(mosq, "localhost", port, 60); while(run == -1){ rc = mosquitto_loop(mosq, -1, 1); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/c/Makefile000066400000000000000000000036071450213760600172670ustar00rootroot00000000000000include ../../../config.mk .PHONY: all clean reallyclean CFLAGS=-I../../../include -Werror LIBS=../../../lib/libmosquitto.so.1 SRC = \ 01-con-discon-success.c \ 01-keepalive-pingreq.c \ 01-no-clean-session.c \ 01-server-keepalive-pingreq.c \ 01-unpwd-set.c \ 01-will-set.c \ 01-will-unpwd-set.c \ 02-subscribe-qos0.c \ 02-subscribe-qos1-async1.c \ 02-subscribe-qos1-async2.c \ 02-subscribe-qos1.c \ 02-subscribe-qos2.c \ 02-unsubscribe-multiple-v5.c \ 02-unsubscribe-v5.c \ 02-unsubscribe.c \ 03-publish-b2c-qos1-unexpected-puback.c \ 03-publish-b2c-qos1.c \ 03-publish-b2c-qos2-len.c \ 03-publish-b2c-qos2-unexpected-pubrel.c \ 03-publish-b2c-qos2-unexpected-pubcomp.c \ 03-publish-b2c-qos2.c \ 03-publish-c2b-qos1-disconnect.c \ 03-publish-c2b-qos1-len.c \ 03-publish-c2b-qos1-receive-maximum.c \ 03-publish-c2b-qos2-disconnect.c \ 03-publish-c2b-qos2-len.c \ 03-publish-c2b-qos2-maximum-qos-0.c \ 03-publish-c2b-qos2-maximum-qos-1.c \ 03-publish-c2b-qos2-pubrec-error.c \ 03-publish-c2b-qos2-receive-maximum-1.c \ 03-publish-c2b-qos2-receive-maximum-2.c \ 03-publish-c2b-qos2.c \ 03-publish-qos0-no-payload.c \ 03-publish-qos0.c \ 03-request-response-1.c \ 03-request-response-2.c \ 03-request-response-correlation-1.c \ 04-retain-qos0.c \ 08-ssl-bad-cacert.c \ 08-ssl-connect-cert-auth-enc.c \ 08-ssl-connect-cert-auth.c \ 08-ssl-connect-no-auth.c \ 08-ssl-fake-cacert.c \ 09-util-topic-tokenise.c \ 11-prop-oversize-packet.c \ 11-prop-recv-qos0.c \ 11-prop-recv-qos1.c \ 11-prop-recv-qos2.c \ 11-prop-send-payload-format.c \ 11-prop-send-content-type.c ifeq ($(WITH_TLS),yes) SRC += \ 08-ssl-connect-cert-auth-custom-ssl-ctx.c \ 08-ssl-connect-cert-auth-custom-ssl-ctx-default.c LIBS += -lssl -lcrypto endif TESTS = ${SRC:.c=.test} all : ${TESTS} ${TESTS} : %.test: %.c $(CC) $< -o $@ $(CFLAGS) $(LIBS) reallyclean : clean -rm -f *.orig clean : rm -f *.test mosquitto-2.0.18/test/lib/cpp/000077500000000000000000000000001450213760600161615ustar00rootroot00000000000000mosquitto-2.0.18/test/lib/cpp/01-con-discon-success.cpp000066400000000000000000000014631450213760600226110ustar00rootroot00000000000000//#include //#include //#include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ disconnect(); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("01-con-discon-success"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/01-keepalive-pingreq.cpp000066400000000000000000000012121450213760600225070ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); } } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("01-keepalive-pingreq"); mosq->connect("localhost", port, 5); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/01-no-clean-session.cpp000066400000000000000000000011711450213760600222600ustar00rootroot00000000000000#include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id, bool clean_session); }; mosquittopp_test::mosquittopp_test(const char *id, bool clean_session) : mosqpp::mosquittopp(id, clean_session) { } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("01-no-clean-session", false); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/01-unpwd-set.cpp000066400000000000000000000011421450213760600210270ustar00rootroot00000000000000#include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("01-unpwd-set"); mosq->username_pw_set("uname", ";'[08gn=#"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/01-will-set.cpp000066400000000000000000000013301450213760600206400ustar00rootroot00000000000000//#include //#include //#include #include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("01-will-set"); mosq->will_set("topic/on/unexpected/disconnect", strlen("will message"), "will message", 1, true); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/01-will-unpwd-set.cpp000066400000000000000000000013021450213760600217720ustar00rootroot00000000000000#include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("01-will-unpwd-set"); mosq->username_pw_set("oibvvwqw", "#'^2hg9a&nm38*us"); mosq->will_set("will-topic", strlen("will message"), "will message", 2, false); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/02-subscribe-qos0.cpp000066400000000000000000000016751450213760600217560ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); void on_subscribe(int mid, int qos_count, const int *granted_qos); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ subscribe(NULL, "qos0/test", 0); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } void mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos) { disconnect(); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("subscribe-qos0-test"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/02-subscribe-qos1.cpp000066400000000000000000000016601450213760600217510ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); void on_subscribe(int mid, int qos_count, const int *granted_qos); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ subscribe(NULL, "qos1/test", 1); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } void mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos) { disconnect(); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("subscribe-qos1-test"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/02-subscribe-qos2.cpp000066400000000000000000000016761450213760600217610ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); void on_subscribe(int mid, int qos_count, const int *granted_qos); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ subscribe(NULL, "qos2/test", 2); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } void mosquittopp_test::on_subscribe(int mid, int qos_count, const int *granted_qos) { disconnect(); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("subscribe-qos2-test"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/02-unsubscribe.cpp000066400000000000000000000015501450213760600214310ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); void on_unsubscribe(int mid); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ unsubscribe(NULL, "unsubscribe/test"); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } void mosquittopp_test::on_unsubscribe(int mid) { disconnect(); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("unsubscribe-test"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/03-publish-b2c-qos1.cpp000066400000000000000000000026011450213760600220770ustar00rootroot00000000000000#include #include #include #include class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_message(const struct mosquitto_message *msg); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); } } void mosquittopp_test::on_message(const struct mosquitto_message *msg) { if(msg->mid != 123){ printf("Invalid mid (%d)\n", msg->mid); exit(1); } if(msg->qos != 1){ printf("Invalid qos (%d)\n", msg->qos); exit(1); } if(strcmp(msg->topic, "pub/qos1/receive")){ printf("Invalid topic (%s)\n", msg->topic); exit(1); } if(strcmp((char *)msg->payload, "message")){ printf("Invalid payload (%s)\n", (char *)msg->payload); exit(1); } if(msg->payloadlen != 7){ printf("Invalid payloadlen (%d)\n", msg->payloadlen); exit(1); } if(msg->retain != false){ printf("Invalid retain (%d)\n", msg->retain); exit(1); } exit(0); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("publish-qos1-test"); mosq->message_retry_set(3); mosq->connect("localhost", port, 60); while(1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return 1; } mosquitto-2.0.18/test/lib/cpp/03-publish-b2c-qos2.cpp000066400000000000000000000026251450213760600221060ustar00rootroot00000000000000#include #include #include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_message(const struct mosquitto_message *msg); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); } } void mosquittopp_test::on_message(const struct mosquitto_message *msg) { if(msg->mid != 13423){ printf("Invalid mid (%d)\n", msg->mid); exit(1); } if(msg->qos != 2){ printf("Invalid qos (%d)\n", msg->qos); exit(1); } if(strcmp(msg->topic, "pub/qos2/receive")){ printf("Invalid topic (%s)\n", msg->topic); exit(1); } if(strcmp((char *)msg->payload, "message")){ printf("Invalid payload (%s)\n", (char *)msg->payload); exit(1); } if(msg->payloadlen != 7){ printf("Invalid payloadlen (%d)\n", msg->payloadlen); exit(1); } if(msg->retain != false){ printf("Invalid retain (%d)\n", msg->retain); exit(1); } run = 0; } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("publish-qos2-test"); mosq->message_retry_set(3); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/03-publish-c2b-qos1-disconnect.cpp000066400000000000000000000021241450213760600242260ustar00rootroot00000000000000#include #include #include static int run = -1; static int first_connection = 1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); void on_publish(int mid); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ if(first_connection == 1){ publish(NULL, "pub/qos1/test", strlen("message"), "message", 1, false); first_connection = 0; } } } void mosquittopp_test::on_disconnect(int rc) { if(rc){ reconnect(); }else{ run = 0; } } void mosquittopp_test::on_publish(int mid) { disconnect(); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("publish-qos1-test"); mosq->message_retry_set(3); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/03-publish-c2b-qos2-disconnect.cpp000066400000000000000000000021241450213760600242270ustar00rootroot00000000000000#include #include #include static int run = -1; static int first_connection = 1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); void on_publish(int mid); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ if(first_connection == 1){ publish(NULL, "pub/qos2/test", strlen("message"), "message", 2, false); first_connection = 0; } } } void mosquittopp_test::on_disconnect(int rc) { if(rc){ reconnect(); }else{ run = 0; } } void mosquittopp_test::on_publish(int mid) { disconnect(); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("publish-qos2-test"); mosq->message_retry_set(3); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/03-publish-c2b-qos2.cpp000066400000000000000000000016511450213760600221040ustar00rootroot00000000000000#include #include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); void on_publish(int mid); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ publish(NULL, "pub/qos2/test", strlen("message"), "message", 2, false); } } void mosquittopp_test::on_disconnect(int rc) { run = 0; } void mosquittopp_test::on_publish(int mid) { disconnect(); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("publish-qos2-test"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/03-publish-qos0-no-payload.cpp000066400000000000000000000016161450213760600235000ustar00rootroot00000000000000#include #include static int run = -1; static int sent_mid = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_publish(int mid); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ publish(&sent_mid, "pub/qos0/no-payload/test", 0, NULL, 0, false); } } void mosquittopp_test::on_publish(int mid) { if(sent_mid == mid){ disconnect(); }else{ exit(1); } } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("publish-qos0-test-np"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/03-publish-qos0.cpp000066400000000000000000000016251450213760600214370ustar00rootroot00000000000000#include #include static int run = -1; static int sent_mid = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_publish(int mid); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ publish(&sent_mid, "pub/qos0/test", strlen("message"), "message", 0, false); } } void mosquittopp_test::on_publish(int mid) { if(sent_mid == mid){ disconnect(); }else{ exit(1); } } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("publish-qos0-test"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/04-retain-qos0.cpp000066400000000000000000000014011450213760600212440ustar00rootroot00000000000000#include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ publish(NULL, "retain/qos0/test", strlen("retained message"), "retained message", 0, true); } } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("retain-qos0-test"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/08-ssl-bad-cacert.cpp000066400000000000000000000010551450213760600216770ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int rc = 1; mosqpp::lib_init(); mosq = new mosquittopp_test("08-ssl-bad-cacert"); mosq->tls_opts_set(1, "tlsv1", NULL); if(mosq->tls_set("this/file/doesnt/exist") == MOSQ_ERR_INVAL){ rc = 0; } delete mosq; mosqpp::lib_cleanup(); return rc; } mosquitto-2.0.18/test/lib/cpp/08-ssl-connect-cert-auth-enc.cpp000066400000000000000000000022631450213760600240020ustar00rootroot00000000000000#include #include static int run = -1; static int password_callback(char* buf, int size, int rwflag, void* userdata) { strncpy(buf, "password", size); buf[size-1] = '\0'; return strlen(buf); } class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ disconnect(); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("08-ssl-connect-crt-auth-enc"); mosq->tls_opts_set(1, "tlsv1", NULL); //mosq->tls_set("../ssl/test-ca.crt", NULL, "../ssl/client.crt", "../ssl/client.key"); mosq->tls_set("../ssl/all-ca.crt", NULL, "../ssl/client-encrypted.crt", "../ssl/client-encrypted.key", password_callback); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/08-ssl-connect-cert-auth.cpp000066400000000000000000000017261450213760600232420ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ disconnect(); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("08-ssl-connect-crt-auth"); mosq->tls_opts_set(1, "tlsv1", NULL); //mosq->tls_set("../ssl/test-ca.crt", NULL, "../ssl/client.crt", "../ssl/client.key"); mosq->tls_set("../ssl/all-ca.crt", NULL, "../ssl/client.crt", "../ssl/client.key"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/08-ssl-connect-no-auth.cpp000066400000000000000000000015721450213760600227200ustar00rootroot00000000000000#include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); void on_disconnect(int rc); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { if(rc){ exit(1); }else{ disconnect(); } } void mosquittopp_test::on_disconnect(int rc) { run = rc; } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("08-ssl-connect-no-auth"); mosq->tls_opts_set(1, "tlsv1", NULL); //mosq->tls_set("../ssl/test-root-ca.crt"); mosq->tls_set("../ssl/all-ca.crt"); mosq->connect("localhost", port, 60); while(run == -1){ mosq->loop(); } delete mosq; delete mosq; mosqpp::lib_cleanup(); return run; } mosquitto-2.0.18/test/lib/cpp/08-ssl-fake-cacert.cpp000066400000000000000000000015111450213760600220540ustar00rootroot00000000000000#include #include static int run = -1; class mosquittopp_test : public mosqpp::mosquittopp { public: mosquittopp_test(const char *id); void on_connect(int rc); }; mosquittopp_test::mosquittopp_test(const char *id) : mosqpp::mosquittopp(id) { } void mosquittopp_test::on_connect(int rc) { exit(1); } int main(int argc, char *argv[]) { struct mosquittopp_test *mosq; int rc; int port = atoi(argv[1]); mosqpp::lib_init(); mosq = new mosquittopp_test("08-ssl-fake-cacert"); mosq->tls_opts_set(1, "tlsv1", NULL); mosq->tls_set("../ssl/test-fake-root-ca.crt", NULL, "../ssl/client.crt", "../ssl/client.key"); mosq->connect("localhost", port, 60); rc = mosq->loop_forever(); delete mosq; mosqpp::lib_cleanup(); if(rc == MOSQ_ERR_ERRNO && errno == EPROTO){ return 0; }else{ return 1; } } mosquitto-2.0.18/test/lib/cpp/09-util-topic-tokenise.cpp000066400000000000000000000064251450213760600230320ustar00rootroot00000000000000#include #include #include void print_error(const char *topic, char **topics, int topic_count) { int i; printf("TOPIC: %s\n", topic); printf("TOKENS: "); for(i=0; i 0: rlen = len(expected) else: rlen = 1 packet_recvd = sock.recv(rlen) if packet_matches(name, packet_recvd, expected): return True else: raise TestError def packet_matches(name, recvd, expected): if recvd != expected: print("FAIL: Received incorrect "+name+".") try: print("Received: "+to_string(recvd)) except struct.error: print("Received (not decoded, len=%d): %s" % (len(recvd), recvd)) try: print("Expected: "+to_string(expected)) except struct.error: print("Expected (not decoded, len=%d): %s" % (len(expected), expected)) return False else: return True def receive_unordered(sock, recv1_packet, recv2_packet, error_string): expected1 = recv1_packet + recv2_packet expected2 = recv2_packet + recv1_packet recvd = b'' while len(recvd) < len(expected1): r = sock.recv(1) if len(r) == 0: raise ValueError(error_string) recvd += r if recvd == expected1 or recvd == expected2: return else: packet_matches(error_string, recvd, expected2) raise ValueError(error_string) def do_send_receive(sock, send_packet, receive_packet, error_string="send receive error"): size = len(send_packet) total_sent = 0 while total_sent < size: sent = sock.send(send_packet[total_sent:]) if sent == 0: raise RuntimeError("socket connection broken") total_sent += sent if expect_packet(sock, error_string, receive_packet): return sock else: sock.close() raise ValueError # Useful for mocking a client receiving (with ack) a qos1 publish def do_receive_send(sock, receive_packet, send_packet, error_string="receive send error"): if expect_packet(sock, error_string, receive_packet): size = len(send_packet) total_sent = 0 while total_sent < size: sent = sock.send(send_packet[total_sent:]) if sent == 0: raise RuntimeError("socket connection broken") total_sent += sent return sock else: sock.close() raise ValueError def client_connect_only(hostname="localhost", port=1888, timeout=10): sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.settimeout(timeout) sock.connect((hostname, port)) return sock def do_client_connect(connect_packet, connack_packet, hostname="localhost", port=1888, timeout=10, connack_error="connack"): sock = client_connect_only(hostname, port, timeout) return do_send_receive(sock, connect_packet, connack_packet, connack_error) def remaining_length(packet): l = min(5, len(packet)) all_bytes = struct.unpack("!"+"B"*l, packet[:l]) mult = 1 rl = 0 for i in range(1,l-1): byte = all_bytes[i] rl += (byte & 127) * mult mult *= 128 if byte & 128 == 0: packet = packet[i+1:] break return (packet, rl) def to_hex_string(packet): if len(packet) == 0: return "" s = "" while len(packet) > 0: packet0 = struct.unpack("!B", packet[0]) s = s+hex(packet0[0]) + " " packet = packet[1:] return s def to_string(packet): if len(packet) == 0: return "" packet0 = struct.unpack("!B%ds" % (len(packet)-1), bytes(packet)) packet0 = packet0[0] cmd = packet0 & 0xF0 if cmd == 0x00: # Reserved return "0x00" elif cmd == 0x10: # CONNECT (packet, rl) = remaining_length(packet) pack_format = "!H" + str(len(packet)-2) + 's' (slen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(slen)+'sBBH' + str(len(packet)-slen-4) + 's' (protocol, proto_ver, flags, keepalive, packet) = struct.unpack(pack_format, packet) s = "CONNECT, proto="+str(protocol)+str(proto_ver)+", keepalive="+str(keepalive) if flags&2: s = s+", clean-session" else: s = s+", durable" pack_format = "!H" + str(len(packet)-2) + 's' (slen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's' (client_id, packet) = struct.unpack(pack_format, packet) s = s+", id="+str(client_id) if flags&4: pack_format = "!H" + str(len(packet)-2) + 's' (slen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's' (will_topic, packet) = struct.unpack(pack_format, packet) s = s+", will-topic="+str(will_topic) pack_format = "!H" + str(len(packet)-2) + 's' (slen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's' (will_message, packet) = struct.unpack(pack_format, packet) s = s+", will-message="+will_message s = s+", will-qos="+str((flags&24)>>3) s = s+", will-retain="+str((flags&32)>>5) if flags&128: pack_format = "!H" + str(len(packet)-2) + 's' (slen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's' (username, packet) = struct.unpack(pack_format, packet) s = s+", username="+str(username) if flags&64: pack_format = "!H" + str(len(packet)-2) + 's' (slen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(slen)+'s' + str(len(packet)-slen) + 's' (password, packet) = struct.unpack(pack_format, packet) s = s+", password="+str(password) if flags&1: s = s+", reserved=1" return s elif cmd == 0x20: # CONNACK (cmd, rl, resv, rc) = struct.unpack('!BBBB', packet) return "CONNACK, rl="+str(rl)+", res="+str(resv)+", rc="+str(rc) elif cmd == 0x30: # PUBLISH dup = (packet0 & 0x08)>>3 qos = (packet0 & 0x06)>>1 retain = (packet0 & 0x01) (packet, rl) = remaining_length(packet) pack_format = "!H" + str(len(packet)-2) + 's' (tlen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(tlen)+'s' + str(len(packet)-tlen) + 's' (topic, packet) = struct.unpack(pack_format, packet) s = "PUBLISH, rl="+str(rl)+", topic="+str(topic)+", qos="+str(qos)+", retain="+str(retain)+", dup="+str(dup) if qos > 0: pack_format = "!H" + str(len(packet)-2) + 's' (mid, packet) = struct.unpack(pack_format, packet) s = s + ", mid="+str(mid) s = s + ", payload="+str(packet) return s elif cmd == 0x40: # PUBACK if len(packet) == 5: (cmd, rl, mid, reason_code) = struct.unpack('!BBHB', packet) return "PUBACK, rl="+str(rl)+", mid="+str(mid)+", reason_code="+str(reason_code) else: (cmd, rl, mid) = struct.unpack('!BBH', packet) return "PUBACK, rl="+str(rl)+", mid="+str(mid) elif cmd == 0x50: # PUBREC if len(packet) == 5: (cmd, rl, mid, reason_code) = struct.unpack('!BBHB', packet) return "PUBREC, rl="+str(rl)+", mid="+str(mid)+", reason_code="+str(reason_code) else: (cmd, rl, mid) = struct.unpack('!BBH', packet) return "PUBREC, rl="+str(rl)+", mid="+str(mid) elif cmd == 0x60: # PUBREL dup = (packet0 & 0x08)>>3 (cmd, rl, mid) = struct.unpack('!BBH', packet) return "PUBREL, rl="+str(rl)+", mid="+str(mid)+", dup="+str(dup) elif cmd == 0x70: # PUBCOMP (cmd, rl, mid) = struct.unpack('!BBH', packet) return "PUBCOMP, rl="+str(rl)+", mid="+str(mid) elif cmd == 0x80: # SUBSCRIBE (packet, rl) = remaining_length(packet) pack_format = "!H" + str(len(packet)-2) + 's' (mid, packet) = struct.unpack(pack_format, packet) s = "SUBSCRIBE, rl="+str(rl)+", mid="+str(mid) topic_index = 0 while len(packet) > 0: pack_format = "!H" + str(len(packet)-2) + 's' (tlen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(tlen)+'sB' + str(len(packet)-tlen-1) + 's' (topic, qos, packet) = struct.unpack(pack_format, packet) s = s + ", topic"+str(topic_index)+"="+str(topic)+","+str(qos) return s elif cmd == 0x90: # SUBACK (packet, rl) = remaining_length(packet) pack_format = "!H" + str(len(packet)-2) + 's' (mid, packet) = struct.unpack(pack_format, packet) pack_format = "!" + "B"*len(packet) granted_qos = struct.unpack(pack_format, packet) s = "SUBACK, rl="+str(rl)+", mid="+str(mid)+", granted_qos="+str(granted_qos[0]) for i in range(1, len(granted_qos)-1): s = s+", "+str(granted_qos[i]) return s elif cmd == 0xA0: # UNSUBSCRIBE (packet, rl) = remaining_length(packet) pack_format = "!H" + str(len(packet)-2) + 's' (mid, packet) = struct.unpack(pack_format, packet) s = "UNSUBSCRIBE, rl="+str(rl)+", mid="+str(mid) topic_index = 0 while len(packet) > 0: pack_format = "!H" + str(len(packet)-2) + 's' (tlen, packet) = struct.unpack(pack_format, packet) pack_format = "!" + str(tlen)+'s' + str(len(packet)-tlen) + 's' (topic, packet) = struct.unpack(pack_format, packet) s = s + ", topic"+str(topic_index)+"="+str(topic) return s elif cmd == 0xB0: # UNSUBACK (cmd, rl, mid) = struct.unpack('!BBH', packet) return "UNSUBACK, rl="+str(rl)+", mid="+str(mid) elif cmd == 0xC0: # PINGREQ (cmd, rl) = struct.unpack('!BB', packet) return "PINGREQ, rl="+str(rl) elif cmd == 0xD0: # PINGRESP (cmd, rl) = struct.unpack('!BB', packet) return "PINGRESP, rl="+str(rl) elif cmd == 0xE0: # DISCONNECT if len(packet) == 3: (cmd, rl, reason_code) = struct.unpack('!BBB', packet) return "DISCONNECT, rl="+str(rl)+", reason_code="+str(reason_code) else: (cmd, rl) = struct.unpack('!BB', packet) return "DISCONNECT, rl="+str(rl) elif cmd == 0xF0: # AUTH (cmd, rl) = struct.unpack('!BB', packet) return "AUTH, rl="+str(rl) def read_varint(sock, rl): varint = 0 multiplier = 1 while True: byte = sock.recv(1) byte, = struct.unpack("!B", byte) varint += (byte & 127)*multiplier multiplier *= 128 rl -= 1 if byte & 128 == 0x00: return (varint, rl) def mqtt_read_string(sock, rl): slen = sock.recv(2) slen, = struct.unpack("!H", slen) payload = sock.recv(slen) payload, = struct.unpack("!%ds" % (slen), payload) rl -= (2 + slen) return (payload, rl) def read_publish(sock, proto_ver=4): cmd, = struct.unpack("!B", sock.recv(1)) if cmd & 0xF0 != 0x30: raise ValueError qos = (cmd & 0x06) >> 1 rl, t = read_varint(sock, 0) topic, rl = mqtt_read_string(sock, rl) if qos > 0: sock.recv(2) rl -= 1 if proto_ver == 5: proplen, rl = read_varint(sock, rl) sock.recv(proplen) rl -= proplen payload = sock.recv(rl).decode('utf-8') return payload def gen_fixed_hdr(command, remaining_length): return struct.pack("B", command) + pack_remaining_length(remaining_length) def gen_variable_hdr(mid=None): if mid is not None: return struct.pack("!H", mid) else: return b"" def 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): if (proto_ver&0x7F) == 3 or proto_ver == 0: remaining_length = 12 elif (proto_ver&0x7F) == 4 or proto_ver == 5: remaining_length = 10 else: raise ValueError if client_id != None: client_id = client_id.encode("utf-8") remaining_length = remaining_length + 2+len(client_id) else: remaining_length = remaining_length + 2 connect_flags = 0 if connect_reserved: connect_flags = connect_flags | 0x01 if clean_session: connect_flags = connect_flags | 0x02 if proto_ver == 5: if properties == b"": properties += mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20) if session_expiry != -1: properties += mqtt5_props.gen_uint32_prop(mqtt5_props.PROP_SESSION_EXPIRY_INTERVAL, session_expiry) properties = mqtt5_props.prop_finalise(properties) remaining_length += len(properties) if will_topic != None: will_topic = will_topic.encode("utf-8") remaining_length = remaining_length + 2+len(will_topic) + 2+len(will_payload) connect_flags = connect_flags | 0x04 | ((will_qos&0x03) << 3) if will_retain: connect_flags = connect_flags | 32 if proto_ver == 5: will_properties = mqtt5_props.prop_finalise(will_properties) remaining_length += len(will_properties) if username != None: username = username.encode("utf-8") remaining_length = remaining_length + 2+len(username) connect_flags = connect_flags | 0x80 if password != None: password = password.encode("utf-8") connect_flags = connect_flags | 0x40 remaining_length = remaining_length + 2+len(password) rl = pack_remaining_length(remaining_length) packet = struct.pack("!B"+str(len(rl))+"s", 0x10, rl) if (proto_ver&0x7F) == 3 or proto_ver == 0: packet = packet + struct.pack("!H6sBBH", len(b"MQIsdp"), b"MQIsdp", proto_ver, connect_flags, keepalive) elif (proto_ver&0x7F) == 4 or proto_ver == 5: packet = packet + struct.pack("!H4sBBH", len(b"MQTT"), b"MQTT", proto_ver, connect_flags, keepalive) if proto_ver == 5: packet += properties if client_id != None: packet = packet + struct.pack("!H"+str(len(client_id))+"s", len(client_id), bytes(client_id)) else: packet = packet + struct.pack("!H", 0) if will_topic != None: packet += will_properties packet = packet + struct.pack("!H"+str(len(will_topic))+"s", len(will_topic), will_topic) if len(will_payload) > 0: packet = packet + struct.pack("!H"+str(len(will_payload))+"s", len(will_payload), will_payload) else: packet = packet + struct.pack("!H", 0) if username != None: packet = packet + struct.pack("!H"+str(len(username))+"s", len(username), username) if password != None: packet = packet + struct.pack("!H"+str(len(password))+"s", len(password), password) return packet def gen_connack(flags=0, rc=0, proto_ver=4, properties=b"", property_helper=True): if proto_ver == 5: if property_helper == True: if properties is not None: properties = mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_TOPIC_ALIAS_MAXIMUM, 10) \ + properties + mqtt5_props.gen_uint16_prop(mqtt5_props.PROP_RECEIVE_MAXIMUM, 20) else: properties = b"" properties = mqtt5_props.prop_finalise(properties) packet = struct.pack('!BBBB', 32, 2+len(properties), flags, rc) + properties else: packet = struct.pack('!BBBB', 32, 2, flags, rc); return packet def gen_publish(topic, qos, payload=None, retain=False, dup=False, mid=0, proto_ver=4, properties=b""): topic = topic.encode("utf-8") rl = 2+len(topic) pack_format = "H"+str(len(topic))+"s" if qos > 0: rl = rl + 2 pack_format = pack_format + "H" if proto_ver == 5: properties = mqtt5_props.prop_finalise(properties) rl += len(properties) # This will break if len(properties) > 127 pack_format = pack_format + "%ds"%(len(properties)) if payload != None: payload = payload.encode("utf-8") rl = rl + len(payload) pack_format = pack_format + str(len(payload))+"s" else: payload = b"" pack_format = pack_format + "0s" rlpacked = pack_remaining_length(rl) cmd = 48 | (qos<<1) if retain: cmd = cmd + 1 if dup: cmd = cmd + 8 if proto_ver == 5: if qos > 0: return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, mid, properties, payload) else: return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, properties, payload) else: if qos > 0: return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, mid, payload) else: return struct.pack("!B" + str(len(rlpacked))+"s" + pack_format, cmd, rlpacked, len(topic), topic, payload) def _gen_command_with_mid(cmd, mid, proto_ver=4, reason_code=-1, properties=None): if proto_ver == 5 and (reason_code != -1 or properties is not None): if reason_code == -1: reason_code = 0 if properties is None: return struct.pack('!BBHB', cmd, 3, mid, reason_code) elif properties == "": return struct.pack('!BBHBB', cmd, 4, mid, reason_code, 0) else: properties = mqtt5_props.prop_finalise(properties) pack_format = "!BBHB"+str(len(properties))+"s" return struct.pack(pack_format, cmd, 2+1+len(properties), mid, reason_code, properties) else: return struct.pack('!BBH', cmd, 2, mid) def gen_puback(mid, proto_ver=4, reason_code=-1, properties=None): return _gen_command_with_mid(64, mid, proto_ver, reason_code, properties) def gen_pubrec(mid, proto_ver=4, reason_code=-1, properties=None): return _gen_command_with_mid(80, mid, proto_ver, reason_code, properties) def gen_pubrel(mid, dup=False, proto_ver=4, reason_code=-1, properties=None): if dup: cmd = 96+8+2 else: cmd = 96+2 return _gen_command_with_mid(cmd, mid, proto_ver, reason_code, properties) def gen_pubcomp(mid, proto_ver=4, reason_code=-1, properties=None): return _gen_command_with_mid(112, mid, proto_ver, reason_code, properties) def gen_subscribe(mid, topic, qos, cmd=130, proto_ver=4, properties=b""): topic = topic.encode("utf-8") packet = struct.pack("!B", cmd) if proto_ver == 5: if properties == b"": packet += pack_remaining_length(2+1+2+len(topic)+1) pack_format = "!HBH"+str(len(topic))+"sB" return packet + struct.pack(pack_format, mid, 0, len(topic), topic, qos) else: properties = mqtt5_props.prop_finalise(properties) packet += pack_remaining_length(2+1+2+len(topic)+len(properties)) pack_format = "!H"+str(len(properties))+"s"+"H"+str(len(topic))+"sB" return packet + struct.pack(pack_format, mid, properties, len(topic), topic, qos) else: packet += pack_remaining_length(2+2+len(topic)+1) pack_format = "!HH"+str(len(topic))+"sB" return packet + struct.pack(pack_format, mid, len(topic), topic, qos) def gen_suback(mid, qos, proto_ver=4): if proto_ver == 5: return struct.pack('!BBHBB', 144, 2+1+1, mid, 0, qos) else: return struct.pack('!BBHB', 144, 2+1, mid, qos) def gen_unsubscribe(mid, topic, cmd=162, proto_ver=4, properties=b""): topic = topic.encode("utf-8") if proto_ver == 5: if properties == b"": pack_format = "!BBHBH"+str(len(topic))+"s" return struct.pack(pack_format, cmd, 2+2+len(topic)+1, mid, 0, len(topic), topic) else: properties = mqtt5_props.prop_finalise(properties) packet = struct.pack("!B", cmd) l = 2+2+len(topic)+1+len(properties) packet += pack_remaining_length(l) pack_format = "!HB"+str(len(properties))+"sH"+str(len(topic))+"s" packet += struct.pack(pack_format, mid, len(properties), properties, len(topic), topic) return packet else: pack_format = "!BBHH"+str(len(topic))+"s" return struct.pack(pack_format, cmd, 2+2+len(topic), mid, len(topic), topic) def gen_unsubscribe_multiple(mid, topics, proto_ver=4): packet = b"" remaining_length = 0 for t in topics: t = t.encode("utf-8") remaining_length += 2+len(t) packet += struct.pack("!H"+str(len(t))+"s", len(t), t) if proto_ver == 5: remaining_length += 2+1 return struct.pack("!BBHB", 162, remaining_length, mid, 0) + packet else: remaining_length += 2 return struct.pack("!BBH", 162, remaining_length, mid) + packet def gen_unsuback(mid, reason_code=0, proto_ver=4): if proto_ver == 5: if isinstance(reason_code, list): reason_code_count = len(reason_code) p = struct.pack('!BBHB', 176, 3+reason_code_count, mid, 0) for r in reason_code: p += struct.pack('B', r) return p else: return struct.pack('!BBHBB', 176, 4, mid, 0, reason_code) else: return struct.pack('!BBH', 176, 2, mid) def gen_pingreq(): return struct.pack('!BB', 192, 0) def gen_pingresp(): return struct.pack('!BB', 208, 0) def _gen_short(cmd, reason_code=-1, proto_ver=5, properties=None): if proto_ver == 5 and (reason_code != -1 or properties is not None): if reason_code == -1: reason_code = 0 if properties is None: return struct.pack('!BBB', cmd, 1, reason_code) elif properties == "": return struct.pack('!BBBB', cmd, 2, reason_code, 0) else: properties = mqtt5_props.prop_finalise(properties) return struct.pack("!BBB", cmd, 1+len(properties), reason_code) + properties else: return struct.pack('!BB', cmd, 0) def gen_disconnect(reason_code=-1, proto_ver=4, properties=None): return _gen_short(0xE0, reason_code, proto_ver, properties) def gen_auth(reason_code=-1, properties=None): return _gen_short(0xF0, reason_code, 5, properties) def pack_remaining_length(remaining_length): s = b"" while True: byte = remaining_length % 128 remaining_length = remaining_length // 128 # If there are more digits to encode, set the top bit of this digit if remaining_length > 0: byte = byte | 0x80 s = s + struct.pack("!B", byte) if remaining_length == 0: return s def get_port(count=1): if count == 1: if len(sys.argv) == 2: return int(sys.argv[1]) else: return 1888 else: if len(sys.argv) >= 1+count: p = () for i in range(0, count): p = p + (int(sys.argv[1+i]),) return p else: return tuple(range(1888, 1888+count)) def get_lib_port(): if len(sys.argv) == 3: return int(sys.argv[2]) else: return 1888 def do_ping(sock, error_string="pingresp"): do_send_receive(sock, gen_pingreq(), gen_pingresp(), error_string) @atexit.register def test_cleanup(): global vg_logfiles if os.environ.get('MOSQ_USE_VALGRIND') is not None: for f in vg_logfiles: try: if os.stat(f).st_size == 0: os.remove(f) except OSError: pass mosquitto-2.0.18/test/mqtt5_opts.py000066400000000000000000000002661450213760600173260ustar00rootroot00000000000000MQTT_SUB_OPT_NO_LOCAL = 0x04 MQTT_SUB_OPT_RETAIN_AS_PUBLISHED = 0x08 MQTT_SUB_OPT_SEND_RETAIN_ALWAYS = 0x00 MQTT_SUB_OPT_SEND_RETAIN_NEW = 0x10 MQTT_SUB_OPT_SEND_RETAIN_NEVER = 0x20 mosquitto-2.0.18/test/mqtt5_props.py000066400000000000000000000037041450213760600175040ustar00rootroot00000000000000import struct PROP_PAYLOAD_FORMAT_INDICATOR = 1 PROP_MESSAGE_EXPIRY_INTERVAL = 2 PROP_CONTENT_TYPE = 3 PROP_RESPONSE_TOPIC = 8 PROP_CORRELATION_DATA = 9 PROP_SUBSCRIPTION_IDENTIFIER = 11 PROP_SESSION_EXPIRY_INTERVAL = 17 PROP_ASSIGNED_CLIENT_IDENTIFIER = 18 PROP_SERVER_KEEP_ALIVE = 19 PROP_AUTHENTICATION_METHOD = 21 PROP_AUTHENTICATION_DATA = 22 PROP_REQUEST_PROBLEM_INFO = 23 PROP_WILL_DELAY_INTERVAL = 24 PROP_REQUEST_RESPONSE_INFO = 25 PROP_RESPONSE_INFO = 26 PROP_SERVER_REFERENCE = 28 PROP_REASON_STRING = 31 PROP_RECEIVE_MAXIMUM = 33 PROP_TOPIC_ALIAS_MAXIMUM = 34 PROP_TOPIC_ALIAS = 35 PROP_MAXIMUM_QOS = 36 PROP_RETAIN_AVAILABLE = 37 PROP_USER_PROPERTY = 38 PROP_MAXIMUM_PACKET_SIZE = 39 PROP_WILDCARD_SUB_AVAILABLE = 40 PROP_SUBSCRIPTION_ID_AVAILABLE = 41 PROP_SHARED_SUB_AVAILABLE = 42 def gen_byte_prop(identifier, byte): prop = struct.pack('BB', identifier, byte) return prop def gen_uint16_prop(identifier, word): prop = struct.pack('!BH', identifier, word) return prop def gen_uint32_prop(identifier, word): prop = struct.pack('!BI', identifier, word) return prop def gen_string_prop(identifier, s): s = s.encode("utf-8") prop = struct.pack('!BH%ds'%(len(s)), identifier, len(s), s) return prop def gen_string_pair_prop(identifier, s1, s2): s1 = s1.encode("utf-8") s2 = s2.encode("utf-8") prop = struct.pack('!BH%dsH%ds'%(len(s1), len(s2)), identifier, len(s1), s1, len(s2), s2) return prop def gen_varint_prop(identifier, val): v = pack_varint(val) return struct.pack("!B"+str(len(v))+"s", identifier, v) def pack_varint(varint): s = b"" while True: byte = varint % 128 varint = varint // 128 # If there are more digits to encode, set the top bit of this digit if varint > 0: byte = byte | 0x80 s = s + struct.pack("!B", byte) if varint == 0: return s def prop_finalise(props): return pack_varint(len(props)) + props mosquitto-2.0.18/test/mqtt5_rc.py000066400000000000000000000027311450213760600167440ustar00rootroot00000000000000MQTT_RC_SUCCESS = 0 MQTT_RC_NORMAL_DISCONNECTION = 0 MQTT_RC_GRANTED_QOS0 = 0 MQTT_RC_GRANTED_QOS1 = 1 MQTT_RC_GRANTED_QOS2 = 2 MQTT_RC_DISCONNECT_WITH_WILL_MSG = 4 MQTT_RC_NO_MATCHING_SUBSCRIBERS = 16 MQTT_RC_NO_SUBSCRIPTION_EXISTED = 17 MQTT_RC_CONTINUE_AUTHENTICATION = 24 MQTT_RC_REAUTHENTICATE = 25 MQTT_RC_UNSPECIFIED = 128 MQTT_RC_MALFORMED_PACKET = 129 MQTT_RC_PROTOCOL_ERROR = 130 MQTT_RC_IMPLEMENTATION_SPECIFIC = 131 MQTT_RC_UNSUPPORTED_PROTOCOL_VERSION = 132 MQTT_RC_CLIENTID_NOT_VALID = 133 MQTT_RC_BAD_USERNAME_OR_PASSWORD = 134 MQTT_RC_NOT_AUTHORIZED = 135 MQTT_RC_SERVER_UNAVAILABLE = 136 MQTT_RC_SERVER_BUSY = 137 MQTT_RC_BANNED = 138 MQTT_RC_SERVER_SHUTTING_DOWN = 139 MQTT_RC_BAD_AUTHENTICATION_METHOD = 140 MQTT_RC_KEEP_ALIVE_TIMEOUT = 141 MQTT_RC_SESSION_TAKEN_OVER = 142 MQTT_RC_TOPIC_FILTER_INVALID = 143 MQTT_RC_TOPIC_NAME_INVALID = 144 MQTT_RC_PACKET_ID_IN_USE = 145 MQTT_RC_PACKET_ID_NOT_FOUND = 146 MQTT_RC_RECEIVE_MAXIMUM_EXCEEDED = 147 MQTT_RC_TOPIC_ALIAS_INVALID = 148 MQTT_RC_PACKET_TOO_LARGE = 149 MQTT_RC_MESSAGE_RATE_TOO_HIGH = 150 MQTT_RC_QUOTA_EXCEEDED = 151 MQTT_RC_ADMINISTRATIVE_ACTION = 152 MQTT_RC_PAYLOAD_FORMAT_INVALID = 153 MQTT_RC_RETAIN_NOT_SUPPORTED = 154 MQTT_RC_QOS_NOT_SUPPORTED = 155 MQTT_RC_USE_ANOTHER_SERVER = 156 MQTT_RC_SERVER_MOVED = 157 MQTT_RC_SHARED_SUBS_NOT_SUPPORTED = 158 MQTT_RC_CONNECTION_RATE_EXCEEDED = 159 MQTT_RC_MAXIMUM_CONNECT_TIME = 160 MQTT_RC_SUBSCRIPTION_IDS_NOT_SUPPORTED = 161 MQTT_RC_WILDCARD_SUBS_NOT_SUPPORTED = 162 mosquitto-2.0.18/test/old/000077500000000000000000000000001450213760600154075ustar00rootroot00000000000000mosquitto-2.0.18/test/old/Makefile000066400000000000000000000010351450213760600170460ustar00rootroot00000000000000include ../../config.mk CC=cc CFLAGS=-I../../src -I../../include -I. -I../.. -Wall -ggdb -DDEBUG -DWITH_CLIENT LDFLAGS= SOVERSION=1 .PHONY: all test clean all : msgsps_pub msgsps_sub msgsps_pub : msgsps_pub.o ${CC} $^ -o $@ ../../lib/libmosquitto.so.${SOVERSION} msgsps_pub.o : msgsps_pub.c msgsps_common.h ${CC} $(CFLAGS) -c $< -o $@ msgsps_sub : msgsps_sub.o ${CC} $^ -o $@ ../../lib/libmosquitto.so.${SOVERSION} msgsps_sub.o : msgsps_sub.c msgsps_common.h ${CC} $(CFLAGS) -c $< -o $@ clean : -rm -f *.o msgsps_pub msgsps_sub mosquitto-2.0.18/test/old/msgsps_common.h000066400000000000000000000001541450213760600204440ustar00rootroot00000000000000#define HOST "127.0.0.1" #define PORT 1883 #define PUB_QOS 1 #define SUB_QOS 1 #define MESSAGE_SIZE 1024L mosquitto-2.0.18/test/old/msgsps_pub.c000066400000000000000000000020361450213760600177360ustar00rootroot00000000000000/* This provides a crude manner of testing the performance of a broker in messages/s. */ #include #include #include #include #include #include #include #include static atomic_int message_count = 0; void my_publish_callback(struct mosquitto *mosq, void *obj, int mid) { message_count++; } int main(int argc, char *argv[]) { struct mosquitto *mosq; int i; uint8_t buf[MESSAGE_SIZE]; mosquitto_lib_init(); mosq = mosquitto_new(NULL, true, NULL); mosquitto_publish_callback_set(mosq, my_publish_callback); mosquitto_connect(mosq, HOST, PORT, 600); mosquitto_loop_start(mosq); i=0; while(1){ mosquitto_publish(mosq, NULL, "perf/test", sizeof(buf), buf, PUB_QOS, false); usleep(100); i++; if(i == 10000){ /* Crude "messages per second" count */ i = message_count; message_count = 0; printf("%d\n", i); i = 0; } } mosquitto_loop_stop(mosq, false); mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return 0; } mosquitto-2.0.18/test/old/msgsps_sub.c000066400000000000000000000016121450213760600177400ustar00rootroot00000000000000/* This provides a crude manner of testing the performance of a broker in messages/s. */ #include #include #include #include #include #include #include #include static atomic_int message_count = 0; void my_message_callback(struct mosquitto *mosq, void *obj, const struct mosquitto_message *msg) { message_count++; } int main(int argc, char *argv[]) { struct mosquitto *mosq; int c; mosquitto_lib_init(); mosq = mosquitto_new(NULL, true, NULL); mosquitto_message_callback_set(mosq, my_message_callback); mosquitto_connect(mosq, HOST, PORT, 600); mosquitto_subscribe(mosq, NULL, "perf/test", SUB_QOS); mosquitto_loop_start(mosq); while(1){ sleep(1); c = message_count; message_count = 0; printf("%d\n", c); } mosquitto_destroy(mosq); mosquitto_lib_cleanup(); return 0; } mosquitto-2.0.18/test/ptest.py000077500000000000000000000043671450213760600163570ustar00rootroot00000000000000#!/usr/bin/env python3 import subprocess import time import sys def next_test(tests, ports): if len(tests) == 0 or len(ports) == 0: return test = tests.pop() proc_ports = () if len(ports) < test[0]: tests.insert(0, test) return None else: if isinstance(test[1], (list,)): args = test[1] else: args = [test[1]] for i in range(0, test[0]): proc_port = ports.pop() proc_ports = proc_ports + (proc_port,) args.append(str(proc_port)) proc = subprocess.Popen(args) proc.start_time = time.time() proc.mosq_port = proc_ports return proc def run_tests(tests, minport=1888, max_running=20): ports = list(range(minport, minport+max_running+1)) start_time = time.time() passed = 0 failed = 0 failed_tests = [] running_tests = [] while len(tests) > 0 or len(running_tests) > 0: if len(running_tests) <= max_running: t = next_test(tests, ports) if t is None: time.sleep(0.1) else: running_tests.append(t) for t in running_tests: t.poll() if t.returncode is not None: running_tests.remove(t) if isinstance(t.mosq_port, tuple): for portret in t.mosq_port: ports.append(portret) else: ports.append(t.mosq_port) t.terminate() t.wait() runtime = time.time() - t.start_time #(stdo, stde) = t.communicate() if t.returncode == 1: print("%0.3fs : \033[31m%s\033[0m" % (runtime, t.args[0])) failed = failed + 1 failed_tests.append(t.args[0]) else: passed = passed + 1 print("%0.3fs : \033[32m%s\033[0m" % (runtime, t.args[0])) print("Passed: %d\nFailed: %d\nTotal: %d\nTotal time: %0.2f" % (passed, failed, passed+failed, time.time()-start_time)) if failed > 0: print("Failing tests:") failed_tests.sort() for f in failed_tests: print(f) sys.exit(1) mosquitto-2.0.18/test/random/000077500000000000000000000000001450213760600161115ustar00rootroot00000000000000mosquitto-2.0.18/test/random/Makefile000066400000000000000000000007451450213760600175570ustar00rootroot00000000000000include ../../config.mk .PHONY: all test ifeq ($(WITH_SHARED_LIBRARIES),yes) LIB_DEP:=../../lib/libmosquitto.so.${SOVERSION} else LIB_DEP:=../../lib/libmosquitto.a endif all : auth_plugin.so auth_plugin.so : auth_plugin.c $(CC) ${CFLAGS} -fPIC -shared $< -o $@ -I../../lib -I../../src ../lib/libmosquitto.so.${SOVERSION} : $(MAKE) -C ../../lib ../lib/libmosquitto.a : $(MAKE) -C ../../lib libmosquitto.a clean : -rm -f *.o random_client *.gcda *.gcno test : all ./test.py mosquitto-2.0.18/test/random/auth_plugin.c000066400000000000000000000025741450213760600206040ustar00rootroot00000000000000#include #include #include #include #include #include int mosquitto_auth_plugin_version(void) { return MOSQ_AUTH_PLUGIN_VERSION; } int mosquitto_auth_plugin_init(void **user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { srandom(time(NULL)); return MOSQ_ERR_SUCCESS; } int mosquitto_auth_plugin_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_init(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_security_cleanup(void *user_data, struct mosquitto_opt *auth_opts, int auth_opt_count, bool reload) { return MOSQ_ERR_SUCCESS; } int mosquitto_auth_acl_check(void *user_data, int access, struct mosquitto *client, const struct mosquitto_acl_msg *msg) { if(random() % 2 == 0){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_ACL_DENIED; } } int mosquitto_auth_unpwd_check(void *user_data, struct mosquitto *client, const char *username, const char *password) { if(random() % 2 == 0){ return MOSQ_ERR_SUCCESS; }else{ return MOSQ_ERR_AUTH; } } int mosquitto_auth_psk_key_get(void *user_data, struct mosquitto *client, const char *hint, const char *identity, char *key, int max_key_len) { return MOSQ_ERR_AUTH; } mosquitto-2.0.18/test/random/pwfile000066400000000000000000000001621450213760600173210ustar00rootroot00000000000000test:$6$cBP7e6sUriMSh8yf$+Z3E9P1g+Hui8zDJA+XJpTHl6+0eym0MtWokmOY4j1svAR5RtjZoXB4OQuHYzrGrdp1e8gXoqcKlcP+1lmepmg== mosquitto-2.0.18/test/random/random.conf000066400000000000000000000031251450213760600202410ustar00rootroot00000000000000per_listener_settings true # Unencrypted MQTT over TCP listener 1883 listener 1884 password_file pwfile listener 1885 auth_plugin ./auth_plugin.so listener 1886 password_file pwfile auth_plugin ./auth_plugin.so # Encrypted MQTT over TCP listener 8883 cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key listener 8884 cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key password_file pwfile listener 8885 cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key auth_plugin ./auth_plugin.so listener 8886 cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key password_file pwfile auth_plugin ./auth_plugin.so # Unencrypted MQTT over WebSockets listener 8000 protocol websockets listener 8001 protocol websockets password_file pwfile listener 8002 protocol websockets auth_plugin ./auth_plugin.so listener 8003 protocol websockets password_file pwfile auth_plugin ./auth_plugin.so # Encrypted MQTT over WebSockets listener 4430 protocol websockets cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key listener 4431 protocol websockets cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key password_file pwfile listener 4432 protocol websockets cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key auth_plugin ./auth_plugin.so listener 4433 protocol websockets cafile ../ssl/all-ca.crt certfile ../ssl/server.crt keyfile ../ssl/server.key password_file pwfile auth_plugin ./auth_plugin.so #log_dest file mosquitto.log #log_type all mosquitto-2.0.18/test/random/random_client.py000077500000000000000000000117771450213760600213210ustar00rootroot00000000000000#!/usr/bin/env python3 import paho.mqtt.client as paho import random import sys import time # This is a client that carries out randomised behaviour. It is intended for # use with the local config file. This file has multiple listeners configured: # * 1883 - unencrypted MQTT over TCP with no authentication # * 1884 - unencrypted MQTT over TCP with password authentication # * 1885 - unencrypted MQTT over TCP with plugin authentication # * 1886 - unencrypted MQTT over TCP with password and plugin authentication # # * 8883 - encrypted MQTT over TCP with no authentication # * 8884 - encrypted MQTT over TCP with password authentication # * 8885 - encrypted MQTT over TCP with plugin authentication # * 8886 - encrypted MQTT over TCP with password and plugin authentication # # * 8000 - unencrypted MQTT over WebSockets with no authentication # * 8001 - unencrypted MQTT over WebSockets with password authentication # * 8002 - unencrypted MQTT over WebSockets with plugin authentication # * 8003 - unencrypted MQTT over WebSockets with password and plugin authentication # # * 4430 - encrypted MQTT over WebSockets with no authentication # * 4431 - encrypted MQTT over WebSockets with password authentication # * 4432 - encrypted MQTT over WebSockets with plugin authentication # * 4433 - encrypted MQTT over WebSockets with password and plugin authentication # # The client randomly picks: # * A port out of the list # * Whether to use encryption # * Whether to use WebSockets # * Clean start or not # * Session expiry interval or not # * QoS to use when subscribing - topics "outgoing/[client id]/message" and "response/#" # * Lifetime of connection # On a per publish message basis it chooses: # * QoS of message # * Topic of message "outgoing/[0-max client]/message" # * Retain # * Interval until next outgoing message ports = [ {"port":1883, "tls":False, "transport":"tcp", "auth":False}, {"port":1884, "tls":False, "transport":"tcp", "auth":True}, {"port":1885, "tls":False, "transport":"tcp", "auth":True}, {"port":1886, "tls":False, "transport":"tcp", "auth":True}, {"port":8883, "tls":True, "transport":"tcp", "auth":False}, {"port":8884, "tls":True, "transport":"tcp", "auth":True}, {"port":8885, "tls":True, "transport":"tcp", "auth":True}, {"port":8886, "tls":True, "transport":"tcp", "auth":True}, {"port":8000, "tls":False, "transport":"websockets", "auth":False}, {"port":8001, "tls":False, "transport":"websockets", "auth":True}, {"port":8002, "tls":False, "transport":"websockets", "auth":True}, {"port":8003, "tls":False, "transport":"websockets", "auth":True}, {"port":4430, "tls":True, "transport":"websockets", "auth":False}, {"port":4431, "tls":True, "transport":"websockets", "auth":True}, {"port":4432, "tls":True, "transport":"websockets", "auth":True}, {"port":4433, "tls":True, "transport":"websockets", "auth":True}, ] booleans = [True, False] qos_values = [0, 1, 2] def on_connect(client, userdata, flags, rc): global running if rc == 0: client.subscribe("response/#", subscribe_qos) client.subscribe("outgoing/%s/message" % (client_id), subscribe_qos) else: running = False def on_message(client, userdata, msg): pass def on_publish(client, userdata, mid): pass def on_disconnect(client, userdata, rc): running = False def do_publish(client): retain = random.choice(booleans) qos = random.choice(qos_values) topic = "outgoing/%d/message" % (random.uniform(1, 1000)) payload = "message" client.publish(topic, payload, qos, retain) next_publish_time = time.time() + random.uniform(0.1, 2.0) def main(): global running global lifetime mqttc = paho.Client(client_id, clean_session=clean_start, protocol=protocol, transport=transport) mqttc.on_message = on_message mqttc.on_publish = on_publish mqttc.on_connect = on_connect mqttc.on_disconnect = on_disconnect if auth and random.choice(booleans): if random.choice(booleans): mqttc.username_pw_set("test", "password") else: mqttc.username_pw_set("bad", "bad") if use_tls: mqttc.tls_set(ca_certs="../ssl/all-ca.crt") mqttc.connect("localhost", port) mqttc.loop_start() while running: time.sleep(0.1) now = time.time() if now > next_publish_time: do_publish(mqttc) if now > lifetime: if random.choice(booleans): mqttc.disconnect() lifetime += 5.0 else: running = False p = random.choice(ports) port = p["port"] use_tls = p["tls"] transport = p["transport"] auth = p["auth"] client_id = "cid"+sys.argv[1] clean_start = random.choice(booleans) subscribe_qos = random.choice(qos_values) protocol = paho.MQTTv311 next_publish_time = time.time() + random.uniform(0.1, 2.0) lifetime = time.time() + random.uniform(5.0, 10.0) running = True main() mosquitto-2.0.18/test/random/test.py000077500000000000000000000020351450213760600174450ustar00rootroot00000000000000#!/usr/bin/env python3 import subprocess import time import sys def next_client(clients): if len(clients) == 0: return c = clients.pop() args = ["./random_client.py", str(c)] proc = subprocess.Popen(args, stderr=subprocess.DEVNULL) proc.cid = c return proc def run_clients(max_clients): clients = list(range(1, max_clients)) start_time = time.time() running_clients = [] while True: print(len(running_clients)) if len(running_clients) < max_clients: c = next_client(clients) if c is not None: running_clients.append(c) else: time.sleep(0.1) for c in running_clients: c.poll() if c.returncode is not None: running_clients.remove(c) clients.append(c.cid) c.terminate() c.wait() env = {} env["LD_LIBRARY_PATH"] = "../../lib" #broker = subprocess.Popen(["../../src/mosquitto", "-c", "random.conf"], env=env) run_clients(1000) mosquitto-2.0.18/test/ssl/000077500000000000000000000000001450213760600154325ustar00rootroot00000000000000mosquitto-2.0.18/test/ssl/all-ca.crt000066400000000000000000000131671450213760600173050ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, L=Derby, O=Mosquitto Project, OU=Testing, CN=Root CA Validity Not Before: Feb 25 14:54:18 2020 GMT Not After : Feb 23 14:54:18 2025 GMT Subject: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:c1:a1:1a:6e:76:1f:98:b7:1c:7e:d6:67:d5:dc: 92:34:ef:48:22:62:94:56:cb:21:29:c1:88:7c:7a: 62:eb:6d:b9:af:8b:80:75:f4:8e:32:e2:20:e2:fa: 3a:49:c8:20:74:53:83:0f:c1:48:e2:13:3e:48:27: f2:e5:7d:55:c5:87:8c:41:9e:e2:90:58:8c:09:97: 1e:bc:5a:ce:10:71:b2:66:02:02:9b:0c:d0:24:47: 7a:3a:4d:3a:2e:c0:f0:65:6b:6a:cf:13:13:8a:f0: 6d:a0:a5:80:5f:6b:58:77:ae:91:6e:ba:ab:c5:c0: 24:f7:22:27:a4:bf:47:52:2d:a0:fc:56:b0:19:16: 84:e9:53:ac:1d:7f:29:af:c2:86:44:f5:9b:04:e4: bf:8f:e1:b8:61:a0:63:55:0a:7a:93:2a:d8:4a:20: b8:6b:b6:e9:20:c6:2c:c2:93:c2:dc:7a:69:90:8e: ea:00:5b:0c:66:8a:90:74:b4:d9:01:98:9d:fe:5b: 66:e0:39:19:22:50:0d:76:3d:1c:04:fb:93:4d:6e: 45:da:e8:cc:27:35:2a:a6:35:a8:87:e1:99:32:42: e8:71:eb:7c:f9:69:70:c7:cf:c5:cc:61:c5:ae:47: dc:20:86:2b:2b:fe:1c:dd:2c:e9:b0:38:b6:72:8e: 09:e9 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 X509v3 Authority Key Identifier: keyid:7A:89:5D:1E:C9:B1:72:2F:38:DB:DE:E7:D3:49:80:2C:01:FA:3B:74 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha256WithRSAEncryption d3:8d:e3:33:87:f3:1e:4f:ff:da:1d:f8:61:3f:4a:ae:21:49: cd:ee:b1:e0:62:ab:44:70:a8:29:92:83:8d:33:45:4c:ac:b0: 66:a0:e8:32:23:76:ef:aa:89:7d:bc:e1:04:17:a5:d7:39:59: 99:ab:d9:bf:0c:fd:c5:b6:ad:6f:45:39:c9:27:f1:3e:c0:af: c3:8e:b1:1f:8f:fc:34:66:31:f4:f1:11:a0:27:99:a2:65:e2: aa:20:a7:98:b6:0e:ff:71:5e:10:e7:ab:1e:33:e7:fb:c8:59: d7:89:7a:3b:d9:a9:9f:48:2f:2e:ff:02:61:cd:86:47:60:61: 8e:81:71:68:f0:cd:63:72:b8:d2:7d:22:9d:6b:07:49:3a:0a: f7:8b:94:b3:98:90:3c:9f:e5:78:1b:84:a9:2e:fb:85:64:59: ce:6f:33:05:18:bc:21:df:f5:7c:10:79:d6:58:34:61:0e:1f: d5:af:b6:a0:8f:86:ce:56:d1:67:4f:b8:7e:50:2d:ba:77:37: 50:0f:91:06:dc:a8:7f:3c:8b:2b:8b:47:df:e3:7e:2f:79:81: 22:70:eb:f9:14:f3:66:73:17:33:e4:26:7e:47:df:80:89:de: a5:e8:5a:a9:c0:4b:3e:1b:9b:11:4b:3b:b4:8b:6a:9d:6c:ce: 39:f5:04:c9 -----BEGIN CERTIFICATE----- MIIDojCCAoqgAwIBAgIBATANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1v c3F1aXR0byBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdSb290 IENBMB4XDTIwMDIyNTE0NTQxOFoXDTI1MDIyMzE0NTQxOFowZTELMAkGA1UEBhMC R0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9q ZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRMwEQYDVQQDDApTaWduaW5nIENBMIIBIjAN BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwaEabnYfmLccftZn1dySNO9IImKU VsshKcGIfHpi6225r4uAdfSOMuIg4vo6ScggdFODD8FI4hM+SCfy5X1VxYeMQZ7i kFiMCZcevFrOEHGyZgICmwzQJEd6Ok06LsDwZWtqzxMTivBtoKWAX2tYd66Rbrqr xcAk9yInpL9HUi2g/FawGRaE6VOsHX8pr8KGRPWbBOS/j+G4YaBjVQp6kyrYSiC4 a7bpIMYswpPC3HppkI7qAFsMZoqQdLTZAZid/ltm4DkZIlANdj0cBPuTTW5F2ujM JzUqpjWoh+GZMkLocet8+Wlwx8/FzGHFrkfcIIYrK/4c3SzpsDi2co4J6QIDAQAB o1AwTjAdBgNVHQ4EFgQUqlpbHJEymz+bw0Js0mj2p+DPvuAwHwYDVR0jBBgwFoAU eoldHsmxci84297n00mALAH6O3QwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsF AAOCAQEA043jM4fzHk//2h34YT9KriFJze6x4GKrRHCoKZKDjTNFTKywZqDoMiN2 76qJfbzhBBel1zlZmavZvwz9xbatb0U5ySfxPsCvw46xH4/8NGYx9PERoCeZomXi qiCnmLYO/3FeEOerHjPn+8hZ14l6O9mpn0gvLv8CYc2GR2BhjoFxaPDNY3K40n0i nWsHSToK94uUs5iQPJ/leBuEqS77hWRZzm8zBRi8Id/1fBB51lg0YQ4f1a+2oI+G zlbRZ0+4flAtunc3UA+RBtyofzyLK4tH3+N+L3mBInDr+RTzZnMXM+QmfkffgIne pehaqcBLPhubEUs7tItqnWzOOfUEyQ== -----END CERTIFICATE----- -----BEGIN CERTIFICATE----- MIIDwjCCAqqgAwIBAgIURMxcSM9J+pY3g2SE3qoM34dHwPkwDQYJKoZIhvcNAQEL BQAwcjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM BURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz dGluZzEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yMDAyMjUxNDU0MThaFw0zMDAyMjIx NDU0MThaMHIxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYD VQQHDAVEZXJieTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNVBAsM B1Rlc3RpbmcxEDAOBgNVBAMMB1Jvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDdpftss7fN4lzDhppzwj2WfRehR95WYmiWnXoEsKyEfuh1hINs vvI3tz1FWEb/usORr6XGZhgYwjIpSORMoBxuOZh8RDNPmO9KpLYXN1i4g+CfkGAK QoBUr7FGGlKDaK4fRg6xx8BKQ1Lxqrx+iAOpIT7tU9YYPYrwiYbdhaYwfMTKXyCl V+JypRRKWgzUkua4YRb2TnEH33NaXS0Tw+A0tRxSN26vwRheCrVfo+6CUB0kEaON +syuiHP1mGrHj3bMh/MTd3H5u2lu+1GW/Re3HdGFLuHhEq6EkF0fnPCaPS+iJKwU 1LgQZwGc+UHglTmmqUS6xhpm++/950fYoaiHAgMBAAGjUDBOMB0GA1UdDgQWBBR6 iV0eybFyLzjb3ufTSYAsAfo7dDAfBgNVHSMEGDAWgBR6iV0eybFyLzjb3ufTSYAs Afo7dDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB7/Zn0VBciDCXo JA4ZX5boZyQMx7Lm62O+ixChT2hW0VNlouacgfSq455sNxFJKam0ZQKzusMzssNQ ticyZUwIosGx36f8qBaGksx0EbgAh9QdOulsYDLW5UsB4Rh94C36NoTd9+BJF6D4 89IpuxQehDKKuRG0NUChEkLvJ2AAPi/+iDHZQMB/sAzaT4gJ4eMeY4p4XBb/a9P2 w05RCpVNyLg32S7ynLNUrz+/lZUfZ8sYhpdECbFDpb0e1iVc1vst8Pur+cSGFO3f HabwuWTdF9Xx8MaH/n32Pv8BxZ/hBdjsXa/CiMyT4POs6XGTpZ2iLcmHo8WS4Uls 5gKvsjuj -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/client-encrypted.crt000066400000000000000000000107231450213760600214200ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 6 (0x6) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Validity Not Before: Feb 25 14:54:19 2020 GMT Not After : Feb 23 14:54:19 2025 GMT Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client encrypted Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:b3:5c:d2:08:93:da:a7:4e:a7:39:74:b0:a2:cd: ce:f7:02:03:74:5d:f1:de:db:ff:51:68:d8:02:51: 1e:80:ac:aa:7c:90:c1:32:c9:7a:e5:ac:c4:bb:c5: 6a:c4:2b:96:3f:14:db:a2:b3:89:b6:24:ec:2f:80: 8a:a3:3b:89:89:29:c1:6a:de:ec:70:b0:9b:cb:92: cf:a1:25:0f:d1:9e:cf:be:71:63:b6:82:85:42:83: e9:bf:38:56:8c:f1:75:a7:7f:76:14:50:4c:67:bb: 53:a2:97:cf:5e:35:bd:fc:bb:c6:fd:aa:6e:fb:d7: fc:9e:64:74:61:6f:ea:48:c0:c1:01:2c:69:f9:20: 0e:6a:d1:d3:a1:a3:3f:f7:0d:88:71:93:27:47:5f: 94:d4:27:c5:9f:4b:be:86:0b:7f:73:dd:97:28:91: f0:aa:f0:09:de:a7:b6:1b:d7:ca:91:34:b0:b9:95: 2c:0f:14:1a:ce:da:84:bd:60:5e:f4:f0:f0:87:71: 93:44:70:88:3e:1c:2f:4e:16:a5:3c:9c:40:09:a0: 22:bd:b4:96:61:cb:e7:58:40:98:6e:61:d3:7f:ae: bf:6d:9a:d1:6b:04:c3:55:bd:93:da:95:0d:06:65: 65:19:3e:bc:d8:80:12:8f:d8:74:9a:20:5e:db:b6: b1:29 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: A4:B3:5E:21:A5:51:C7:37:ED:86:36:79:88:D7:36:88:FE:D0:3F:5D X509v3 Authority Key Identifier: keyid:AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 Signature Algorithm: sha256WithRSAEncryption 79:90:68:9b:1f:92:48:63:e4:bd:4d:1c:65:a5:b2:25:71:92: b4:13:41:b9:9b:fc:50:0b:38:65:34:17:da:22:9c:9f:8e:2b: a9:25:06:02:00:49:89:c4:cb:cc:6e:3d:b5:09:15:9f:f9:25: e7:a1:61:51:20:9f:68:f3:42:e3:41:70:a8:4a:7a:11:31:3d: 55:f5:20:49:d2:12:f5:1d:f6:c5:11:48:e5:c2:e8:47:bc:e1: d6:e1:a9:d9:f8:d7:78:18:b5:f5:9b:dd:cf:05:88:9e:06:59: 54:a5:b8:1e:db:3e:51:12:28:f4:c3:ff:cb:a3:77:19:b2:86: 05:6e:e0:82:77:7f:5f:cd:79:48:c1:bb:39:3b:67:a9:7d:0a: 65:24:de:ba:3e:01:a0:be:af:10:88:88:ec:24:b7:8f:ad:49: b0:b6:cf:ee:65:bd:78:39:c7:53:0e:a1:32:58:62:af:4c:54: 7d:3d:4a:20:ea:c7:b7:15:08:1a:29:f7:40:ab:a2:46:10:b5: e5:39:31:88:97:6d:54:fd:d5:9c:24:d1:88:e7:3f:97:b8:75: 54:f6:83:c3:de:13:c5:55:5a:df:da:af:d2:f8:d9:3a:c1:83: 75:a7:1c:c3:17:13:b8:94:54:73:65:11:87:11:e3:d8:5e:48: df:32:95:95 -----BEGIN CERTIFICATE----- MIID3jCCAsagAwIBAgIBBjANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx EDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjAwMjI1 MTQ1NDE5WhcNMjUwMjIzMTQ1NDE5WjCBgjELMAkGA1UEBhMCR0IxGDAWBgNVBAgM D05vdHRpbmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwG U2VydmVyMRMwEQYDVQQLDApQcm9kdWN0aW9uMR4wHAYDVQQDDBV0ZXN0IGNsaWVu dCBlbmNyeXB0ZWQwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCzXNII k9qnTqc5dLCizc73AgN0XfHe2/9RaNgCUR6ArKp8kMEyyXrlrMS7xWrEK5Y/FNui s4m2JOwvgIqjO4mJKcFq3uxwsJvLks+hJQ/Rns++cWO2goVCg+m/OFaM8XWnf3YU UExnu1Oil89eNb38u8b9qm771/yeZHRhb+pIwMEBLGn5IA5q0dOhoz/3DYhxkydH X5TUJ8WfS76GC39z3ZcokfCq8Anep7Yb18qRNLC5lSwPFBrO2oS9YF708PCHcZNE cIg+HC9OFqU8nEAJoCK9tJZhy+dYQJhuYdN/rr9tmtFrBMNVvZPalQ0GZWUZPrzY gBKP2HSaIF7btrEpAgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8W HU9wZW5TU0wgR2VuZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBSks14hpVHH N+2GNnmI1zaI/tA/XTAfBgNVHSMEGDAWgBSqWlsckTKbP5vDQmzSaPan4M++4DAN BgkqhkiG9w0BAQsFAAOCAQEAeZBomx+SSGPkvU0cZaWyJXGStBNBuZv8UAs4ZTQX 2iKcn44rqSUGAgBJicTLzG49tQkVn/kl56FhUSCfaPNC40FwqEp6ETE9VfUgSdIS 9R32xRFI5cLoR7zh1uGp2fjXeBi19ZvdzwWIngZZVKW4Hts+URIo9MP/y6N3GbKG BW7ggnd/X815SMG7OTtnqX0KZSTeuj4BoL6vEIiI7CS3j61JsLbP7mW9eDnHUw6h Mlhir0xUfT1KIOrHtxUIGin3QKuiRhC15TkxiJdtVP3VnCTRiOc/l7h1VPaDw94T xVVa39qv0vjZOsGDdaccwxcTuJRUc2URhxHj2F5I3zKVlQ== -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/client-encrypted.key000066400000000000000000000033271450213760600214220ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED DEK-Info: DES-EDE3-CBC,D72CFBC7CD65D7FB vNK1aAAQQ+mBvJuQh9hM34Vn+yqj+ISIP5bkrtwfGIjTnnN3KNbCqUPhVTaO1uz9 YA1qpCpr+64oLjpcPBZ1tIVhVwcIoq8/nCJKiWEA7/zICDq1f2x7t3JP5NoP9TlD GaAm77JmriUZY0/DEyVBLhRyRIasO3tQSKnmHY1IHI1xalmxK46FTF8YsNpUr5bu UdhUyfslVVFxgSArrrQfPi6GS/ik/4Kr9RZUb3U3Zx1xxACGIDudQIYlkSzy0Ye5 7ff6JS3Y3UzPi//RKBUVWxqSwPlv6KqfNLQ+vNB4mnILSMmxHrOVY2bB+6c5OdP9 LSqDF/Rt+Xga9FraibHtvkrZsr3zQ6IlFt6sjvnN5K2v8d70wep2+lS8o7ljaQkp dgvP5YcWhdpMA5ye7JlVQOq7ApmIP1/MhlpLeH7gSZpazKvjQHzPLhjMTjJgDZLj lHn3z0+iLIY7Jt9O0NCKHcGvc96A0y+CPt5OaXYLzbnAZXr3HXwB0t85L9FXtZfp DL0uFgzQavqQagR6fQvN+F2D/jK3aMA1BmOAlTXl19OgEeXRulltKQfKrE+66W99 HVfRxapJMdtIvbZ4unzC4AHmLoDhTF2ryA0U97JeIta3NAk0D1gR9Q3uTWL3FKaO 4zyTnkUmhEe1xOkXu69n6FannSE0HlyuceX/9VvnPobEXAfs1C5Z3GQ+6WlXtQPh +Aaa0mkEG9zukXf1txDoOokXuKHFqQj731MxMj6ah99FBTvr+FNHIj0TpmmQKUBU 8oufr1E2tUiRwcGD9jGOd7RUJ9Yg4haSeEF5fJfyfIiEa2Vfd+FwuZ31xkC1pf0r un456DMT+TwcoCjgGabD5WrRlrIWwcBanwpktiGuDb0B42SZjo6Hb0UxIuialtis l/bF1HdTQU8uNwA85M2zKWW8nELQxtp8+sebkOJW+hocNuGNMa8R+2rLXrrKnaIY JcWSXHEKbNSVFHohEDHJlC8OTHqhOWf1dqBg23po9PJGcNGmRXnsW1s5I3LMVIuV +wy8boQZHY/3OYeHmVfvHpnR1XIz1uOGEyX3jl/pvrlmNnkfF8ZWewcpSarORwSa PGulUbsVbG6gXtn4A0cgf2XsbnlB5id1IGzbmeVO/7NMSbqrvucOeHTJWWMNItEk 6DNnU4k8i0LI/qXSld9d67nqEaYPQlesENASnhAAekMNC6qwrn0CeK/Hw6kKv/y7 TsMMsC8bSVCUgHDu1s3GeJ/ziAbmAebw3mdsO6r9HDszlhpqU4htJWmZG8PT9XKy f5RoIM039FZFF34T4dZQTJ6AdgIRjtTE/ViOBoQxRd6sHlsBQjX2i2NqHwIifGpS y9l4F/AzkpCCxxdey2ueupU9EEGWTifIFH3L0nwKbtDU/PUbSUx0wQTfD4PoB3ll 1EI+jJGvFqkvlPmKBroMbo6ckd7j7PZba7koiDV4QA2ALDHUruSM8rbVGbF4fplg a29WU16BNWPIjrjVYwKBh0571jl8tleun63tvRQ3Cma+QOFJtyYaX01LqYed48od 19o/XiAarErs077YcrBsiNASBLILTr0QR87oLDNQWLbJghRUCUGV5TVifUnAr2tO -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/client-expired.crt000066400000000000000000000107151450213760600210640ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 4 (0x4) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Validity Not Before: Aug 20 00:00:00 2012 GMT Not After : Aug 21 00:00:00 2012 GMT Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client expired Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:d0:5a:fb:75:01:67:ef:ce:6a:66:1b:44:11:68: 0d:49:d1:8e:68:39:f3:a8:71:38:c3:1f:7b:ff:77: b7:0f:2e:e8:87:db:be:48:5c:12:6f:ba:fd:3c:22: ec:7b:dd:2f:47:42:c5:db:9a:1b:8e:c3:9e:3e:c1: 59:53:19:69:7d:37:f8:70:75:b4:eb:28:09:4e:88: dd:b1:0f:21:fc:4b:33:98:2a:9a:e6:ed:8d:2a:7b: b4:b7:c9:53:28:c8:76:69:35:f2:2e:3d:31:2c:4b: 51:f9:2c:73:b9:ab:26:01:7e:8c:ef:7f:33:ee:99: ca:ad:61:9f:60:3d:ac:11:c8:09:7b:fd:31:dd:7e: 7e:d8:68:69:49:a8:2e:29:f0:9f:61:24:b7:63:5d: 98:93:73:96:7e:6f:a6:c2:3b:05:05:9c:82:eb:87: dd:f4:56:02:c2:ef:1e:34:02:0a:9c:9d:9a:7e:0c: 67:9b:91:74:a0:6e:5e:f6:52:5b:f7:f3:b7:0e:fe: 4e:0e:10:e4:fa:dc:4b:91:62:b4:49:42:6d:ea:84: 87:4a:89:8e:0d:8a:42:f2:6c:82:a1:93:bd:4d:a2: f0:ad:0f:ba:f7:6f:60:b7:7a:1b:9a:b5:e6:41:92: d7:3b:37:c6:79:b1:70:9a:6b:35:6a:42:3a:f1:20: 90:b7 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 74:67:BC:4D:15:27:BF:FF:E6:FF:20:B2:FE:9E:D5:B4:A2:57:D1:78 X509v3 Authority Key Identifier: keyid:AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 Signature Algorithm: sha256WithRSAEncryption 62:fd:1a:03:bc:1a:45:d0:ac:c1:4c:61:08:d0:df:d4:3f:8e: 85:f5:6c:ca:f0:ac:75:f9:56:54:f2:e2:17:95:e2:40:be:b3: cf:6e:c9:ff:db:12:cb:cd:c9:21:9a:79:35:45:2b:98:e7:38: 4d:a8:f1:39:db:5f:e4:fd:7e:f3:da:24:77:86:3a:1c:ab:f1: 60:af:33:ed:5b:7a:e8:cb:18:0c:7c:d7:32:af:50:8b:9d:74: 53:38:2f:31:9b:16:fc:99:c5:36:b7:4a:bd:96:38:27:96:b2: ba:9b:87:8e:48:2a:d7:3d:40:00:8f:54:8e:00:c7:8d:b8:97: 0c:3d:d5:67:5c:31:8d:ac:40:7a:27:86:a7:88:ac:90:ac:eb: ae:e3:35:dd:b2:03:ae:8b:c0:9d:a3:32:ac:d7:39:f2:b7:d2: f8:d2:f8:76:4d:77:cf:df:fd:e6:d6:7c:df:67:3d:21:01:e2: 45:d2:59:47:e6:f6:08:99:13:7b:ac:f9:ec:51:0d:8e:68:83: 7c:82:84:3d:03:24:dd:db:4c:8d:5a:44:8f:93:de:ea:14:b5: 8e:e4:65:02:a4:98:4a:20:eb:07:01:b0:80:9e:2c:52:00:c8: f2:9e:60:8e:72:67:57:97:44:7f:65:f7:2b:19:95:e6:c3:38: 80:39:66:cd -----BEGIN CERTIFICATE----- MIID3DCCAsSgAwIBAgIBBDANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx EDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMTIwODIw MDAwMDAwWhcNMTIwODIxMDAwMDAwWjCBgDELMAkGA1UEBhMCR0IxGDAWBgNVBAgM D05vdHRpbmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwG U2VydmVyMRMwEQYDVQQLDApQcm9kdWN0aW9uMRwwGgYDVQQDDBN0ZXN0IGNsaWVu dCBleHBpcmVkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0Fr7dQFn 785qZhtEEWgNSdGOaDnzqHE4wx97/3e3Dy7oh9u+SFwSb7r9PCLse90vR0LF25ob jsOePsFZUxlpfTf4cHW06ygJTojdsQ8h/EszmCqa5u2NKnu0t8lTKMh2aTXyLj0x LEtR+SxzuasmAX6M738z7pnKrWGfYD2sEcgJe/0x3X5+2GhpSaguKfCfYSS3Y12Y k3OWfm+mwjsFBZyC64fd9FYCwu8eNAIKnJ2afgxnm5F0oG5e9lJb9/O3Dv5ODhDk +txLkWK0SUJt6oSHSomODYpC8myCoZO9TaLwrQ+6929gt3obmrXmQZLXOzfGebFw mms1akI68SCQtwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1P cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUdGe8TRUnv//m /yCy/p7VtKJX0XgwHwYDVR0jBBgwFoAUqlpbHJEymz+bw0Js0mj2p+DPvuAwDQYJ KoZIhvcNAQELBQADggEBAGL9GgO8GkXQrMFMYQjQ39Q/joX1bMrwrHX5VlTy4heV 4kC+s89uyf/bEsvNySGaeTVFK5jnOE2o8TnbX+T9fvPaJHeGOhyr8WCvM+1beujL GAx81zKvUIuddFM4LzGbFvyZxTa3Sr2WOCeWsrqbh45IKtc9QACPVI4Ax424lww9 1WdcMY2sQHonhqeIrJCs667jNd2yA66LwJ2jMqzXOfK30vjS+HZNd8/f/ebWfN9n PSEB4kXSWUfm9giZE3us+exRDY5og3yChD0DJN3bTI1aRI+T3uoUtY7kZQKkmEog 6wcBsICeLFIAyPKeYI5yZ1eXRH9l9ysZlebDOIA5Zs0= -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/client-expired.key000066400000000000000000000032131450213760600210570ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEA0Fr7dQFn785qZhtEEWgNSdGOaDnzqHE4wx97/3e3Dy7oh9u+ SFwSb7r9PCLse90vR0LF25objsOePsFZUxlpfTf4cHW06ygJTojdsQ8h/EszmCqa 5u2NKnu0t8lTKMh2aTXyLj0xLEtR+SxzuasmAX6M738z7pnKrWGfYD2sEcgJe/0x 3X5+2GhpSaguKfCfYSS3Y12Yk3OWfm+mwjsFBZyC64fd9FYCwu8eNAIKnJ2afgxn m5F0oG5e9lJb9/O3Dv5ODhDk+txLkWK0SUJt6oSHSomODYpC8myCoZO9TaLwrQ+6 929gt3obmrXmQZLXOzfGebFwmms1akI68SCQtwIDAQABAoIBAHz1ZAQbcMOA740H Yz5xQi74kEjwILLwHJPhqRNhMBfaETmRz8BEAAakhcXwSBZNZFJ/uHxpI4fuyFRo z3KoNf0UeVqxLW0vWM2SBitvoPlX/LyRKM/Avr4w7QSgqNA30dRtty6GIpynG6Wu REWhYKzawhnNF09NSyHK/7PPqQgL8+3et4naoyTBfHryA1rSlAZmOAH9xWhbfTzH mWUazRp0kRQWQ/HCwQF4ZvXpE52K6U3OWhei440tJEPZDmrdKNrgFwxrBCUBWHuV ZP9N/SST52uzJS2t5oQ89jb8aH3zBz0bwwWNQgMBo1MV73JCsrO3RMyaWWSXNGCE fJ99JKkCgYEA+C/S61CAEd7eLBguQrY6EkyROf+SMVxD5DjXShpk/SsSuXY76Wu2 tpY0dNOjIGk1DIPmGdRtMcOg1RYDFcPF67lj6gwBgXk2qREUtdIzQ/X+3nVCaTCU /JuXQHTVnWYuYqT8Nd+IytKc4LrFWNpPDar0a6DSvd3QWQ95Tyg/f2UCgYEA1uom myPZxQRmXOwvm+u5ogA1NzCGOxMzpV/Ay2ahqC4as5AsnsL5xgIwYmGCpITYelQp 8FcQLgvHIHKZjHSSH/qxUvJelcthHp3WDoj2yeiH88AI56xN4pbSYHEZzq6/jyhN qykopsLpz67Qz2tn/on3DmjSrMcPqV1nblkHs+sCgYBnlM4al8ZbrwBattzXyuYB rSMPabLCFxfesDpqGwn6/3cZIFdw3ButqJLMD2gNptsVFhd6wEWyd0swo7c15jc8 Ymtoywn113kQpqhWGhx7SLfOcHH/JN+JbgZ6SEi/IF5LnUAF2/1jaPNAd7LVmodT 1P2dzckmpOTHxsWCW/HkYQKBgGn7uKQjLt5gyBYlB2lt+vJwBc48qMVzN7HjIZFt AGWOru5EOCzm3AQQykmJ6sI1HQhefvweA0Wh20YeHajNR85rc40DJy/ZxwAxOAGc +48glALZfcq6BwKp+/9BZ0esl50CdCLnPjvWvTUE8caIIhW9dc9uVA6OcCPGgx5A 23KXAoGAB1+qBe3rZZ7ke5PFNstASj4PqcwmZRxu7P7Xy6ziHsLTKvyx665IWd+F fdrFYWWP5tj+7CGzvLtD0nruAdGyECtmSCclH3iCmjjAWHQiqiZT4ShGzprkQj9d bsuhZ95e3mK7liSVujXl5neFWetyoj4MqA1OWjiNaEEUQ02NOlo= -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/client-revoked.crt000066400000000000000000000107151450213760600210630ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 5 (0x5) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Validity Not Before: Feb 25 14:54:19 2020 GMT Not After : Feb 23 14:54:19 2025 GMT Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client revoked Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:d3:04:65:0d:da:e3:e6:66:d2:cc:40:05:24:fe: 17:67:40:bb:6c:35:dd:18:2c:70:7e:20:d1:00:26: 56:b7:1b:4e:e6:3e:8c:6c:0d:e4:d2:c0:dd:71:30: 02:f7:a0:83:79:0f:15:94:cd:a3:aa:c7:d4:e3:15: af:0a:1a:b3:b8:54:b8:eb:f3:6b:72:3b:d3:f4:0b: c6:4c:a9:79:58:95:53:a3:4a:31:81:97:31:a1:67: f7:4d:9c:8b:02:b2:8d:79:b2:b1:87:3f:35:75:7e: d1:04:6b:fb:7f:44:d7:3c:c2:4b:73:99:ee:61:a0: 54:2f:47:a4:62:e3:e6:0c:bb:1a:88:8c:a1:94:8f: b5:79:d5:bb:be:75:f8:a7:e1:56:8b:dc:0f:90:9b: 94:45:50:fd:0b:7c:a9:bf:17:5a:0a:02:b4:15:3c: 88:fa:93:5f:1b:20:8a:c3:aa:c8:18:d1:02:27:38: 34:38:8c:ed:f7:58:50:20:53:9e:29:5e:3c:e9:f6: cc:98:37:1c:e4:24:7f:2f:44:39:42:31:7c:30:13: 0f:42:2c:c8:e1:53:ac:05:4b:e3:bd:7b:05:7a:d3: c1:14:ee:c3:06:75:64:5d:11:a6:be:4d:53:8d:06: 04:1f:1e:14:a7:8b:53:b9:aa:c3:97:c9:3c:8a:45: 6b:05 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 4B:8F:CC:F4:64:26:0C:0A:37:2B:D1:18:76:9D:AF:B7:D6:19:47:92 X509v3 Authority Key Identifier: keyid:AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 Signature Algorithm: sha256WithRSAEncryption 13:0e:0b:71:b7:f3:53:f9:4e:7b:19:20:89:4e:a2:bf:e3:a2: 5d:66:35:ba:02:ca:b5:b7:39:3f:4f:5b:47:b9:7a:14:c6:83: 28:02:2a:fe:68:56:d1:08:8d:e1:a0:c0:8b:8a:38:92:41:ba: 79:11:d4:df:8b:f5:1a:bd:ae:59:97:41:8c:4c:de:28:87:ce: fb:9e:ad:fb:22:48:d4:3d:9c:60:96:e5:35:71:b9:bc:24:ec: 11:e5:c8:96:1c:b1:ec:96:26:32:91:ef:a9:d9:d9:b8:3f:92: 9e:61:54:d7:b5:2d:f5:ac:89:4a:49:3e:8f:a9:b7:e2:39:7d: 98:5f:21:25:0c:71:16:e7:12:d5:e5:9c:01:6b:a8:50:65:ab: 48:db:a8:04:c1:ec:3e:ea:2f:54:30:f8:38:0c:90:fc:71:68: 56:98:a9:d4:b7:0e:bb:66:a9:fc:24:50:0b:b9:46:cf:45:56: 86:0c:7d:b9:e2:9b:ec:36:e4:c9:fd:96:a6:b0:f7:f3:c9:d4: 74:8e:6a:68:5a:2e:6d:6f:78:26:af:93:7d:9c:53:73:92:b5: 1d:c1:77:52:ea:2d:21:06:b6:3a:71:be:59:c9:51:a8:fe:89: fc:6c:e3:7e:5e:46:93:4c:eb:4f:14:1d:e8:05:99:95:7c:49: 40:c9:db:81 -----BEGIN CERTIFICATE----- MIID3DCCAsSgAwIBAgIBBTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx EDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjAwMjI1 MTQ1NDE5WhcNMjUwMjIzMTQ1NDE5WjCBgDELMAkGA1UEBhMCR0IxGDAWBgNVBAgM D05vdHRpbmdoYW1zaGlyZTETMBEGA1UEBwwKTm90dGluZ2hhbTEPMA0GA1UECgwG U2VydmVyMRMwEQYDVQQLDApQcm9kdWN0aW9uMRwwGgYDVQQDDBN0ZXN0IGNsaWVu dCByZXZva2VkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA0wRlDdrj 5mbSzEAFJP4XZ0C7bDXdGCxwfiDRACZWtxtO5j6MbA3k0sDdcTAC96CDeQ8VlM2j qsfU4xWvChqzuFS46/NrcjvT9AvGTKl5WJVTo0oxgZcxoWf3TZyLArKNebKxhz81 dX7RBGv7f0TXPMJLc5nuYaBUL0ekYuPmDLsaiIyhlI+1edW7vnX4p+FWi9wPkJuU RVD9C3ypvxdaCgK0FTyI+pNfGyCKw6rIGNECJzg0OIzt91hQIFOeKV486fbMmDcc 5CR/L0Q5QjF8MBMPQizI4VOsBUvjvXsFetPBFO7DBnVkXRGmvk1TjQYEHx4Up4tT uarDl8k8ikVrBQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1P cGVuU1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUS4/M9GQmDAo3 K9EYdp2vt9YZR5IwHwYDVR0jBBgwFoAUqlpbHJEymz+bw0Js0mj2p+DPvuAwDQYJ KoZIhvcNAQELBQADggEBABMOC3G381P5TnsZIIlOor/jol1mNboCyrW3OT9PW0e5 ehTGgygCKv5oVtEIjeGgwIuKOJJBunkR1N+L9Rq9rlmXQYxM3iiHzvuerfsiSNQ9 nGCW5TVxubwk7BHlyJYcseyWJjKR76nZ2bg/kp5hVNe1LfWsiUpJPo+pt+I5fZhf ISUMcRbnEtXlnAFrqFBlq0jbqATB7D7qL1Qw+DgMkPxxaFaYqdS3DrtmqfwkUAu5 Rs9FVoYMfbnim+w25Mn9lqaw9/PJ1HSOamhaLm1veCavk32cU3OStR3Bd1LqLSEG tjpxvlnJUaj+ifxs435eRpNM608UHegFmZV8SUDJ24E= -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/client-revoked.key000066400000000000000000000032131450213760600210560ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEA0wRlDdrj5mbSzEAFJP4XZ0C7bDXdGCxwfiDRACZWtxtO5j6M bA3k0sDdcTAC96CDeQ8VlM2jqsfU4xWvChqzuFS46/NrcjvT9AvGTKl5WJVTo0ox gZcxoWf3TZyLArKNebKxhz81dX7RBGv7f0TXPMJLc5nuYaBUL0ekYuPmDLsaiIyh lI+1edW7vnX4p+FWi9wPkJuURVD9C3ypvxdaCgK0FTyI+pNfGyCKw6rIGNECJzg0 OIzt91hQIFOeKV486fbMmDcc5CR/L0Q5QjF8MBMPQizI4VOsBUvjvXsFetPBFO7D BnVkXRGmvk1TjQYEHx4Up4tTuarDl8k8ikVrBQIDAQABAoIBAC8u5mGothjImQ3u qrfQ0O7XfJD/okZLeYPaVqFP8UfUJVo6Vi+7E5VEZr9uWtt/2qXxB4RUTupa8HEu YgtCWTk4SHkJ3taWJhiFoXt20ZlLGn6CkntFkWVj19pUzIh34EZ7/FIfghaZmqcA diXJAM+nKjPZEYJm1SwVOt6Z0tC5hRW9R3eCf8dxC6YlPJv9sebi6C9AqKVe6IsG 98CNFJG7hdgdsMFCzjDfasHreXGZ9w/pG9O+szAloqyM6k8R/3gT1Cx8Qwh6KIMt xL1KZyBw3dm7rCBxtozZC8nRzGFNoHWYS44wFxYaN11Y96h+ACLJYD7q6oOL4Oz9 Kh8hn+kCgYEA/6X8WDbjEv9akZurMa3AOOAsAHb+K/kSyc3pV3C3RMxR2y4z6W4x 4ZiJePpwsFZR0Ss/wXPnVBo1CEAJxHrBIb5zrzQwYIPFrNL7lPO4oalN0+Xpvo5r GIP354Yh1Eb98eLotBpcppI2MiE6z++lvUJ8HGcIuOMNeFEmIRl1E18CgYEA006x vMGrzv36ZPPsTkaAU8gLVinqseEggNd90fuOAi9UQ/9k/MjeTYtL/c/3VcIWgHA1 4Emvt5UUI0Gr3/it+O2cr5snIglrsnAFADaWrzjSMHLpLdHzUfMHCbAJLYO9vYec l2vpIcASvR7C1cQzFuBhNZC13uRWtV8O4BvPoBsCgYA/+Tl8mb+ZMW1oopvkgqZn lTFtrFlOh7W76ltKFlrGTJrvTlCPSZQR8Cn2rDUm63Lt9PSvZGGvGh/LQLsw/8b0 usQYQ+cXP+JnrSRn0dWSHtvq+s3TcbA7IksXFOnCRUGnFjFFYJNu24fUY5xkDIRd KBYGxYHZQnhMc4InJke14QKBgQCXSb6DkH48IydFZEcJ+/whABbtx/EbDj5BQQf6 cYrJEa3ZSV+6hO50wojT3jQNmHqX1r8cKGXAoOHcJEa0gs28bhNCv2kTO396MC3E a1ETfzEuMve0MJ9vSvr8+qZ3id0td4yr9TzjRyujcAS7HFAfzuKKgWNVhFJ4ZOi3 l/HdhQKBgE7V2j71No6xkjj9jOguI3TpCFXqQoSa9KhWOVXJrbpfE0K2QxvfQ6WO usWDGw9102OdFqfaFf2LE1cbIQe9M35QRrT9AjsZxtoc60XpZEwYS+FFh7po/t2t Apb8jiyBBAI9aYNRlWVo/1emnG6QUR45gX0Bu7glr+P4A+aopBi2 -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/client.crt000066400000000000000000000106711450213760600174270ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 3 (0x3) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Validity Not Before: Feb 25 14:54:19 2020 GMT Not After : Feb 23 14:54:19 2025 GMT Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=test client Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:bf:6a:a5:cd:66:6a:1d:20:48:3a:f1:ff:61:9d: fd:1f:18:26:a0:43:4c:b0:4c:b9:8d:4e:7d:d0:81: e0:43:81:9e:70:75:cb:c4:57:49:3c:84:34:51:45: a2:9f:00:50:20:d6:5f:34:3c:02:bb:69:2f:64:4a: 28:21:e3:95:41:e8:50:04:f3:bf:f2:5a:9e:27:64: 5b:b3:bc:49:96:36:10:56:06:47:1a:ca:db:ad:6f: e3:f7:83:dc:42:37:28:07:58:a7:6f:26:45:b7:69: 6f:af:28:62:f8:7e:98:98:21:0a:a6:da:ae:d5:4b: fe:db:09:1a:b4:75:d5:09:3b:13:9e:33:9e:b4:d6: 5e:21:e6:fb:37:09:bb:1a:56:e1:5d:64:bc:5a:77: 99:ac:81:cc:2b:b7:9b:49:b6:e8:ba:2e:32:d9:9e: 8e:4d:2e:fc:17:d0:37:44:0f:35:a9:af:f1:44:bb: cb:2c:2a:75:f0:7e:ba:b6:1a:73:32:d6:1f:4c:3b: 9b:38:f0:e9:22:06:3c:94:6b:5f:69:e4:be:2f:fa: 9c:c4:9e:7c:c0:dd:c2:3c:53:b8:28:ca:77:a0:96: 6b:9c:cb:3f:44:b1:c5:51:75:89:a0:16:ba:82:63: f7:c4:24:7f:06:89:58:45:10:69:e4:97:f5:35:fe: 7e:97 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 36:51:2B:BB:3E:B7:6B:2B:7F:D4:86:AE:67:75:C2:01:54:5B:72:3C X509v3 Authority Key Identifier: keyid:AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 Signature Algorithm: sha256WithRSAEncryption 32:70:51:f0:c2:35:6f:83:e3:bc:f3:f2:6f:e9:79:e6:a9:51: d0:69:fa:fb:a8:a8:d4:59:c4:c4:ee:9b:59:9a:ce:a8:2e:7e: 71:a5:23:4c:27:76:e5:b6:e1:6d:bd:a4:24:f1:38:01:7d:d8: d6:f2:c4:d8:58:b5:59:0d:b9:05:45:62:59:34:54:56:49:c4: 2c:f4:bd:17:a0:f2:72:e3:63:c3:69:40:55:e8:a4:57:23:38: e5:5e:f2:b0:3a:ee:27:b4:0e:ca:5e:a9:55:60:db:4d:30:ad: c5:13:d3:a4:ed:49:ff:c3:4a:e5:82:9d:5d:c6:ad:62:d9:49: 90:d1:0f:5e:89:1b:d7:f3:c1:3c:45:dc:84:09:b2:77:c2:fe: 47:9d:90:d4:f1:6c:54:20:a9:0d:9e:f8:a4:b9:55:c9:22:ef: 30:d1:d2:59:ba:ae:c1:d2:60:44:83:7f:0a:eb:36:ed:e2:0e: 7c:67:b3:c2:0d:25:bd:75:36:d8:af:ad:62:f8:f4:80:8f:ae: ec:e7:1c:a6:1f:f5:ff:8e:8b:c8:28:03:d3:de:08:4e:26:e1: 61:ce:3d:24:93:9b:da:d7:f7:8e:15:5d:32:55:5e:c9:7f:6b: 0d:a8:f7:b2:73:85:2a:63:25:93:37:14:ce:64:cc:f6:07:a1: dc:29:f6:53 -----BEGIN CERTIFICATE----- MIID0zCCArugAwIBAgIBAzANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx EDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjAwMjI1 MTQ1NDE5WhcNMjUwMjIzMTQ1NDE5WjB4MQswCQYDVQQGEwJHQjEYMBYGA1UECAwP Tm90dGluZ2hhbXNoaXJlMRMwEQYDVQQHDApOb3R0aW5naGFtMQ8wDQYDVQQKDAZT ZXJ2ZXIxEzARBgNVBAsMClByb2R1Y3Rpb24xFDASBgNVBAMMC3Rlc3QgY2xpZW50 MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAv2qlzWZqHSBIOvH/YZ39 HxgmoENMsEy5jU590IHgQ4GecHXLxFdJPIQ0UUWinwBQINZfNDwCu2kvZEooIeOV QehQBPO/8lqeJ2Rbs7xJljYQVgZHGsrbrW/j94PcQjcoB1inbyZFt2lvryhi+H6Y mCEKptqu1Uv+2wkatHXVCTsTnjOetNZeIeb7Nwm7GlbhXWS8WneZrIHMK7ebSbbo ui4y2Z6OTS78F9A3RA81qa/xRLvLLCp18H66thpzMtYfTDubOPDpIgY8lGtfaeS+ L/qcxJ58wN3CPFO4KMp3oJZrnMs/RLHFUXWJoBa6gmP3xCR/BolYRRBp5Jf1Nf5+ lwIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVuU1NMIEdl bmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUNlEruz63ayt/1IauZ3XCAVRb cjwwHwYDVR0jBBgwFoAUqlpbHJEymz+bw0Js0mj2p+DPvuAwDQYJKoZIhvcNAQEL BQADggEBADJwUfDCNW+D47zz8m/peeapUdBp+vuoqNRZxMTum1mazqgufnGlI0wn duW24W29pCTxOAF92NbyxNhYtVkNuQVFYlk0VFZJxCz0vReg8nLjY8NpQFXopFcj OOVe8rA67ie0DspeqVVg200wrcUT06TtSf/DSuWCnV3GrWLZSZDRD16JG9fzwTxF 3IQJsnfC/kedkNTxbFQgqQ2e+KS5Vcki7zDR0lm6rsHSYESDfwrrNu3iDnxns8IN Jb11NtivrWL49ICPruznHKYf9f+Oi8goA9PeCE4m4WHOPSSTm9rX944VXTJVXsl/ aw2o97JzhSpjJZM3FM5kzPYHodwp9lM= -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/client.key000066400000000000000000000032171450213760600174250ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEAv2qlzWZqHSBIOvH/YZ39HxgmoENMsEy5jU590IHgQ4GecHXL xFdJPIQ0UUWinwBQINZfNDwCu2kvZEooIeOVQehQBPO/8lqeJ2Rbs7xJljYQVgZH GsrbrW/j94PcQjcoB1inbyZFt2lvryhi+H6YmCEKptqu1Uv+2wkatHXVCTsTnjOe tNZeIeb7Nwm7GlbhXWS8WneZrIHMK7ebSbboui4y2Z6OTS78F9A3RA81qa/xRLvL LCp18H66thpzMtYfTDubOPDpIgY8lGtfaeS+L/qcxJ58wN3CPFO4KMp3oJZrnMs/ RLHFUXWJoBa6gmP3xCR/BolYRRBp5Jf1Nf5+lwIDAQABAoIBAQCxu6DAG3wkFzl6 IgFy7nN9T7tty4+Fk3gm0N7Zn/5QMCahXX8ai8Ggw1CgtfvNj0jXdLVplt8ijQRI JuMktGB+lerW7k0oByQah4DuXsIlC4YXmjSjmABqBh6yUGlPwk8Uoyi0d+D78JaX GPTsrv+ZIfT2AM+dlbbKQqXdMhvhOLHn+p8+j5iX4UMt5UY3wbVH9gu4nTSG2Sce +OKRbOynCleYtlhpkVx2J2f3szxz/FBGjywX/9EMmJvOg3uvJ74lxYEB3e5RQdnZ FAxRlh+S54u1AjqFjUjLtWFgYjrQfmwtEA+GYGDwQK8CZJKEN8ARkNl4SzNu+qj0 mDfWIzjBAoGBAOhhBCY/k6rwa6RjwArEX4KpTkAISo3esBC8IF7rIDWrvK0c+hqY QO8Zdt2vnJNO2e94c7WXLZ90FGan2vD7E9ICEU4fC2Rk3xdMUSpTFToKRvWvD7Z1 RhOYkOLz904t391UdQrhT3DnIZ+05XbheR9GJWS2SImbCqjlzI3aFTOfAoGBANLf tIwf6k676mOigJ0ueNI3/Hz3UbnJFWeqwclgQbpCQpzLeexyKrkTZqslxnge29hb JrQrem97WSVC8EqXU3PI/0chRYUUL2UdGX6nKlBL+MDLbGoUkRNqrBxhUajBT2Yq 3tONIlHsFFd843dAr90DZxoj8WYA4NnaW4xR0ZIJAoGAeBWiek2CduSVC7eMh0Ph g3kQeeCO/m9kltFQ/RwOYg3ki6Uczd9+NtD27yqQBEPMNYcObHm6Vts6q630Y3Gs VWtCHBfI4FGMQ9LpYrDamEq1TsLvoL9LvlaqEM44L4tfU1YQwdWbIuIeKxTlO6Da 4cFzE29rXsjjIlLWeTuIl0sCgYEA0Mak5VqvyzXnUK/RsE0TV+YQN9VQ96SraZC4 /dwsFvGFK+GUm9FIlSYtLuNehQzgUmukfRrSxE8WKnsEloUOHYNxooXBY4lhhzVx SWDN1uPwq0h71Ob534RsVEjR5UdGifuF02NXCE64sQm31xiXRTUaPdo6JOhXtbin jNxwpakCgYEAoGkfZxUd6yZtNLmEKUwFT4/1edMg9XdyZggbfkaM2acn6cCk/XdJ zZSDcvnNyui30Amuiiin40QWX0OizeTnUS73knPTitQIb0+YEXp6NTKKsc1Frn6w pGjTOoVH4PDwBGs7vSO8uip6gdeGI1bCAK5zKeWR5gHE/UCrP/WQXy8= -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/crl-empty.pem000066400000000000000000000012261450213760600200520ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIBwDCBqQIBATANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjETMBEGA1UE CAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNV BAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EXDTIwMDIyNTE0NTQxOVoY DzIxMDIwNDE2MTQ1NDE5WqAOMAwwCgYDVR0UBAMCAQEwDQYJKoZIhvcNAQELBQAD ggEBADXrRSpMfj+Iuz/Uy/ti4k0Qx+H/e93pown8Cgx/w9FwtsTsaTKOff0r3uKb KpKJJ4BSkysUOaZ72cLoooNYoEcYgpcqx3PlhmjuBGcOH1YG5ca+nzIayZgQe3Nl hGBvYfpX+YMpG7gHy5WPxi0T+uUF7XfTEfpmw8asVSZvZNy0nMB3cZCCA4yiICay vaOIrrHshSlDPw6iafhcBNLAdq5Xz+KF4Pv78Wfs+zwnm0BzRGtVB7cWCaGvUi3v dAqzzBsdP0naFYVaZ1BJcE06Dn5O6LSA6snOswCTGOYI50zMZzRXkUo3pZ/xqVPc Cdo6QspVlxGedSxXD13KbGPAoak= -----END X509 CRL----- mosquitto-2.0.18/test/ssl/crl.pem000066400000000000000000000012621450213760600167160ustar00rootroot00000000000000-----BEGIN X509 CRL----- MIIB1jCBvwIBATANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjETMBEGA1UE CAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNV BAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EXDTIwMDIyNTE0NTQxOVoY DzIxMDIwNDE2MTQ1NDE5WjAUMBICAQUXDTIwMDIyNTE0NTQxOVqgDjAMMAoGA1Ud FAQDAgECMA0GCSqGSIb3DQEBCwUAA4IBAQCMM5PyBAY5BuNVk0k2Bqn5FvlIrSnS LMZaoUVG/OtgjMD6g47dSXVHgIXmuFu3Bp44mRM85ZVd1URjmjR4ZwfVfcprkqo7 L655K+nyPUoq5IZh7y4MKVYwbEfetu0HjWuOqFI9T7zalOF9MfeoOx6u93CTgUvy 1s5EVnG0d0qon3CEHTJwpzYQDgXVesUX0ZqNwvKnMGQhB8YQ/NOX807xQR5Ckl7s 6CYkAySe84lMascnwe1nFp3nGIxbOTxXqohWkvscM6933+veisgh6F4p63oF4rKs Xr93Bf9FsvwfitI/PfMWkKzFEEaZTjAM26ioLgBBcBxxIJleLysyudd2 -----END X509 CRL----- mosquitto-2.0.18/test/ssl/gen.sh000077500000000000000000000111241450213760600165410ustar00rootroot00000000000000# This file generates the keys and certificates used for testing mosquitto. # None of the keys are encrypted, so do not just use this script to generate # files for your own use. rm -f *.crt *.key *.csr for a in root signing; do rm -rf ${a}CA/ mkdir -p ${a}CA/newcerts touch ${a}CA/index.txt echo 01 > ${a}CA/serial echo 01 > ${a}CA/crlnumber done rm -rf certs BASESUBJ="/C=GB/ST=Derbyshire/L=Derby/O=Mosquitto Project/OU=Testing" SBASESUBJ="/C=GB/ST=Nottinghamshire/L=Nottingham/O=Server/OU=Production" BBASESUBJ="/C=GB/ST=Nottinghamshire/L=Nottingham/O=Server/OU=Bridge" # The root CA openssl genrsa -out test-root-ca.key 2048 openssl req -new -x509 -days 3650 -key test-root-ca.key -out test-root-ca.crt -config openssl.cnf -subj "${BASESUBJ}/CN=Root CA/" # Another root CA that doesn't sign anything openssl genrsa -out test-bad-root-ca.key 2048 openssl 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/" # 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. openssl genrsa -out test-fake-root-ca.key 2048 openssl 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/" # An intermediate CA, signed by the root CA, used to sign server/client csrs. openssl genrsa -out test-signing-ca.key 2048 openssl req -out test-signing-ca.csr -key test-signing-ca.key -new -config openssl.cnf -subj "${BASESUBJ}/CN=Signing CA/" openssl ca -batch -config openssl.cnf -name CA_root -extensions v3_ca -out test-signing-ca.crt -infiles test-signing-ca.csr rm -f test-signing-ca.csr # An alternative intermediate CA, signed by the root CA, not used to sign anything. openssl genrsa -out test-alt-ca.key 2048 openssl req -out test-alt-ca.csr -key test-alt-ca.key -new -config openssl.cnf -subj "${BASESUBJ}/CN=Alternative Signing CA/" openssl ca -batch -config openssl.cnf -name CA_root -extensions v3_ca -out test-alt-ca.crt -infiles test-alt-ca.csr rm -f test-alt-ca.csr # Valid server key and certificate. openssl genrsa -out server.key 2048 openssl req -new -key server.key -out server.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=localhost/" openssl ca -batch -config openssl.cnf -name CA_signing -out server.crt -infiles server.csr rm -f server.csr # Expired server certificate openssl genrsa -out server-expired.key 2048 openssl req -new -key server-expired.key -out server-expired.csr -config openssl.cnf -subj "${SBASESUBJ}-expired/CN=localhost/" openssl ca -batch -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out server-expired.crt -infiles server-expired.csr rm -f server-expired.csr # Valid client key and certificate. openssl genrsa -out client.key 2048 openssl req -new -key client.key -out client.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client/" openssl ca -batch -config openssl.cnf -name CA_signing -out client.crt -infiles client.csr rm -f client.csr # Expired client certificate openssl genrsa -out client-expired.key 2048 openssl req -new -key client-expired.key -out client-expired.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client expired/" openssl ca -batch -config openssl.cnf -name CA_signing -days 1 -startdate 120820000000Z -enddate 120821000000Z -out client-expired.crt -infiles client-expired.csr rm -f client-expired.csr # Empty CRL file openssl ca -batch -config openssl.cnf -name CA_signing -gencrl -out crl-empty.pem # Revoked client certificate openssl genrsa -out client-revoked.key 2048 openssl req -new -key client-revoked.key -out client-revoked.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client revoked/" openssl ca -batch -config openssl.cnf -name CA_signing -out client-revoked.crt -infiles client-revoked.csr openssl ca -batch -config openssl.cnf -name CA_signing -revoke client-revoked.crt openssl ca -batch -config openssl.cnf -name CA_signing -gencrl -out crl.pem rm -f client-revoked.csr # Valid client key and certificate, encrypted (use "password" as password) openssl genrsa -des3 -out client-encrypted.key -passout pass:password 2048 openssl req -new -key client-encrypted.key -out client-encrypted.csr -config openssl.cnf -subj "${SBASESUBJ}/CN=test client encrypted/" -passin pass:password openssl ca -batch -config openssl.cnf -name CA_signing -out client-encrypted.crt -infiles client-encrypted.csr rm -f client-encrypted.csr cat test-signing-ca.crt test-root-ca.crt > all-ca.crt #mkdir certs #cp test-signing-ca.crt certs/test-signing-ca.pem #cp test-root-ca.crt certs/test-root.ca.pem #openssl rehash certs mosquitto-2.0.18/test/ssl/openssl.cnf000066400000000000000000000270661450213760600176200ustar00rootroot00000000000000# # OpenSSL example configuration file. # This is mostly being used for generation of certificate requests. # # This definition stops the following lines choking if HOME isn't # defined. HOME = . RANDFILE = $ENV::HOME/.rnd # Extra OBJECT IDENTIFIER info: #oid_file = $ENV::HOME/.oid oid_section = new_oids # To use this configuration file with the "-extfile" option of the # "openssl x509" utility, name here the section containing the # X.509v3 extensions to use: # extensions = # (Alternatively, use a configuration file that has only # X.509v3 extensions in its main [= default] section.) [ new_oids ] # We can add new OIDs in here for use by 'ca', 'req' and 'ts'. # Add a simple OID like this: # testoid1=1.2.3.4 # Or use config file substitution like this: # testoid2=${testoid1}.5.6 # Policies used by the TSA examples. tsa_policy1 = 1.2.3.4.1 tsa_policy2 = 1.2.3.4.5.6 tsa_policy3 = 1.2.3.4.5.7 #################################################################### [ ca ] default_ca = CA_default # The default ca section #################################################################### [ CA_signing ] dir = ./signingCA # Where everything is kept certs = $dir/certs # Where the issued certs are kept crl_dir = $dir/crl # Where the issued crl are kept database = $dir/index.txt # database index file. #unique_subject = no # Set to 'no' to allow creation of # several ctificates with same subject. new_certs_dir = $dir/newcerts # default place for new certs. certificate = test-signing-ca.crt # The CA certificate serial = $dir/serial # The current serial number crlnumber = $dir/crlnumber # the current crl number # must be commented out to leave a V1 CRL crl = $dir/crl.pem # The current CRL private_key = test-signing-ca.key # The private key RANDFILE = $dir/.rand # private random number file x509_extensions = usr_cert # The extentions to add to the cert # Comment out the following two lines for the "traditional" # (and highly broken) format. name_opt = ca_default # Subject Name options cert_opt = ca_default # Certificate field options # Extension copying option: use with caution. # copy_extensions = copy # Extensions to add to a CRL. Note: Netscape communicator chokes on V2 CRLs # so this is commented out by default to leave a V1 CRL. # crlnumber must also be commented out to leave a V1 CRL. # crl_extensions = crl_ext default_days = 1825 # how long to certify for default_crl_days= 30000 # how long before next CRL default_md = default # use public key default MD preserve = no # keep passed DN ordering # A few difference way of specifying how similar the request should look # For type CA, the listed attributes must be the same, and the optional # and supplied fields are just that :-) policy = policy_anything [ CA_inter ] dir = ./interCA certs = $dir/certs crl_dir = $dir/crl database = $dir/index.txt new_certs_dir = $dir/newcerts certificate = test-inter-ca.crt serial = $dir/serial crlnumber = $dir/crlnumber crl = $dir/crl.pem private_key = test-inter-ca.key RANDFILE = $dir/.rand #x509_extensions = v3_ca x509_extensions = usr_cert name_opt = ca_default cert_opt = ca_default default_days = 1825 default_crl_days = 30 default_md = default preserve = no policy = policy_match unique_subject = yes [ CA_root ] dir = ./rootCA certs = $dir/certs crl_dir = $dir/crl database = $dir/index.txt new_certs_dir = $dir/newcerts certificate = test-root-ca.crt serial = $dir/serial crlnumber = $dir/crlnumber crl = $dir/crl.pem private_key = test-root-ca.key RANDFILE = $dir/.rand x509_extensions = v3_ca name_opt = ca_default cert_opt = ca_default default_days = 1825 default_crl_days = 30 default_md = default preserve = no policy = policy_match unique_subject = yes # For the CA policy [ policy_match ] countryName = match stateOrProvinceName = match organizationName = match organizationalUnitName = optional commonName = supplied emailAddress = optional # For the 'anything' policy # At this point in time, you must list all acceptable 'object' # types. [ policy_anything ] countryName = optional stateOrProvinceName = optional localityName = optional organizationName = optional organizationalUnitName = optional commonName = supplied emailAddress = optional #################################################################### [ req ] default_bits = 2048 default_keyfile = privkey.pem distinguished_name = req_distinguished_name attributes = req_attributes x509_extensions = v3_ca # The extentions to add to the self signed cert # Passwords for private keys if not present they will be prompted for # input_password = secret # output_password = secret # This sets a mask for permitted string types. There are several options. # default: PrintableString, T61String, BMPString. # pkix : PrintableString, BMPString (PKIX recommendation before 2004) # utf8only: only UTF8Strings (PKIX recommendation after 2004). # nombstr : PrintableString, T61String (no BMPStrings or UTF8Strings). # MASK:XXXX a literal mask value. # WARNING: ancient versions of Netscape crash on BMPStrings or UTF8Strings. string_mask = utf8only # req_extensions = v3_req # The extensions to add to a certificate request [ req_distinguished_name ] countryName = Country Name (2 letter code) countryName_default = GB countryName_min = 2 countryName_max = 2 stateOrProvinceName = State or Province Name (full name) stateOrProvinceName_default = Derbyshire localityName = Locality Name (eg, city) localityName_default = Derby 0.organizationName = Organization Name (eg, company) 0.organizationName_default = Mosquitto Project # we can do this but it is not needed normally :-) #1.organizationName = Second Organization Name (eg, company) #1.organizationName_default = World Wide Web Pty Ltd organizationalUnitName = Organizational Unit Name (eg, section) organizationalUnitName_default = Testing commonName = Common Name (e.g. server FQDN or YOUR name) commonName_max = 64 emailAddress = Email Address emailAddress_max = 64 # SET-ex3 = SET extension number 3 [ req_attributes ] challengePassword = A challenge password challengePassword_min = 4 challengePassword_max = 20 unstructuredName = An optional company name [ usr_cert ] # These extensions are added when 'ca' signs a request. # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This is required for TSA certificates. # extendedKeyUsage = critical,timeStamping [ v3_req ] # Extensions to add to a certificate request basicConstraints = CA:FALSE keyUsage = nonRepudiation, digitalSignature, keyEncipherment [ v3_ca ] # Extensions for a typical CA # PKIX recommendation. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid:always,issuer # This is what PKIX recommends but some broken software chokes on critical # extensions. #basicConstraints = critical,CA:true # So we do this instead. basicConstraints = CA:true # Key usage: this is typical for a CA certificate. However since it will # prevent it being used as an test self-signed certificate it is best # left out by default. # keyUsage = cRLSign, keyCertSign # Some might want this also # nsCertType = sslCA, emailCA # Include email address in subject alt name: another PKIX recommendation # subjectAltName=email:copy # Copy issuer details # issuerAltName=issuer:copy # DER hex encoding of an extension: beware experts only! # obj=DER:02:03 # Where 'obj' is a standard or added object # You can even override a supported extension: # basicConstraints= critical, DER:30:03:01:01:FF [ crl_ext ] # CRL extensions. # Only issuerAltName and authorityKeyIdentifier make any sense in a CRL. # issuerAltName=issuer:copy authorityKeyIdentifier=keyid:always [ proxy_cert_ext ] # These extensions should be added when creating a proxy certificate # This goes against PKIX guidelines but some CAs do it and some software # requires this to avoid interpreting an end user certificate as a CA. basicConstraints=CA:FALSE # Here are some examples of the usage of nsCertType. If it is omitted # the certificate can be used for anything *except* object signing. # This is OK for an SSL server. # nsCertType = server # For an object signing certificate this would be used. # nsCertType = objsign # For normal client use this is typical # nsCertType = client, email # and for everything including object signing: # nsCertType = client, email, objsign # This is typical in keyUsage for a client certificate. # keyUsage = nonRepudiation, digitalSignature, keyEncipherment # This will be displayed in Netscape's comment listbox. nsComment = "OpenSSL Generated Certificate" # PKIX recommendations harmless if included in all certificates. subjectKeyIdentifier=hash authorityKeyIdentifier=keyid,issuer # This stuff is for subjectAltName and issuerAltname. # Import the email address. # subjectAltName=email:copy # An alternative to produce certificates that aren't # deprecated according to PKIX. # subjectAltName=email:move # Copy subject details # issuerAltName=issuer:copy #nsCaRevocationUrl = http://www.domain.dom/ca-crl.pem #nsBaseUrl #nsRevocationUrl #nsRenewalUrl #nsCaPolicyUrl #nsSslServerName # This really needs to be in place for it to be a proxy certificate. proxyCertInfo=critical,language:id-ppl-anyLanguage,pathlen:3,policy:foo #################################################################### [ tsa ] default_tsa = tsa_config1 # the default TSA section [ tsa_config1 ] # These are used by the TSA reply generation only. dir = ./demoCA # TSA root directory serial = $dir/tsaserial # The current serial number (mandatory) crypto_device = builtin # OpenSSL engine to use for signing signer_cert = $dir/tsacert.pem # The TSA signing certificate # (optional) certs = $dir/cacert.pem # Certificate chain to include in reply # (optional) signer_key = $dir/private/tsakey.pem # The TSA private key (optional) default_policy = tsa_policy1 # Policy if request did not specify it # (optional) other_policies = tsa_policy2, tsa_policy3 # acceptable policies (optional) digests = md5, sha1 # Acceptable message digests (mandatory) accuracy = secs:1, millisecs:500, microsecs:100 # (optional) clock_precision_digits = 0 # number of digits after dot. (optional) ordering = yes # Is ordering defined for timestamps? # (optional, default: no) tsa_name = yes # Must the TSA name be included in the reply? # (optional, default: no) ess_cert_id_chain = no # Must the ESS cert id chain be included? # (optional, default: no) mosquitto-2.0.18/test/ssl/readme.txt000066400000000000000000000001541450213760600174300ustar00rootroot00000000000000This directory contains certificates and keys required for SSL testing. The CA key has password "password". mosquitto-2.0.18/test/ssl/rootCA/000077500000000000000000000000001450213760600166215ustar00rootroot00000000000000mosquitto-2.0.18/test/ssl/rootCA/crlnumber000066400000000000000000000000031450213760600205260ustar00rootroot0000000000000001 mosquitto-2.0.18/test/ssl/rootCA/index.txt.attr000066400000000000000000000000251450213760600214370ustar00rootroot00000000000000unique_subject = yes mosquitto-2.0.18/test/ssl/rootCA/serial000066400000000000000000000000031450213760600200140ustar00rootroot0000000000000003 mosquitto-2.0.18/test/ssl/server-expired.crt000066400000000000000000000107071450213760600211150ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Validity Not Before: Aug 20 00:00:00 2012 GMT Not After : Aug 21 00:00:00 2012 GMT Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production-expired, CN=localhost Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:95:0d:f4:ee:f2:c8:f8:84:23:78:af:73:53:78: 78:95:fd:cd:a6:fd:fd:0f:c7:ee:1c:39:e7:3c:d5: 2b:ac:1d:ab:92:e9:8d:df:c1:15:37:40:3d:d1:b6: 96:78:ec:4e:63:54:53:14:d4:9d:bc:42:6d:d9:5d: 3b:ce:d4:d0:a5:f1:ef:32:5a:c7:63:1b:2c:01:a7: f4:9e:9a:39:95:c4:70:02:fa:8f:d2:d1:fc:0c:51: 4e:e1:91:54:88:ee:0d:c1:f0:6a:17:7d:05:9e:f2: 2e:b8:ed:49:b3:41:70:21:94:b4:02:22:bf:ff:79: 0d:fb:38:bb:a1:3d:c0:a9:60:5e:39:18:8e:07:48: 15:10:7b:b0:01:2b:2b:35:8c:67:be:85:70:cf:ba: 99:bc:a8:1d:50:3f:ac:d9:32:91:ea:59:c4:4a:7a: 72:5d:28:1e:43:5b:0b:b5:c0:d0:9d:ac:c5:68:c9: e5:ef:3e:cf:58:04:e6:99:4e:21:7c:c0:80:9d:88: f4:89:ca:d3:17:e1:77:fa:31:8c:7d:14:3e:af:e0: 16:f8:67:28:4b:18:bb:fd:c3:4a:64:1f:c7:26:3b: 0c:db:04:e7:11:35:13:99:ca:9c:25:87:48:e6:60: f2:a1:ef:7c:c2:5f:c3:02:ee:4c:27:32:da:20:76: 70:79 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 75:36:7F:77:C7:7D:8D:B8:2B:7C:7D:8B:D8:0C:AD:59:3C:B0:85:E6 X509v3 Authority Key Identifier: keyid:AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 Signature Algorithm: sha256WithRSAEncryption 74:c7:1f:42:e3:00:94:1a:16:ec:c9:17:02:1f:4f:e6:b0:4a: 4b:b1:2d:d2:3f:04:54:54:23:d1:b6:da:fa:fc:ac:3e:32:35: a9:68:6b:b7:bc:06:ee:58:d5:95:a5:48:56:cb:ea:9d:d3:5e: 68:ce:8f:65:60:40:42:a6:8d:c5:e4:33:d3:ef:ed:e4:fd:23: fe:28:34:ca:eb:2f:69:45:8e:61:dc:e2:0c:50:96:35:94:90: 25:61:55:d5:9c:d8:00:63:e0:6e:a1:67:f2:3f:34:a5:9d:33: 2a:7d:de:c0:89:8c:46:b1:fc:d4:19:7e:be:83:e0:f1:34:ff: 41:d3:cd:fb:e5:71:9d:05:00:67:af:f3:03:be:f6:e9:db:76: 58:89:72:68:7f:32:84:ff:c0:38:95:89:60:1b:99:fc:5e:37: 81:fa:ce:e7:78:7f:6c:3e:b9:70:74:62:62:d3:c2:8e:8e:2c: 11:fc:e6:fa:9a:cd:1e:79:67:51:01:54:1e:7d:db:32:09:13: 14:91:a3:56:2d:8e:fa:f8:3d:49:67:fe:b2:c8:11:8a:09:0e: 05:b0:0e:6b:39:4e:c5:7e:13:ea:40:41:26:d1:c0:c3:a2:cb: cc:3d:cf:fe:59:0a:e1:b8:0d:50:47:0a:86:b4:72:21:89:b7: 5b:e2:37:2d -----BEGIN CERTIFICATE----- MIID2TCCAsGgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx EDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMTIwODIw MDAwMDAwWhcNMTIwODIxMDAwMDAwWjB+MQswCQYDVQQGEwJHQjEYMBYGA1UECAwP Tm90dGluZ2hhbXNoaXJlMRMwEQYDVQQHDApOb3R0aW5naGFtMQ8wDQYDVQQKDAZT ZXJ2ZXIxGzAZBgNVBAsMElByb2R1Y3Rpb24tZXhwaXJlZDESMBAGA1UEAwwJbG9j YWxob3N0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlQ307vLI+IQj eK9zU3h4lf3Npv39D8fuHDnnPNUrrB2rkumN38EVN0A90baWeOxOY1RTFNSdvEJt 2V07ztTQpfHvMlrHYxssAaf0npo5lcRwAvqP0tH8DFFO4ZFUiO4NwfBqF30FnvIu uO1Js0FwIZS0AiK//3kN+zi7oT3AqWBeORiOB0gVEHuwASsrNYxnvoVwz7qZvKgd UD+s2TKR6lnESnpyXSgeQ1sLtcDQnazFaMnl7z7PWATmmU4hfMCAnYj0icrTF+F3 +jGMfRQ+r+AW+GcoSxi7/cNKZB/HJjsM2wTnETUTmcqcJYdI5mDyoe98wl/DAu5M JzLaIHZweQIDAQABo3sweTAJBgNVHRMEAjAAMCwGCWCGSAGG+EIBDQQfFh1PcGVu U1NMIEdlbmVyYXRlZCBDZXJ0aWZpY2F0ZTAdBgNVHQ4EFgQUdTZ/d8d9jbgrfH2L 2AytWTywheYwHwYDVR0jBBgwFoAUqlpbHJEymz+bw0Js0mj2p+DPvuAwDQYJKoZI hvcNAQELBQADggEBAHTHH0LjAJQaFuzJFwIfT+awSkuxLdI/BFRUI9G22vr8rD4y Naloa7e8Bu5Y1ZWlSFbL6p3TXmjOj2VgQEKmjcXkM9Pv7eT9I/4oNMrrL2lFjmHc 4gxQljWUkCVhVdWc2ABj4G6hZ/I/NKWdMyp93sCJjEax/NQZfr6D4PE0/0HTzfvl cZ0FAGev8wO+9unbdliJcmh/MoT/wDiViWAbmfxeN4H6zud4f2w+uXB0YmLTwo6O LBH85vqazR55Z1EBVB592zIJExSRo1Ytjvr4PUln/rLIEYoJDgWwDms5TsV+E+pA QSbRwMOiy8w9z/5ZCuG4DVBHCoa0ciGJt1viNy0= -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/server-expired.key000066400000000000000000000032131450213760600211070ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAlQ307vLI+IQjeK9zU3h4lf3Npv39D8fuHDnnPNUrrB2rkumN 38EVN0A90baWeOxOY1RTFNSdvEJt2V07ztTQpfHvMlrHYxssAaf0npo5lcRwAvqP 0tH8DFFO4ZFUiO4NwfBqF30FnvIuuO1Js0FwIZS0AiK//3kN+zi7oT3AqWBeORiO B0gVEHuwASsrNYxnvoVwz7qZvKgdUD+s2TKR6lnESnpyXSgeQ1sLtcDQnazFaMnl 7z7PWATmmU4hfMCAnYj0icrTF+F3+jGMfRQ+r+AW+GcoSxi7/cNKZB/HJjsM2wTn ETUTmcqcJYdI5mDyoe98wl/DAu5MJzLaIHZweQIDAQABAoIBAGp3BJtkaS4xXBDI 6UwWwbMJDUqZIpeSC763kTZ/YOlYbAPMtNy80oWbakyP6ZzH1RnX0lwPnfcpT8Mx eBW9JqdRTrQd6UsdzmoEaJKcwEL8g7Fs/SvtduXpcblmkAYaW1NKgMz0LP6iJ8NJ IhpaxFgIGidoYNF+ywDFPifmruWLa9OOQNv3fFrLHfLaZnoO/jdk6uMPMuoTsaV2 VgkzTlyQ+6VkBKB0j1VKGJIPsQglrw2kTdlucyWD6J5Lymex38nfuu8hVwIMZYxj eC0lxLllNagu6RgRx4PYmOv4041dP39MxAuLfawWz61/gXxzhaiBvUI9SXvLmWY+ hhyfFXECgYEAw3v+dBXCUSsQ/TU0AQCukGvzWYdgksZN5mK2Jq1dkdvVbgWYdrcv sY05n0ejrojouLShVdXY7hktzG122nZLuZSN8Vb8enV/FjM5s+JZi6DRSZJ/9KQN sklutALXyDsfxcvqnAZkznx/BRF2Ny1ZPuWewInFNP5B+OJ4u+GLjK0CgYEAwzJt 3VvfVNsqagHWM94L+tiDBHQjz0Wiv69wZzcCJcaVfGly8F0Uyt/DHs4cl1l3aKS3 04wgVHkowvm//MoApRYNt4LS80BnL42NFinPT+L+/eNETRUheSOEFzdi2aXAmD2G ojaVON9BKvr69BfSHNtSQFpX3qZYyczS8c41wH0CgYBYs+jwb/cusaYR35RraA3O Bs3zsBRIRaePhPc2cbBlwSUFuZBHPjRsErM07WL+ja1cMsqKknDPCanYe0tVMhyG ZzxJaLlEMBCs2C20zF7plt2gztM1BUQZxGxxTmDvwLRYIoGgrt4LPD6+4/+KZg97 FOKGZ32O4Fi7QLicOGoEOQKBgCxcU5eQ/4pbXKJG1JVpCzPw7KWgd1rtqnUBu/vZ BoXrQaHKnTJ/FPCeNcvUb679yCNh+9z55YcNGfRlqfobNlZOUsO32ZUqt8iY1M2K pvCy19x/P9B80uSi66wTDEYGY2S15tkKqpMIOdk4vLuohjnEpka1wW56Q4dpIy+M +65JAoGAYV8oukeeucSAIB74AdP6F9NP1HQc2RQT6lVYQRkSwuhRi1rEyelati/Y bezK5vsHPaNQcTM0IJup0MSQjslm7NU06xiW+jE5YCbaqTzwto1YB9U9NnOI3Cd7 2kwcJzEAvgJNEbGvHVE1c0TQh7/5YePfY+smrlpLdGF5IwsU0Fw= -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/server.crt000066400000000000000000000106631450213760600174600ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Validity Not Before: Feb 25 14:54:18 2020 GMT Not After : Feb 23 14:54:18 2025 GMT Subject: C=GB, ST=Nottinghamshire, L=Nottingham, O=Server, OU=Production, CN=localhost Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:f0:01:ba:97:f8:35:4c:d0:d4:1e:22:9a:d8:af: f6:a8:1d:75:05:a8:8d:aa:04:a9:3b:b8:fc:c6:bd: 2d:23:23:b1:fe:73:c4:24:75:aa:b2:55:9c:c8:27: 37:66:15:8d:10:4b:46:52:dd:f7:0c:e3:07:90:35: 35:64:f4:c1:34:89:14:9e:7f:5a:da:ba:6a:80:29: 19:9e:38:55:85:f1:bb:b0:1e:61:7d:99:03:28:2f: 75:4b:eb:06:aa:bc:da:d0:c2:97:cb:63:f8:83:94: c0:e6:22:da:37:18:99:68:b0:cf:b7:5e:03:bd:8b: 3e:f2:b7:47:cb:fe:c8:e8:45:73:e3:23:6e:93:14: 6a:b0:af:86:e1:b4:83:30:b5:da:df:a0:08:ac:d6: 9f:d1:4e:bd:bb:f7:7e:b4:28:c0:16:35:cb:c4:18: 7a:5b:92:cd:0e:d9:0d:d6:57:ca:6c:59:ef:ad:2e: 99:8d:41:07:87:70:0b:27:a9:1b:65:a4:f9:75:15: 81:cc:c8:d8:d2:b5:49:c9:77:01:21:ad:a7:44:3d: 4a:88:c9:5f:dd:70:6a:f6:14:0a:4c:d2:b4:d2:8c: f6:5f:cf:bd:03:0a:dd:ac:08:c2:54:5d:77:e5:96: f1:a3:06:31:5f:4f:d8:b7:f9:ce:8f:18:20:74:0e: 66:43 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Basic Constraints: CA:FALSE Netscape Comment: OpenSSL Generated Certificate X509v3 Subject Key Identifier: 21:93:75:73:22:5F:FA:88:1E:8C:4E:00:A8:B1:AD:67:B2:A7:7C:E3 X509v3 Authority Key Identifier: keyid:AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 Signature Algorithm: sha256WithRSAEncryption 5e:71:9a:51:b5:47:5b:a5:1a:fd:05:26:b6:98:50:47:d1:f3: c7:b9:1e:23:09:68:2c:23:74:48:55:2f:69:f7:e0:06:31:c0: 0c:14:4a:9a:e4:43:b4:1d:ec:80:3b:14:e7:2e:63:db:d5:99: 0a:64:5f:4e:0b:1e:e8:2d:db:7f:71:ad:b7:a6:51:a0:c9:e1: f4:52:19:30:c1:8d:ab:36:3c:77:85:da:f7:c0:5f:0b:54:d8: 48:c8:2b:98:ae:e0:f6:34:85:a1:17:5e:a5:cb:65:ea:cc:cc: 67:40:64:bf:0d:fd:21:de:1f:13:01:13:51:88:de:33:f9:94: d9:a3:13:9f:ba:6f:b4:bd:8b:61:1f:b7:43:24:97:30:f6:ab: 67:0e:ee:8d:6a:11:ba:4b:b1:1f:61:bd:d9:a0:c7:38:b1:5a: 4c:e6:51:36:03:5a:d6:56:85:b3:2f:32:0f:8d:96:da:5a:42: 85:10:ba:bb:cf:75:c9:ff:73:95:bc:34:c1:99:76:ca:b1:b5: 63:88:2c:98:51:b4:b5:61:ea:0e:20:6a:22:cf:09:65:26:b8: dc:72:d3:a1:fa:78:5c:b5:09:d9:b6:e6:d7:05:1b:35:72:e0: d8:ee:a3:39:95:5e:24:55:8c:1e:7e:87:17:40:b3:4f:4c:90: c9:2b:f2:43 -----BEGIN CERTIFICATE----- MIID0TCCArmgAwIBAgIBATANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3Qx EDAOBgNVBAsMB1Rlc3RpbmcxEzARBgNVBAMMClNpZ25pbmcgQ0EwHhcNMjAwMjI1 MTQ1NDE4WhcNMjUwMjIzMTQ1NDE4WjB2MQswCQYDVQQGEwJHQjEYMBYGA1UECAwP Tm90dGluZ2hhbXNoaXJlMRMwEQYDVQQHDApOb3R0aW5naGFtMQ8wDQYDVQQKDAZT ZXJ2ZXIxEzARBgNVBAsMClByb2R1Y3Rpb24xEjAQBgNVBAMMCWxvY2FsaG9zdDCC ASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAPABupf4NUzQ1B4imtiv9qgd dQWojaoEqTu4/Ma9LSMjsf5zxCR1qrJVnMgnN2YVjRBLRlLd9wzjB5A1NWT0wTSJ FJ5/Wtq6aoApGZ44VYXxu7AeYX2ZAygvdUvrBqq82tDCl8tj+IOUwOYi2jcYmWiw z7deA72LPvK3R8v+yOhFc+MjbpMUarCvhuG0gzC12t+gCKzWn9FOvbv3frQowBY1 y8QYeluSzQ7ZDdZXymxZ760umY1BB4dwCyepG2Wk+XUVgczI2NK1Scl3ASGtp0Q9 SojJX91wavYUCkzStNKM9l/PvQMK3awIwlRdd+WW8aMGMV9P2Lf5zo8YIHQOZkMC AwEAAaN7MHkwCQYDVR0TBAIwADAsBglghkgBhvhCAQ0EHxYdT3BlblNTTCBHZW5l cmF0ZWQgQ2VydGlmaWNhdGUwHQYDVR0OBBYEFCGTdXMiX/qIHoxOAKixrWeyp3zj MB8GA1UdIwQYMBaAFKpaWxyRMps/m8NCbNJo9qfgz77gMA0GCSqGSIb3DQEBCwUA A4IBAQBecZpRtUdbpRr9BSa2mFBH0fPHuR4jCWgsI3RIVS9p9+AGMcAMFEqa5EO0 HeyAOxTnLmPb1ZkKZF9OCx7oLdt/ca23plGgyeH0UhkwwY2rNjx3hdr3wF8LVNhI yCuYruD2NIWhF16ly2XqzMxnQGS/Df0h3h8TARNRiN4z+ZTZoxOfum+0vYthH7dD JJcw9qtnDu6NahG6S7EfYb3ZoMc4sVpM5lE2A1rWVoWzLzIPjZbaWkKFELq7z3XJ /3OVvDTBmXbKsbVjiCyYUbS1YeoOIGoizwllJrjcctOh+nhctQnZtubXBRs1cuDY 7qM5lV4kVYwefocXQLNPTJDJK/JD -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/server.key000066400000000000000000000032171450213760600174550ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpQIBAAKCAQEA8AG6l/g1TNDUHiKa2K/2qB11BaiNqgSpO7j8xr0tIyOx/nPE JHWqslWcyCc3ZhWNEEtGUt33DOMHkDU1ZPTBNIkUnn9a2rpqgCkZnjhVhfG7sB5h fZkDKC91S+sGqrza0MKXy2P4g5TA5iLaNxiZaLDPt14DvYs+8rdHy/7I6EVz4yNu kxRqsK+G4bSDMLXa36AIrNaf0U69u/d+tCjAFjXLxBh6W5LNDtkN1lfKbFnvrS6Z jUEHh3ALJ6kbZaT5dRWBzMjY0rVJyXcBIa2nRD1KiMlf3XBq9hQKTNK00oz2X8+9 AwrdrAjCVF135ZbxowYxX0/Yt/nOjxggdA5mQwIDAQABAoIBAQC26PpluxoT0sr1 tHXCUkhu0xROHajpO+glxdOPOrldoGSUgXGoP6y5gJmdyJVlzWLWWifcG6GeRp+K /aIVsJpWCWqXaIO7Unq79Za6iEBVdmcNz/mImMZZJ+IC27kXAhrZIpRAw42v6fwg 58raVnsD2ExVeObs22Q74gZrp19B88KFc8Ce3ZTJMhvIkrAbG38ilnlxZVCdxCzM Yl8NAxgHDlKUBDI9omKgksbWYwMWanZxQYwJ1i5rxJuDmGlmwqTe0z2W+2v0GxYj EldVDq9mK9dqQZQI0mQIJzGmG+weFlPoj8+GbU3aySULt8q6L/4U1nbmPfPlu5sp C1vbbsExAoGBAPtRG11qK1CP6AiYFAVUY4WF/OEzVlB2VEhyeXSm9QixZG1BbdHW vHWRxSwPwTtHwbih+hEuOXjAG8sg+JI60Iz8auhf3EVS6DjXpqN7+dua8x3ttJ9s c6PHDqRHxRcBKBGdC3Wx5IvxGGCqEb/4Aa5t3JUVPH8PvQaxCTJcJKzXAoGBAPR6 qvynjR75CqH/tDtSbR5CrUrXaup6s4xw/nUTbxXTb3PsVKklI+unFFDEs+PhmIyE xdCC2xuWbRzVSkF5vgbqraGqjGK04DGFjMdHA1oN9YskAoDaFudWp9vHAXKB0rFe HOYNuWEy2dR7qMvA9No2QbEN7Z2FWGcdTgVReth1AoGBAIxwe5lNLh1b/a9nxLBh wyeng3QZax+VsG23wtWEQyPzdYp0aLk9hZ0xs3x25WWKKOBTa9nT+fvXZvCxYRbe VRKkL93hS8dVmD3DjPSI4ExvH3LXFfuM8GZSY4U8MhAz7j9Bgljn6y6ksRm7kCW1 osLl08Ff16mtktU9c0U4JqqLAoGAc2OCWIVsYfYBQrzBgE5DGkk2KWDLIaiQHfUm 5HMrtw14SSp+OudAsPTG7egpT6EmswvnoaZha//vt/AjgAvJ2NHi6a7pW50rQ7RY 4aVuu45jGi0A0Xgd30pTJ7Qhxr3nh7d3xE0t9eZeUZ+b569G4cdB8iM0x2gsdV6r eG8goBECgYEAg2hxFf/moUr9XGUPvKgEnHHpnKflsqa93YApEP+zgMdr1yLXTKNw 5YL7tRyExXFmuNOAD3R8l0yUItX+uP01lzNrkdKl2S+yU3cYEaAvRSWH5SKqilrB e2/sVivHAthr8uIqjSnaLdpkQ3pyb7X5auSf/VfruQjHhuHCsXToRxU= -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/signingCA/000077500000000000000000000000001450213760600172745ustar00rootroot00000000000000mosquitto-2.0.18/test/ssl/signingCA/crlnumber000066400000000000000000000000031450213760600212010ustar00rootroot0000000000000003 mosquitto-2.0.18/test/ssl/signingCA/index.txt.attr000066400000000000000000000000251450213760600221120ustar00rootroot00000000000000unique_subject = yes mosquitto-2.0.18/test/ssl/signingCA/serial000066400000000000000000000000031450213760600204670ustar00rootroot0000000000000007 mosquitto-2.0.18/test/ssl/test-alt-ca.crt000066400000000000000000000105001450213760600202560ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 2 (0x2) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, L=Derby, O=Mosquitto Project, OU=Testing, CN=Root CA Validity Not Before: Feb 25 14:54:18 2020 GMT Not After : Feb 23 14:54:18 2025 GMT Subject: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Alternative Signing CA Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:ce:93:cf:ac:4e:f4:14:e1:4b:aa:b9:e8:dd:c0: 7f:eb:ab:55:16:da:8f:01:1b:55:6e:7e:b3:e0:4e: 03:68:5f:48:b4:8c:d3:d2:44:ac:3b:3a:78:88:ac: 90:f9:22:d3:b9:8a:24:35:e4:c9:2e:a0:25:b1:a6: ca:d8:86:97:8b:63:34:73:12:8c:f6:bb:38:ea:40: db:d6:ce:06:33:bb:ea:9b:3b:60:c2:af:22:07:08: 41:e4:8c:d4:ef:9d:57:b3:73:8c:28:3a:22:15:1b: 63:67:a5:cc:00:ca:a3:7c:c8:ef:d9:64:72:c2:ef: 31:a1:a6:b4:d9:ad:15:66:42:32:5c:8f:6e:dd:bc: 97:7a:5a:07:a4:a1:e2:cd:27:c3:95:5b:1d:7b:d5: 27:65:b4:34:da:6c:59:40:3a:c0:78:41:8c:48:64: e9:dd:8d:f6:a6:ff:b3:3b:63:f7:9e:f8:f9:d1:a0: 0d:0a:34:3c:2f:51:73:05:58:76:cd:ca:62:61:cb: bc:9d:76:d6:e6:ca:1b:3b:95:a2:2f:24:6c:20:84: d2:fa:28:4f:b1:d5:85:eb:f6:47:49:d5:77:a3:03: 05:cb:fa:c9:c6:b0:bf:38:ca:8e:9f:44:98:28:ee: a5:fe:d5:bc:85:7a:40:6e:e1:6b:f3:43:a2:22:0f: 28:7b Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: 75:A1:3D:93:BD:A7:31:3D:0F:D2:0B:8D:04:43:49:BF:BC:B7:BD:87 X509v3 Authority Key Identifier: keyid:7A:89:5D:1E:C9:B1:72:2F:38:DB:DE:E7:D3:49:80:2C:01:FA:3B:74 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha256WithRSAEncryption b1:d6:97:e3:46:14:82:1e:c6:8c:50:b8:e8:13:4b:62:70:62: 0c:f9:3d:07:19:cf:d0:78:2c:53:1f:10:87:0f:f9:2a:95:2e: 6f:c6:d3:87:d7:69:8d:7e:42:ee:c3:50:e6:13:56:65:6d:0f: 7c:cb:9c:35:d6:12:ff:e1:57:63:98:e0:80:53:9d:2b:8e:45: c4:34:e4:c0:60:79:d6:53:85:bc:5d:26:e4:ce:1b:6b:c4:ef: 47:e5:87:a9:9c:ea:a8:dc:35:cd:f0:b2:95:60:e2:67:89:56: e0:1e:95:71:2b:6a:77:91:15:ad:a1:50:27:5d:03:1c:13:0f: 2f:7d:ea:41:3d:1b:9f:e4:b4:b5:92:99:ca:32:dc:17:d9:54: 52:f9:b9:e0:9b:ed:23:b7:78:d3:07:36:34:2f:25:19:5f:49: e6:35:c6:d9:99:07:e9:52:dd:01:09:a9:d7:bf:e7:f4:74:6f: e2:0b:ce:da:7f:fa:38:95:43:d0:6c:f3:c4:1b:14:1c:47:50: 14:a9:48:4d:0c:d0:c6:be:a3:bc:17:9c:e3:92:24:e6:b3:51: 91:64:f4:55:1d:d1:5f:1b:69:90:ac:7e:69:e5:92:f7:d6:d2: 8a:f5:b2:5d:9b:79:8b:19:1c:6f:5a:9b:17:e5:c1:44:89:13: 0f:69:17:7c -----BEGIN CERTIFICATE----- MIIDrjCCApagAwIBAgIBAjANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1v c3F1aXR0byBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdSb290 IENBMB4XDTIwMDIyNTE0NTQxOFoXDTI1MDIyMzE0NTQxOFowcTELMAkGA1UEBhMC R0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9q ZWN0MRAwDgYDVQQLDAdUZXN0aW5nMR8wHQYDVQQDDBZBbHRlcm5hdGl2ZSBTaWdu aW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzpPPrE70FOFL qrno3cB/66tVFtqPARtVbn6z4E4DaF9ItIzT0kSsOzp4iKyQ+SLTuYokNeTJLqAl sabK2IaXi2M0cxKM9rs46kDb1s4GM7vqmztgwq8iBwhB5IzU751Xs3OMKDoiFRtj Z6XMAMqjfMjv2WRywu8xoaa02a0VZkIyXI9u3byXeloHpKHizSfDlVsde9UnZbQ0 2mxZQDrAeEGMSGTp3Y32pv+zO2P3nvj50aANCjQ8L1FzBVh2zcpiYcu8nXbW5sob O5WiLyRsIITS+ihPsdWF6/ZHSdV3owMFy/rJxrC/OMqOn0SYKO6l/tW8hXpAbuFr 80OiIg8oewIDAQABo1AwTjAdBgNVHQ4EFgQUdaE9k72nMT0P0guNBENJv7y3vYcw HwYDVR0jBBgwFoAUeoldHsmxci84297n00mALAH6O3QwDAYDVR0TBAUwAwEB/zAN BgkqhkiG9w0BAQsFAAOCAQEAsdaX40YUgh7GjFC46BNLYnBiDPk9BxnP0HgsUx8Q hw/5KpUub8bTh9dpjX5C7sNQ5hNWZW0PfMucNdYS/+FXY5jggFOdK45FxDTkwGB5 1lOFvF0m5M4ba8TvR+WHqZzqqNw1zfCylWDiZ4lW4B6VcStqd5EVraFQJ10DHBMP L33qQT0bn+S0tZKZyjLcF9lUUvm54JvtI7d40wc2NC8lGV9J5jXG2ZkH6VLdAQmp 17/n9HRv4gvO2n/6OJVD0GzzxBsUHEdQFKlITQzQxr6jvBec45Ik5rNRkWT0VR3R XxtpkKx+aeWS99bSivWyXZt5ixkcb1qbF+XBRIkTD2kXfA== -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/test-alt-ca.key000066400000000000000000000032131450213760600202610ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEogIBAAKCAQEAzpPPrE70FOFLqrno3cB/66tVFtqPARtVbn6z4E4DaF9ItIzT 0kSsOzp4iKyQ+SLTuYokNeTJLqAlsabK2IaXi2M0cxKM9rs46kDb1s4GM7vqmztg wq8iBwhB5IzU751Xs3OMKDoiFRtjZ6XMAMqjfMjv2WRywu8xoaa02a0VZkIyXI9u 3byXeloHpKHizSfDlVsde9UnZbQ02mxZQDrAeEGMSGTp3Y32pv+zO2P3nvj50aAN CjQ8L1FzBVh2zcpiYcu8nXbW5sobO5WiLyRsIITS+ihPsdWF6/ZHSdV3owMFy/rJ xrC/OMqOn0SYKO6l/tW8hXpAbuFr80OiIg8oewIDAQABAoIBAEgNoHMeet5JkwXy oHmwai3+bchx5U1ihlLrGLyVGXUvPwHS2RNPZq+l/mLVph9v+V+PAoBV06JSs7Ma VUhe8b7plGLKxqZMuVZj1wo+hEVJN1R7yo09XuYLCEi6oo8NV4i9NdbWKAsqqWp+ lwBzrcCZqacu9SRvH+Wdaxk92Of4cHcbP8nHSw9uGg6xmMONXntdJyFgX84DVC3P hRbl8SbhxzcVGGanhMvuag3lK/rJdoZGM9HRuXVUDY4vMNHzeyH783OOp3+U7TOI MQzG3gAnVzdUIqP2OxiAL8lqdPgsNPyl6z+fYnVe+8GzbkzSr7GxrEv6KqUQTX9Y IOn0cRECgYEA61OMoEeTHSaoIfPMAOYx+gq6s71x0xk5OI7kKDwitFuuxR+kA/Fi uzwomYSq8yUMAxxX97WCVQfeF5SiDYZ4ETnLjuNwh4i4mZbhwET6KeEfZz0MFQMM tBOB8e+SaNUbf1Of8l3qeFrCitYn1sY2BCGhz7DPUDwN2tqL6+Oot1kCgYEA4Lmz w9XLWe5aP2ix30qHnDjGzEouO30JxZMhk0iH/iu0QD7NEO4KPugLMATVdu5yfKnT 9Xr6gOfj2nUsMSiLBzyvOgo7OaCbznEMTdF7s3hfkh8nhKyOqYVGjzvjJP5tlnmq i+j/PAeB/my9eTof6msxJdlmVj34WXmBHQoNN/MCgYA56yxXXoZkzFjhUmHJbt6q De35wwy6yiB9PR4GkRZxkYcoWStDFSwZrSrI7hAtG9cjBNzZyMC1MOSGpTxlW809 YB4rourVUN8uXiZd7hwsJo5WGH5axY9g2tRGuZItXxYPdoONYXQN/ziWdzMC93Hf /m8W8Qt1UfKPBO8fNb8WsQKBgFTwe+ziazkzqTLcXJbcccNvhlyDEVR033OpOACW YqiEVl4OHq5uerrqNAhTW2fXmrhZ7H6VnAeLHolcznZKL7ptioGyik4u0ZVHD3J+ YnYkYmM1mVdBba7PbCsJZMJ/1GYS2I6HY6mJ4O2MplUizhtppqr6r/6a77rJ/S4/ tV1XAoGAV/73Onjr9h5TNdhzyvNYoyh9+DB8lCqMZW4oyb51m1IbhcOaHXvgiEoR WLW+iIKllTX0S0N3bwc1hzf5719FuhRN8a7KnokJ61sRwS6gdfKWTHF1j3kSlzRP +i7PEoxbRSqeA9lE/fKWprV5Iq4BP0xc1nujii8W4IjcXkp/cE8= -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/test-bad-root-ca.crt000066400000000000000000000025371450213760600212200ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDyjCCArKgAwIBAgIUdyk9NtFrh5WnXWwmIarH9aqo+2cwDQYJKoZIhvcNAQEL BQAwdjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM BURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz dGluZzEUMBIGA1UEAwwLQmFkIFJvb3QgQ0EwHhcNMjAwMjI1MTQ1NDE4WhcNMzAw MjIyMTQ1NDE4WjB2MQswCQYDVQQGEwJHQjETMBEGA1UECAwKRGVyYnlzaGlyZTEO MAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9qZWN0MRAwDgYD VQQLDAdUZXN0aW5nMRQwEgYDVQQDDAtCYWQgUm9vdCBDQTCCASIwDQYJKoZIhvcN AQEBBQADggEPADCCAQoCggEBAMsMhOIsSRKAopYjV/0lG252A4Xd1qUQlcwOPq3Z 1JYuBsa2We19xT427tSTXoDR2zdUH9nB1709wes7oTM8q1WWHszV/8DLBHotZZzf aNFy9ipwqjaJXMG5hWO1p+wg2q2BspoOpRcWhNO4ZrR5dPT1cm/N+A8TxtYd4WtO PWxXQj84rppeEUJjyE1QIRTGuQMiQxzsiyiTvyzKjuCELFcPjNwpqjEfxKaoHCD/ 5GWH1C8r2pOVIpRnP9qDVX4jQvSyAdfWeuPT0h2qHWp751e5w/inXkavzqYqI9xE vImUgW5/rVY8DrOf8huFXpfMwMBYuzxjbnObPhD85xRcc58CAwEAAaNQME4wHQYD VR0OBBYEFBuIpb/fFNJ54niM+oiZMHEDFPiNMB8GA1UdIwQYMBaAFBuIpb/fFNJ5 4niM+oiZMHEDFPiNMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQELBQADggEBAH71 WiPLeVumxVuB40cuaKpNDcMGFg3snkKi9d9eEVTP1gtfWt1dclXYnaPwYr8a3d5D iZnIVC5LUtFdWHaO6SHwsNmb59LFfXPtYxO3mOxUbSW3kTuB/N0B6laOcViPVpVt nLJ3FKKcRAjTuBfP191hbG6uG1bdAh5VLrDgA0taXcwiRd7zlKp+MdoxbetnLw3R GyzdAlWjJUGm5b7cE5sJZ0t3UoJsDeJckYJzUeDRV/90395pay3E9bd3ooa+1K9Q AJk8MuGRY7W4qtC1JioqCTJJpkyryNql2pXiN4RqChTElYa1mbOP1qet1xLMEQY6 8D9qi22Al8++KQ+gw0o= -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/test-bad-root-ca.key000066400000000000000000000032171450213760600212140ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEAywyE4ixJEoCiliNX/SUbbnYDhd3WpRCVzA4+rdnUli4GxrZZ 7X3FPjbu1JNegNHbN1Qf2cHXvT3B6zuhMzyrVZYezNX/wMsEei1lnN9o0XL2KnCq NolcwbmFY7Wn7CDarYGymg6lFxaE07hmtHl09PVyb834DxPG1h3ha049bFdCPziu ml4RQmPITVAhFMa5AyJDHOyLKJO/LMqO4IQsVw+M3CmqMR/EpqgcIP/kZYfULyva k5UilGc/2oNVfiNC9LIB19Z649PSHaodanvnV7nD+KdeRq/Opioj3ES8iZSBbn+t VjwOs5/yG4Vel8zAwFi7PGNuc5s+EPznFFxznwIDAQABAoIBAAduwuKAmoAp40m5 q3vhwtpNSZ253CSYsdMRZmv4wFZrAuZ9QFd4NiMr4Zw4dMokZHDnDG9tMBeGTjXt Ld5xRxhP8XqwDreg9t3+EW0npG+eVLKDA0gRySpyPxbCTI5ROZAGYmJPTO3GbkBN zLyogYaCAZlkIcNzhuDJoTnLWGZBzrsvwCGqwHdjGras31FSf+HYC8KcjQysQmUV F/puNldvV5rXo8rDOIdtrCC9oYvGMJDSk9X0qsHLWWqVnrruy7SmHMJ2kn6wUFfj qjm9OTdaHJOrswNmW/xLyQzj5gQOh45y7/e1W2X08HpUBzuraOnDJM2ty0dTetEW sb2+eUECgYEA8iGLJcHOWmK3cws/mAfI4GCvI1FcwYKXJzVCs+lozlzd7kioM4dW h4Mg30LbBDDsytxMNcaq5Aglwdg5CLg657vreQeZk6HUuFcwf//sJoBRAbwScE6r pI5fSSce9K+xzQzjpKiWy+Q3eC3Sax5yeeEKBSolUSKgVmDQ/lgv+D8CgYEA1q3m YK2RukYOEdcT2BpZmJI8vmeKRl6rswj6q4eSzRnruCrLxgNsDIQUkhX6zQ+a5Lhy g8USSqnpAZdBTXYIFo8fztx5abzXZpXrrUXpZTjNLncHrYE0/ztaBhEjVhoRRZ4P 4LMNnPKWNX+E5g+IhvzA8D9spSWTD1L1SorZrKECgYEAkkaBcYXry97nRLD+8jGB wUungoacqqrh9eXPLjFMB59C07lBJCAWvjcRnM8e0SFdbBA6WiJzCt+BL+IYUpQ5 wdVdI/jbZrzVbaf+vNU3LOtIBOxBl2dvejIojmD76oZZu66Vt9vBfduZRxknjV8P eWHiU8xqTuHES2qh14YfNLECgYB8GYkMqCmO0cJ+Y6OQECNtBFRjCT2w0jdVRsKJ d9TQBcTy8KJddEr4rT2q+VPDSNsUjt979udNDA2rIsHYQnwIdnI/xcnV6xllrxLP VpPGNOC/lIV2sjNtu+SdjzirJGSJpwassTUUXlOg13d++snEpsXt2+w1R5RMjntH vR56IQKBgQCF+kqRYtwBIXXyQDpxFV7ISKhfIdI2wnmEKHekXsg6APm2Hzqt8xGq nIqJIMUJvNp4mgBjw7drVuyuLu/xAsHg9zd2e85wnM5nyxTSlrD4EyN7oSSzEl4n qMI+y868gX0ZHjejFTRHiEPjERO9TndYlsXpbl8sUxs6TYzZNjB7FQ== -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/test-fake-root-ca.crt000066400000000000000000000025231450213760600213730ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDwjCCAqqgAwIBAgIUJtJNmR2IUunoKAPzp6GCdwpeRGkwDQYJKoZIhvcNAQEL BQAwcjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM BURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz dGluZzEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yMDAyMjUxNDU0MThaFw0zMDAyMjIx NDU0MThaMHIxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYD VQQHDAVEZXJieTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNVBAsM B1Rlc3RpbmcxEDAOBgNVBAMMB1Jvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDPeAsCyQPsV8X9jdcGSomKHUlqTSotEKCgZQDd6/flheJ386n5 Ay6g/37wH+qvP5l6bpTGxPLkiXrPcHKs0iYb65e4vzIUX0MmxWJjlqRnRmSflZHR lLDb/2TXGAFOnKX7p7jJ2PNaA3A3HdRby5UUQKW2y4To6RpWgMUwZE2Rv3rSwkaU +Yzfg+F2GxZo1lSo0KtEZ8aSnP/QC+BhGH+pD+YeOLgS806aa0U82mIGKY5ovgyU 1mn9cKGCwSj9vGIidOivP9w0JYwxEXLjXvp2ZS2o3asquITF0VhMIws7UrvynyFF OLmjxmVRjoNoEUakx0/zuHe0YCz8kbkg0yuhAgMBAAGjUDBOMB0GA1UdDgQWBBRU Cz3bp0AtTrVrLL/JDJwIXvG3WTAfBgNVHSMEGDAWgBRUCz3bp0AtTrVrLL/JDJwI XvG3WTAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQC8iqJSyJOCPJak f6DEinKJzQde5aYvkswyJ1gVKVQztF50bPUsXHHrQNP6Vvk8U/EFWWqYqJQ3Mk6/ t262FxS1wTllhddNu9+YfXA7yO38GDfcsr4uBTylxOTJY7lI+aRn5oOn6A9OuQcV m1HjE5QQiV8JXAl80JX29FYxSVDmDjLG+48GMhIHNhtYH4IS/jZFyfwbn+JeolEB NeGUtwryMAJqnptsIXen85mW/6j33/d7n1nabVa6Mo7V/07eE0uC45Ngmi2isOxB 23WEhOkJRANNbm9fK6qn8YMAzsGvx9O4SeQEUdp2Yd/jTvdgIBB+Ewzem8Pmn53C 7fgo0LjB -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/test-fake-root-ca.key000066400000000000000000000032131450213760600213700ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAz3gLAskD7FfF/Y3XBkqJih1Jak0qLRCgoGUA3ev35YXid/Op +QMuoP9+8B/qrz+Zem6UxsTy5Il6z3ByrNImG+uXuL8yFF9DJsViY5akZ0Zkn5WR 0ZSw2/9k1xgBTpyl+6e4ydjzWgNwNx3UW8uVFECltsuE6OkaVoDFMGRNkb960sJG lPmM34PhdhsWaNZUqNCrRGfGkpz/0AvgYRh/qQ/mHji4EvNOmmtFPNpiBimOaL4M lNZp/XChgsEo/bxiInTorz/cNCWMMRFy4176dmUtqN2rKriExdFYTCMLO1K78p8h RTi5o8ZlUY6DaBFGpMdP87h3tGAs/JG5INMroQIDAQABAoIBAEoBBuRycYzPbldY Tff3hIIYmkRpy/6RLMqp3JpMfnuHu1WQO/QP94UEPfJHYD4s0IFEipswS3fLtlvi P3V37JIPAmqrAKEVre1ZgRQG+xO/n0rxXjdE86U1v3GeJXE2HVrb4+VUFtHn4hI2 +LXZs46q1LGUfQ9bfsKWYkA1txmicBMcWho5ugTe0h/LucwOFE1CUg6bBr1BJhpK t2HCOE3vAnYZA9Zv/KowECP0PL5nw37Fpqvqpkr67j12q7z8XGAeqnl6HtLUZOVU AwSvgLcGgKsRaDTmLLwnwEvOWe3yZvC80uO8FpI4JYiRH+O0e6v5S5yW+KS+Ogl0 cAD3WAECgYEA75AZzAtOG8P19mfcPzYydR2RYw1gEVGYXVeCZFlcSpBHhgQUXFaU TJmTHZoIQ5Mmf7csjzc9FSExACLWmuQDzg6+c9Y7lETcBKLf/5/za/q1x1ExXQ+3 pAA3SYBsFAFy2npI5NOGx2y118Z+eXYRTeW6oJQhdncdDXH339jiwEECgYEA3bQ2 8q9i90LQsnGKC45/d+laS2nXbroU+DNtfIz390m9rAjjmy4625BIXY7oaEua3D9s 1QK8S6eYEVUsoXzFaLHxPAstKyJhxFYPR4++EUZ7ro7BZMJ2AJSq7aFWpOpjlsAU Olt51Ijh1sOpK/X1xbAifi7c3ocpGGILYg3Dk2ECgYEA11tghXiIQBearmdRrJWp KHVrNHNasFb8tLStaE6Y1AL9+TEDqLrAWFga05qb4TuQeXGOojSTOcJ7zVaEO/vM m9nPRk0JhFGexKAy5BbDeoeIEGUiDjnJ6am0CeRjxFxFBri1fNfXKsHEevRa0A/e oHtrmV1w85FC0cppfZb4yMECgYARY1T7662DXwYnOKhvB5oPuYmPaJmw4X9LiB0K K7Q2/N2XZIsVXKbZGZPTYqXvqB5ZL9BFVJWYCWjv0xJRCAwjjfExmF7Ohz/LukQw hKGPkUuaATBBys6edQqC4Kh+/rMY26+6c/o2JRYxVd8qx3ujKZFK/DnuagNbGjVy V0oDgQKBgBKjvOnQhFx5yGKiHUNoAFcq7y5QdpvMHUfAtsz7qHHdCEkUbJ9DqjRk j3Pwn/iR7pAXYVN1s+jmLfFTlFjLqFNXe9+TYKgnMWkKZA+9dTsryD77BJj3fhLH SbvXQs/GvYAc8xIrB5pSTIRe8r2B8PaEUjiG5rtjIHT5F5G0y92M -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/test-root-ca.crt000066400000000000000000000025231450213760600204670ustar00rootroot00000000000000-----BEGIN CERTIFICATE----- MIIDwjCCAqqgAwIBAgIURMxcSM9J+pY3g2SE3qoM34dHwPkwDQYJKoZIhvcNAQEL BQAwcjELMAkGA1UEBhMCR0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxDjAMBgNVBAcM BURlcmJ5MRowGAYDVQQKDBFNb3NxdWl0dG8gUHJvamVjdDEQMA4GA1UECwwHVGVz dGluZzEQMA4GA1UEAwwHUm9vdCBDQTAeFw0yMDAyMjUxNDU0MThaFw0zMDAyMjIx NDU0MThaMHIxCzAJBgNVBAYTAkdCMRMwEQYDVQQIDApEZXJieXNoaXJlMQ4wDAYD VQQHDAVEZXJieTEaMBgGA1UECgwRTW9zcXVpdHRvIFByb2plY3QxEDAOBgNVBAsM B1Rlc3RpbmcxEDAOBgNVBAMMB1Jvb3QgQ0EwggEiMA0GCSqGSIb3DQEBAQUAA4IB DwAwggEKAoIBAQDdpftss7fN4lzDhppzwj2WfRehR95WYmiWnXoEsKyEfuh1hINs vvI3tz1FWEb/usORr6XGZhgYwjIpSORMoBxuOZh8RDNPmO9KpLYXN1i4g+CfkGAK QoBUr7FGGlKDaK4fRg6xx8BKQ1Lxqrx+iAOpIT7tU9YYPYrwiYbdhaYwfMTKXyCl V+JypRRKWgzUkua4YRb2TnEH33NaXS0Tw+A0tRxSN26vwRheCrVfo+6CUB0kEaON +syuiHP1mGrHj3bMh/MTd3H5u2lu+1GW/Re3HdGFLuHhEq6EkF0fnPCaPS+iJKwU 1LgQZwGc+UHglTmmqUS6xhpm++/950fYoaiHAgMBAAGjUDBOMB0GA1UdDgQWBBR6 iV0eybFyLzjb3ufTSYAsAfo7dDAfBgNVHSMEGDAWgBR6iV0eybFyLzjb3ufTSYAs Afo7dDAMBgNVHRMEBTADAQH/MA0GCSqGSIb3DQEBCwUAA4IBAQB7/Zn0VBciDCXo JA4ZX5boZyQMx7Lm62O+ixChT2hW0VNlouacgfSq455sNxFJKam0ZQKzusMzssNQ ticyZUwIosGx36f8qBaGksx0EbgAh9QdOulsYDLW5UsB4Rh94C36NoTd9+BJF6D4 89IpuxQehDKKuRG0NUChEkLvJ2AAPi/+iDHZQMB/sAzaT4gJ4eMeY4p4XBb/a9P2 w05RCpVNyLg32S7ynLNUrz+/lZUfZ8sYhpdECbFDpb0e1iVc1vst8Pur+cSGFO3f HabwuWTdF9Xx8MaH/n32Pv8BxZ/hBdjsXa/CiMyT4POs6XGTpZ2iLcmHo8WS4Uls 5gKvsjuj -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/test-root-ca.key000066400000000000000000000032171450213760600204700ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEpAIBAAKCAQEA3aX7bLO3zeJcw4aac8I9ln0XoUfeVmJolp16BLCshH7odYSD bL7yN7c9RVhG/7rDka+lxmYYGMIyKUjkTKAcbjmYfEQzT5jvSqS2FzdYuIPgn5Bg CkKAVK+xRhpSg2iuH0YOscfASkNS8aq8fogDqSE+7VPWGD2K8ImG3YWmMHzEyl8g pVficqUUSloM1JLmuGEW9k5xB99zWl0tE8PgNLUcUjdur8EYXgq1X6PuglAdJBGj jfrMrohz9Zhqx492zIfzE3dx+btpbvtRlv0Xtx3RhS7h4RKuhJBdH5zwmj0voiSs FNS4EGcBnPlB4JU5pqlEusYaZvvv/edH2KGohwIDAQABAoIBAQDQTYhPrUqlJAJY Ay0uczLcNi259cffWVa/jbm5pKxNTNN8dg/paD5M3FmpzP/UoBnh1bgvD42/3umz YPylgqeVc216A8JRIQJqHQfAI9Sue8njS5Tmr37Zl9A7eMtpEjzpyTZQH9D4OfM+ iV3icEM4dLUl529Ckrv7uNPVZiA8WZUNa3NQ4lsuLHms12FOAi17wBMJOu0xTse/ vK7wucJ+p4wgXT8QZbOvqHmm3THMxKhhFYOoJK5S32jjy3kB4I56YzV6DRArLPNr RTjdXHtzNYUT4dYkDubekIDaebMKQi7nUofP6ZduJ7SAC9D118iKqChDTYP35Vmk kqqO2kgpAoGBAP47G+KhqT4tMLGs96r65Ve5KeRyLPLEgKfGXiEgrK1+lO9CFoTv 7hmZ3cF61IccilP/Tw1MG5uRrnXJWVi/u1jvdZEg74dGO2PivYhmdEgw/ZfJCAcn r8W+KKfrUQCcL/h++IrcLUVcRF4xjbhyvaCBc0zSpvxA5pn724caTm5NAoGBAN8w 1IUGpajJg8xHXca5y8UQeZwozjEn5oHdjivYL+lT2sKSKE49xzLmMPqhRtuIeiEe wBTo85PJ6SCjJPGc6wvbqPAq6CNC3BjZdh4i/+O87+fUloFAuXJn2TPAbulwFkvq 5GjeTbrJL+pd+x8VYalWSYusyxTdnlJyPE3KXOQjAoGBAJnrM4DMm2i3d2m67N+p szyfMEvNDIWWjsYFBWxNGf6YSpdojbXChYcebvH66b07fExKoJPOZlCTrOpHEz72 Jfk8UROiuyJNVRuuZU21qeUjNAW3gpLCZlr0PC0d/Ra/eROb2+JGV2pM6F+W5NSt Mz1/4ky6pLrImFTV9R0gwidpAoGAL7ZGmDF1lIGPtUnEWEk7sGL3PFTUz1lSQ4zT abgLdfvBFjscdq1qOg1PhySW+zNPuGjUcyPhfkR5m8qEiUocTSqmEMF7Yp5WYtGK GKMuxMaNGqgtjHADtNtSaWfHzgtyGMScE3cCct2zaoywtFJj0Elr63oC5/EAeWuG TLLn7LUCgYBB/Vy6WRhI8Eg+aI4vScgssdtx8FxGTYl/ZRQvgvK3iUnQdjcibY71 oPy7L1yzbOokoGZJWfcsvefTbO62DEi3k9uYlCd6T32RMbR/s1UQFZDWKcpNKzbo 5N21GE7lQp55F01nDE81RU4mcNhg1mf7792DobM7H+vM4wXW54sG9w== -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/ssl/test-signing-ca.crt000066400000000000000000000104441450213760600211430ustar00rootroot00000000000000Certificate: Data: Version: 3 (0x2) Serial Number: 1 (0x1) Signature Algorithm: sha256WithRSAEncryption Issuer: C=GB, ST=Derbyshire, L=Derby, O=Mosquitto Project, OU=Testing, CN=Root CA Validity Not Before: Feb 25 14:54:18 2020 GMT Not After : Feb 23 14:54:18 2025 GMT Subject: C=GB, ST=Derbyshire, O=Mosquitto Project, OU=Testing, CN=Signing CA Subject Public Key Info: Public Key Algorithm: rsaEncryption RSA Public-Key: (2048 bit) Modulus: 00:c1:a1:1a:6e:76:1f:98:b7:1c:7e:d6:67:d5:dc: 92:34:ef:48:22:62:94:56:cb:21:29:c1:88:7c:7a: 62:eb:6d:b9:af:8b:80:75:f4:8e:32:e2:20:e2:fa: 3a:49:c8:20:74:53:83:0f:c1:48:e2:13:3e:48:27: f2:e5:7d:55:c5:87:8c:41:9e:e2:90:58:8c:09:97: 1e:bc:5a:ce:10:71:b2:66:02:02:9b:0c:d0:24:47: 7a:3a:4d:3a:2e:c0:f0:65:6b:6a:cf:13:13:8a:f0: 6d:a0:a5:80:5f:6b:58:77:ae:91:6e:ba:ab:c5:c0: 24:f7:22:27:a4:bf:47:52:2d:a0:fc:56:b0:19:16: 84:e9:53:ac:1d:7f:29:af:c2:86:44:f5:9b:04:e4: bf:8f:e1:b8:61:a0:63:55:0a:7a:93:2a:d8:4a:20: b8:6b:b6:e9:20:c6:2c:c2:93:c2:dc:7a:69:90:8e: ea:00:5b:0c:66:8a:90:74:b4:d9:01:98:9d:fe:5b: 66:e0:39:19:22:50:0d:76:3d:1c:04:fb:93:4d:6e: 45:da:e8:cc:27:35:2a:a6:35:a8:87:e1:99:32:42: e8:71:eb:7c:f9:69:70:c7:cf:c5:cc:61:c5:ae:47: dc:20:86:2b:2b:fe:1c:dd:2c:e9:b0:38:b6:72:8e: 09:e9 Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Subject Key Identifier: AA:5A:5B:1C:91:32:9B:3F:9B:C3:42:6C:D2:68:F6:A7:E0:CF:BE:E0 X509v3 Authority Key Identifier: keyid:7A:89:5D:1E:C9:B1:72:2F:38:DB:DE:E7:D3:49:80:2C:01:FA:3B:74 X509v3 Basic Constraints: CA:TRUE Signature Algorithm: sha256WithRSAEncryption d3:8d:e3:33:87:f3:1e:4f:ff:da:1d:f8:61:3f:4a:ae:21:49: cd:ee:b1:e0:62:ab:44:70:a8:29:92:83:8d:33:45:4c:ac:b0: 66:a0:e8:32:23:76:ef:aa:89:7d:bc:e1:04:17:a5:d7:39:59: 99:ab:d9:bf:0c:fd:c5:b6:ad:6f:45:39:c9:27:f1:3e:c0:af: c3:8e:b1:1f:8f:fc:34:66:31:f4:f1:11:a0:27:99:a2:65:e2: aa:20:a7:98:b6:0e:ff:71:5e:10:e7:ab:1e:33:e7:fb:c8:59: d7:89:7a:3b:d9:a9:9f:48:2f:2e:ff:02:61:cd:86:47:60:61: 8e:81:71:68:f0:cd:63:72:b8:d2:7d:22:9d:6b:07:49:3a:0a: f7:8b:94:b3:98:90:3c:9f:e5:78:1b:84:a9:2e:fb:85:64:59: ce:6f:33:05:18:bc:21:df:f5:7c:10:79:d6:58:34:61:0e:1f: d5:af:b6:a0:8f:86:ce:56:d1:67:4f:b8:7e:50:2d:ba:77:37: 50:0f:91:06:dc:a8:7f:3c:8b:2b:8b:47:df:e3:7e:2f:79:81: 22:70:eb:f9:14:f3:66:73:17:33:e4:26:7e:47:df:80:89:de: a5:e8:5a:a9:c0:4b:3e:1b:9b:11:4b:3b:b4:8b:6a:9d:6c:ce: 39:f5:04:c9 -----BEGIN CERTIFICATE----- MIIDojCCAoqgAwIBAgIBATANBgkqhkiG9w0BAQsFADByMQswCQYDVQQGEwJHQjET MBEGA1UECAwKRGVyYnlzaGlyZTEOMAwGA1UEBwwFRGVyYnkxGjAYBgNVBAoMEU1v c3F1aXR0byBQcm9qZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRAwDgYDVQQDDAdSb290 IENBMB4XDTIwMDIyNTE0NTQxOFoXDTI1MDIyMzE0NTQxOFowZTELMAkGA1UEBhMC R0IxEzARBgNVBAgMCkRlcmJ5c2hpcmUxGjAYBgNVBAoMEU1vc3F1aXR0byBQcm9q ZWN0MRAwDgYDVQQLDAdUZXN0aW5nMRMwEQYDVQQDDApTaWduaW5nIENBMIIBIjAN BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAwaEabnYfmLccftZn1dySNO9IImKU VsshKcGIfHpi6225r4uAdfSOMuIg4vo6ScggdFODD8FI4hM+SCfy5X1VxYeMQZ7i kFiMCZcevFrOEHGyZgICmwzQJEd6Ok06LsDwZWtqzxMTivBtoKWAX2tYd66Rbrqr xcAk9yInpL9HUi2g/FawGRaE6VOsHX8pr8KGRPWbBOS/j+G4YaBjVQp6kyrYSiC4 a7bpIMYswpPC3HppkI7qAFsMZoqQdLTZAZid/ltm4DkZIlANdj0cBPuTTW5F2ujM JzUqpjWoh+GZMkLocet8+Wlwx8/FzGHFrkfcIIYrK/4c3SzpsDi2co4J6QIDAQAB o1AwTjAdBgNVHQ4EFgQUqlpbHJEymz+bw0Js0mj2p+DPvuAwHwYDVR0jBBgwFoAU eoldHsmxci84297n00mALAH6O3QwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsF AAOCAQEA043jM4fzHk//2h34YT9KriFJze6x4GKrRHCoKZKDjTNFTKywZqDoMiN2 76qJfbzhBBel1zlZmavZvwz9xbatb0U5ySfxPsCvw46xH4/8NGYx9PERoCeZomXi qiCnmLYO/3FeEOerHjPn+8hZ14l6O9mpn0gvLv8CYc2GR2BhjoFxaPDNY3K40n0i nWsHSToK94uUs5iQPJ/leBuEqS77hWRZzm8zBRi8Id/1fBB51lg0YQ4f1a+2oI+G zlbRZ0+4flAtunc3UA+RBtyofzyLK4tH3+N+L3mBInDr+RTzZnMXM+QmfkffgIne pehaqcBLPhubEUs7tItqnWzOOfUEyQ== -----END CERTIFICATE----- mosquitto-2.0.18/test/ssl/test-signing-ca.key000066400000000000000000000032131450213760600211370ustar00rootroot00000000000000-----BEGIN RSA PRIVATE KEY----- MIIEowIBAAKCAQEAwaEabnYfmLccftZn1dySNO9IImKUVsshKcGIfHpi6225r4uA dfSOMuIg4vo6ScggdFODD8FI4hM+SCfy5X1VxYeMQZ7ikFiMCZcevFrOEHGyZgIC mwzQJEd6Ok06LsDwZWtqzxMTivBtoKWAX2tYd66RbrqrxcAk9yInpL9HUi2g/Faw GRaE6VOsHX8pr8KGRPWbBOS/j+G4YaBjVQp6kyrYSiC4a7bpIMYswpPC3HppkI7q AFsMZoqQdLTZAZid/ltm4DkZIlANdj0cBPuTTW5F2ujMJzUqpjWoh+GZMkLocet8 +Wlwx8/FzGHFrkfcIIYrK/4c3SzpsDi2co4J6QIDAQABAoIBAEdv26OTWxbpv86f 5dFGPn7fJRriid33tXWFXIioUqSPZ+l3K17ZajklqoJzVVvxROAGC52dbvlRpjHS 4099zU5CMyHmr6oXsRq8sW9GhS4V9H6kETgJIyWvZU3rPiMPteGFHvPlEtm42Ilj ZhhOL2aAdlGG92bO/BRdeojStUqAvJn+5jYBpskZqw/4lLNlmpR8TpFOoGGE+eOC uXnf7Gz9+drPkoOg1/024Jygr721Klimkd6idf4v0hYt+g9GN+RVyxHKv2zYBGCV xCTYg2j7bKDgIUhBOtNGGZbpYLu5nPpZbWg9X0KEFFR7EBikSTYoBpaElCAkk3dp rGwLJr0CgYEA6oCtYouVcVabSVfvKpKep/RAYWFrz2VR5kakmRiis0Rpd1OLvwYt +Lz3c0j3ghHilcuVbB6pTjhdqa95whcPbsLzm5TcNovz3jz6sBrVJRH0wCfh+YGM hsU3SbeRDLaXCsvzmKeYrUG6SojNWsKOST+Iw93n6VlcR5nzXGobXYcCgYEA02E1 PQa+030IJt7Ord/ogOC7zK9D2s8vqK5hB5tEVpzw+NtK++HyezRgQ87/O8zm9Yka HhT6RcfhBu+UuPKZy3q+kQ5Lf3AXaj5kmfxgC9B83IwLmKINsusuBuzT4eHkVUor Lme+tNmztKogyITqDm7Bs8N98Xt8URzFMHf7uQ8CgYBx9hDMyDra9pTGouZG0TQr OQcki/yhsIKJnyEUiaVf60o5xC8wqSckL2kt7HLkEh8EXwiBn7D1o2zZLr7ENQK5 +CH9JO2T0JW2FfpaJOAagMxpqbgm0e9h+2uv9naWMBHdHFqIgEIxSLTozezGQ7B0 Jl0nmqq+ez/eSBG9go9D9wKBgBOCmGhelARvOO5liEwSK6Khm5Pj5W5vyyVVmw0Z rrAT4kYF76DaFQh8KBp6I6LAYdzt36RBKWyBa2q5eE+tzLY0SRyYZi2IChE7Wwu/ eJn+j1fH8VYQNxV5kZEAEPp7YBjjNKMe3kJRCb42Kbp4UiYs4OIXvCsqy6ms7yJv IKPPAoGBANxTT4FCNVISIvbNUSr1irLHbRfDg9m/3L7M/fU4dXO8RRY5L1LYi0hb fY3jV2nykLXbcUimJOPp0uwWMcFGM+LxVOwHGgYVBhd7mWeEAt3mLXXrNuP03gzw s4EBEKX/zGIt9YPxbhPKY5pJp0kyJ7WDxptPi+arTZA5SwJp9yyu -----END RSA PRIVATE KEY----- mosquitto-2.0.18/test/unit/000077500000000000000000000000001450213760600156105ustar00rootroot00000000000000mosquitto-2.0.18/test/unit/Makefile000066400000000000000000000117311450213760600172530ustar00rootroot00000000000000include ../../config.mk .PHONY: all check test test-broker test-lib clean coverage CPPFLAGS:=$(CPPFLAGS) -I../.. -I../../include -I../../lib -I../../src ifeq ($(WITH_BUNDLED_DEPS),yes) CPPFLAGS:=$(CPPFLAGS) -I../../deps endif CFLAGS:=$(CFLAGS) -coverage -Wall -ggdb LDFLAGS:=$(LDFLAGS) -coverage LDADD:=$(LDADD) -lcunit TEST_OBJS = test.o \ datatype_read.o \ datatype_write.o \ misc_trim_test.o \ property_add.o \ property_read.o \ property_user_read.o \ property_write.o \ stubs.o \ util_topic_test.o \ utf8.o LIB_OBJS = memory_mosq.o \ memory_public.o \ misc_mosq.o \ packet_datatypes.o \ property_mosq.o \ util_mosq.o \ util_topic.o \ utf8_mosq.o BRIDGE_TOPIC_TEST_OBJS = \ bridge_topic_test.o \ stubs.o \ BRIDGE_TOPIC_OBJS = \ bridge_topic.o \ memory_mosq.o \ memory_public.o \ util_topic.o \ PERSIST_READ_TEST_OBJS = \ persist_read_test.o \ persist_read_stubs.o PERSIST_READ_OBJS = \ memory_mosq.o \ memory_public.o \ misc_mosq.o \ packet_datatypes.o \ persist_read.o \ persist_read_v234.o \ persist_read_v5.o \ property_mosq.o \ retain.o \ topic_tok.o \ utf8_mosq.o \ util_mosq.o PERSIST_WRITE_TEST_OBJS = \ persist_write_test.o \ persist_write_stubs.o PERSIST_WRITE_OBJS = \ database.o \ memory_mosq.o \ memory_public.o \ misc_mosq.o \ packet_datatypes.o \ persist_read.o \ persist_read_v234.o \ persist_read_v5.o \ persist_write.o \ persist_write_v5.o \ property_mosq.o \ retain.o \ subs.o \ topic_tok.o \ utf8_mosq.o \ util_mosq.o TLS_TEST_OBJS = \ tls_test.o \ tls_stubs.o SUBS_TEST_OBJS = \ subs_test.o \ subs_stubs.o SUBS_OBJS = \ database.o \ memory_mosq.o \ memory_public.o \ subs.o \ topic_tok.o all : test check : test mosq_test : ${TEST_OBJS} ${LIB_OBJS} $(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD) bridge_topic_test : ${BRIDGE_TOPIC_TEST_OBJS} ${BRIDGE_TOPIC_OBJS} $(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD) persist_read_test : ${PERSIST_READ_TEST_OBJS} ${PERSIST_READ_OBJS} $(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD) persist_write_test : ${PERSIST_WRITE_TEST_OBJS} ${PERSIST_WRITE_OBJS} $(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD) subs_test : ${SUBS_TEST_OBJS} ${SUBS_OBJS} $(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD) tls_test : ${TLS_TEST_OBJS} ${TLS_OBJS} $(CROSS_COMPILE)$(CC) $(LDFLAGS) -o $@ $^ $(LDADD) -lssl -lcrypto bridge_topic.o : ../../src/bridge_topic.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_BRIDGE -c -o $@ $^ database.o : ../../src/database.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ memory_mosq.o : ../../lib/memory_mosq.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ memory_public.o : ../../src/memory_public.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ misc_mosq.o : ../../lib/misc_mosq.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ packet_datatypes.o : ../../lib/packet_datatypes.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ persist_read.o : ../../src/persist_read.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ persist_read_v234.o : ../../src/persist_read_v234.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ persist_read_v5.o : ../../src/persist_read_v5.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ persist_write.o : ../../src/persist_write.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ persist_write_v5.o : ../../src/persist_write_v5.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ property_mosq.o : ../../lib/property_mosq.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ retain.o : ../../src/retain.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ subs.o : ../../src/subs.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ topic_tok.o : ../../src/topic_tok.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -DWITH_BROKER -DWITH_PERSISTENCE -c -o $@ $^ util_mosq.o : ../../lib/util_mosq.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ util_topic.o : ../../lib/util_topic.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ utf8_mosq.o : ../../lib/utf8_mosq.c $(CROSS_COMPILE)$(CC) $(CPPFLAGS) $(CFLAGS) -c -o $@ $^ build : mosq_test bridge_topic_test persist_read_test persist_write_test subs_test tls_test test-lib : build ./mosq_test ./tls_test test-broker : build ./bridge_topic_test ./persist_read_test ./persist_write_test ./subs_test test : test-broker test-lib clean : -rm -rf mosq_test bridge_topic_test persist_read_test persist_write_test -rm -rf *.o *.gcda *.gcno coverage.info out/ coverage : lcov --capture --directory . --output-file coverage.info genhtml coverage.info --output-directory out mosquitto-2.0.18/test/unit/bridge_topic_test.c000066400000000000000000000056711450213760600214560ustar00rootroot00000000000000#include "config.h" #include #include #include #define WITH_BRIDGE #define WITH_BROKER #include "mosquitto_broker_internal.h" #include "property_mosq.h" #include "packet_mosq.h" static void map_valid_helper(const char *topic, const char *local_prefix, const char *remote_prefix, const char *incoming, const char *expected) { struct mosquitto mosq; struct mosquitto__bridge bridge; char *map_topic; int rc; memset(&mosq, 0, sizeof(struct mosquitto)); memset(&bridge, 0, sizeof(struct mosquitto__bridge)); mosq.bridge = &bridge; rc = bridge__add_topic(&bridge, topic, bd_in, 0, local_prefix, remote_prefix); CU_ASSERT_EQUAL(rc, 0); map_topic = strdup(incoming); rc = bridge__remap_topic_in(&mosq, &map_topic); CU_ASSERT_EQUAL(rc, 0); CU_ASSERT_PTR_NOT_NULL(map_topic); if(topic){ CU_ASSERT_STRING_EQUAL(map_topic, expected); free(map_topic); } } static void map_invalid_helper(const char *topic, const char *local_prefix, const char *remote_prefix) { struct mosquitto mosq; struct mosquitto__bridge bridge; int rc; memset(&mosq, 0, sizeof(struct mosquitto)); memset(&bridge, 0, sizeof(struct mosquitto__bridge)); mosq.bridge = &bridge; rc = bridge__add_topic(&bridge, topic, bd_in, 0, local_prefix, remote_prefix); CU_ASSERT_NOT_EQUAL(rc, 0); } static void TEST_remap_valid(void) { /* Examples from man page */ map_valid_helper("pattern", "L/", "R/", "R/pattern", "L/pattern"); map_valid_helper("pattern", "L/", NULL, "pattern", "L/pattern"); map_valid_helper("pattern", NULL, "R/", "R/pattern", "pattern"); map_valid_helper("pattern", NULL, NULL, "pattern", "pattern"); map_valid_helper(NULL, "local", "remote", "local", "remote"); } static void TEST_remap_invalid(void) { /* Examples from man page */ map_invalid_helper(NULL, "L/", NULL); map_invalid_helper(NULL, NULL, "R/"); map_invalid_helper(NULL, NULL, NULL); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_bridge_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Bridge remap", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Bridge remap test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Remap valid", TEST_remap_valid) || !CU_add_test(test_suite, "Remap invalid", TEST_remap_invalid) ){ printf("Error adding Bridge remap CUnit tests.\n"); return 1; } return 0; } int main(int argc, char *argv[]) { unsigned int fails; UNUSED(argc); UNUSED(argv); if(CU_initialize_registry() != CUE_SUCCESS){ printf("Error initializing CUnit registry.\n"); return 1; } if(0 || init_bridge_tests() ){ CU_cleanup_registry(); return 1; } CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); fails = CU_get_number_of_failures(); CU_cleanup_registry(); return (int)fails; } mosquitto-2.0.18/test/unit/datatype_read.c000066400000000000000000000535321450213760600205720ustar00rootroot00000000000000#include #include #include "packet_mosq.h" static void byte_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t value_expected) { struct mosquitto__packet packet; uint8_t value = 0; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = packet__read_byte(&packet, &value); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(value, value_expected); } static void uint16_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, uint16_t value_expected) { struct mosquitto__packet packet; uint16_t value = 0; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = packet__read_uint16(&packet, &value); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(value, value_expected); } static void uint32_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, uint32_t value_expected) { struct mosquitto__packet packet; uint32_t value = 0; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = packet__read_uint32(&packet, &value); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(value, value_expected); } static void varint_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, uint32_t value_expected, uint8_t bytes_expected) { struct mosquitto__packet packet; uint32_t value = UINT32_MAX; uint8_t bytes = UINT8_MAX; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = packet__read_varint(&packet, &value, &bytes); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(value, value_expected); CU_ASSERT_EQUAL(bytes, bytes_expected); } static void binary_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, const uint8_t *value_expected, int length_expected) { struct mosquitto__packet packet; uint8_t *value = NULL; uint16_t length = UINT16_MAX; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = packet__read_binary(&packet, (uint8_t **)&value, &length); CU_ASSERT_EQUAL(rc, rc_expected); if(value_expected){ /* FIXME - this should be a memcmp */ CU_ASSERT_NSTRING_EQUAL(value, value_expected, length_expected); }else{ CU_ASSERT_EQUAL(value, NULL); } CU_ASSERT_EQUAL(length, length_expected); free(value); } static void string_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, const uint8_t *value_expected, uint16_t length_expected) { struct mosquitto__packet packet; uint8_t *value = NULL; uint16_t length = UINT16_MAX; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = packet__read_string(&packet, (char **)&value, &length); CU_ASSERT_EQUAL(rc, rc_expected); if(value_expected){ if(value){ CU_ASSERT_NSTRING_EQUAL(value, value_expected, length_expected); } }else{ CU_ASSERT_PTR_NULL(value); } CU_ASSERT_EQUAL(length, length_expected); free(value); } static void bytes_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, const uint8_t *value_expected, int count) { struct mosquitto__packet packet; uint8_t value[count]; int rc; int i; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = packet__read_bytes(&packet, value, (uint32_t)count); CU_ASSERT_EQUAL(rc, rc_expected); if(rc == MOSQ_ERR_SUCCESS){ CU_ASSERT_EQUAL(packet.pos, count); } if(value_expected){ for(i=0; i #include #include #include "packet_mosq.h" /* ======================================================================== * BYTE TESTS * ======================================================================== */ /* This tests writing a Byte to an incoming packet. */ static void TEST_byte_write(void) { uint8_t payload[260]; struct mosquitto__packet packet; int i; memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); packet.payload = payload; packet.packet_length = 256; for(i=0; i<256; i++){ packet__write_byte(&packet, (uint8_t)(255-i)); } CU_ASSERT_EQUAL(packet.pos, 256); for(i=0; i<256; i++){ CU_ASSERT_EQUAL(payload[i], (uint8_t)(255-i)); } } /* ======================================================================== * TWO BYTE INTEGER TESTS * ======================================================================== */ /* This tests writing a Two Byte Integer to an incoming packet. */ static void TEST_uint16_write(void) { uint8_t payload[650]; uint16_t *payload16; struct mosquitto__packet packet; int i; memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); packet.payload = payload; packet.packet_length = 650; for(i=0; i<325; i++){ packet__write_uint16(&packet, (uint16_t)(100*i)); } CU_ASSERT_EQUAL(packet.pos, 650); payload16 = (uint16_t *)payload; for(i=0; i<325; i++){ CU_ASSERT_EQUAL(payload16[i], htons((uint16_t)(100*i))); } } /* ======================================================================== * FOUR BYTE INTEGER TESTS * ======================================================================== */ /* This tests writing a Four Byte Integer to an incoming packet. */ static void TEST_uint32_write(void) { uint8_t *payload; uint32_t *payload32; struct mosquitto__packet packet; int i; payload = calloc(42000, sizeof(uint32_t)); if(!payload){ CU_FAIL_FATAL("Out of memory"); } memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.packet_length = 42000; for(i=0; i<10500; i++){ packet__write_uint32(&packet, (uint32_t)(1000*i)); } CU_ASSERT_EQUAL(packet.pos, 42000); payload32 = (uint32_t *)payload; for(i=0; i<10500; i++){ CU_ASSERT_EQUAL(payload32[i], htonl((uint32_t)(1000*i))); } free(payload); } /* ======================================================================== * UTF-8 STRING TESTS * ======================================================================== */ /* This tests writing a UTF-8 String to an incoming packet. */ static void TEST_string_write(void) { uint8_t payload[100]; struct mosquitto__packet packet; memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, 100); packet.payload = payload; packet.packet_length = 100; packet__write_string(&packet, "first test", strlen("first test")); packet__write_string(&packet, "second test", strlen("second test")); CU_ASSERT_EQUAL(packet.pos, 2+10+2+11); CU_ASSERT_EQUAL(payload[0], 0); CU_ASSERT_EQUAL(payload[1], 10); CU_ASSERT_NSTRING_EQUAL(payload+2, "first test", 10); CU_ASSERT_EQUAL(payload[2+10+0], 0); CU_ASSERT_EQUAL(payload[2+10+1], 11); CU_ASSERT_NSTRING_EQUAL(payload+2+10+2, "second test", 11); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_datatype_write_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Datatype write", NULL, NULL); if(!test_suite){ printf("Error adding CUnit test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Byte write", TEST_byte_write) || !CU_add_test(test_suite, "Two Byte Integer write", TEST_uint16_write) || !CU_add_test(test_suite, "Four Byte Integer write", TEST_uint32_write) || !CU_add_test(test_suite, "UTF-8 String write", TEST_string_write) ){ printf("Error adding Datatype write CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/files/000077500000000000000000000000001450213760600167125ustar00rootroot00000000000000mosquitto-2.0.18/test/unit/files/persist_read/000077500000000000000000000000001450213760600213765ustar00rootroot00000000000000mosquitto-2.0.18/test/unit/files/persist_read/corrupt-header-long.test-db000066400000000000000000000000531450213760600265410ustar00rootroot00000000000000corruptcorruptcorruptcorruptcorruptcorrupt mosquitto-2.0.18/test/unit/files/persist_read/corrupt-header-short.test-db000066400000000000000000000000101450213760600267320ustar00rootroot00000000000000corrupt mosquitto-2.0.18/test/unit/files/persist_read/empty.test-db000066400000000000000000000000001450213760600240060ustar00rootroot00000000000000mosquitto-2.0.18/test/unit/files/persist_read/unsupported-version.test-db000066400000000000000000000000271450213760600267340ustar00rootroot00000000000000mosquitto dbmosquitto-2.0.18/test/unit/files/persist_read/v3-bad-chunk.test-db000066400000000000000000000000641450213760600250440ustar00rootroot00000000000000mosquitto dbnochunk mosquitto-2.0.18/test/unit/files/persist_read/v3-cfg-bad-dbid.test-db000066400000000000000000000000471450213760600253740ustar00rootroot00000000000000mosquitto db  mosquitto-2.0.18/test/unit/files/persist_read/v3-cfg-truncated.test-db000066400000000000000000000000461450213760600257360ustar00rootroot00000000000000mosquitto db mosquitto-2.0.18/test/unit/files/persist_read/v3-cfg.test-db000066400000000000000000000000471450213760600237500ustar00rootroot00000000000000mosquitto db 4Vxmosquitto-2.0.18/test/unit/files/persist_read/v3-client-message.test-db000066400000000000000000000002231450213760600261050ustar00rootroot00000000000000mosquitto db 4VxT source_idtopicpayload client-idR# client-idTsmosquitto-2.0.18/test/unit/files/persist_read/v3-client.test-db000066400000000000000000000001021450213760600244570ustar00rootroot00000000000000mosquitto db 4Vx client-idR#mosquitto-2.0.18/test/unit/files/persist_read/v3-message-store.test-db000066400000000000000000000001301450213760600257600ustar00rootroot00000000000000mosquitto db 4Vx source_idtopicpayloadmosquitto-2.0.18/test/unit/files/persist_read/v3-retain.test-db000066400000000000000000000001461450213760600244730ustar00rootroot00000000000000mosquitto db 4VxT source_idtopicpayloadTmosquitto-2.0.18/test/unit/files/persist_read/v3-sub.test-db000066400000000000000000000001421450213760600237760ustar00rootroot00000000000000mosquitto db 5Vx client-idR# client-id subscriptionmosquitto-2.0.18/test/unit/files/persist_read/v4-cfg.test-db000066400000000000000000000000471450213760600237510ustar00rootroot00000000000000mosquitto db 4Vxmosquitto-2.0.18/test/unit/files/persist_read/v4-message-store.test-db000066400000000000000000000001441450213760600257660ustar00rootroot00000000000000mosquitto db 4Vx2Tv source_idusername[topicpayloadmosquitto-2.0.18/test/unit/files/persist_read/v5-bad-chunk.test-db000066400000000000000000000001011450213760600250360ustar00rootroot00000000000000mosquitto db abcdefghijmosquitto-2.0.18/test/unit/files/persist_read/v5-cfg-truncated.test-db000066400000000000000000000000561450213760600257410ustar00rootroot00000000000000mosquitto db4Vxmosquitto-2.0.18/test/unit/files/persist_read/v5-client.test-db000066400000000000000000000001201450213760600244610ustar00rootroot00000000000000mosquitto db4Vx#R client-idmosquitto-2.0.18/test/unit/files/persist_read/v6-cfg.test-db000066400000000000000000000000571450213760600237540ustar00rootroot00000000000000mosquitto db4Vxmosquitto-2.0.18/test/unit/files/persist_read/v6-client-message-props.test-db000066400000000000000000000003101450213760600272460ustar00rootroot00000000000000mosquitto db4Vx=T [source_idusernametopicpayload(#R [client-idusrnameTs client-idmosquitto-2.0.18/test/unit/files/persist_read/v6-client-message.test-db000066400000000000000000000003051450213760600261110ustar00rootroot00000000000000mosquitto db4Vx=T [source_idusernametopicpayload(#R [client-idusrnameTs client-idmosquitto-2.0.18/test/unit/files/persist_read/v6-client.test-db000066400000000000000000000001371450213760600244720ustar00rootroot00000000000000mosquitto db4Vx(#R [client-idusrnamemosquitto-2.0.18/test/unit/files/persist_read/v6-message-store-props.test-db000066400000000000000000000003101450213760600271240ustar00rootroot00000000000000mosquitto db4Vx@ [source_idusernametopicpayload(#R [client-idusrnames client-idmosquitto-2.0.18/test/unit/files/persist_read/v6-message-store.test-db000066400000000000000000000001641450213760600257720ustar00rootroot00000000000000mosquitto db4Vx= [source_idusernametopicpayloadmosquitto-2.0.18/test/unit/files/persist_read/v6-retain.test-db000066400000000000000000000002041450213760600244710ustar00rootroot00000000000000mosquitto db4Vx=T [source_idusernametopicpayloadTmosquitto-2.0.18/test/unit/files/persist_read/v6-sub.test-db000066400000000000000000000002101450213760600237750ustar00rootroot00000000000000mosquitto db4Vx(#R [client-idusrname!v#  client-idsubscriptionmosquitto-2.0.18/test/unit/files/persist_write/000077500000000000000000000000001450213760600216155ustar00rootroot00000000000000mosquitto-2.0.18/test/unit/files/persist_write/empty.test-db000066400000000000000000000000571450213760600242410ustar00rootroot00000000000000mosquitto dbmosquitto-2.0.18/test/unit/files/persist_write/v4-full.test-db000066400000000000000000000007121450213760600243720ustar00rootroot00000000000000mosquitto db Amosqpub|0000-dev[queued/messagesmessage1Amosqpub|0000-dev[queued/messagesmessage2F?mosqpub|0000-dev[retained/message/testmessagedurable1}\durable2\durable2 durable2 ?!durable1sub/qos0/no-messagesdurable2queued/messagesmosquitto-2.0.18/test/unit/files/persist_write/v6-message-store-no-ref.test-db000066400000000000000000000000571450213760600273760ustar00rootroot00000000000000mosquitto db4Vxmosquitto-2.0.18/test/unit/misc_trim_test.c000066400000000000000000000101251450213760600210000ustar00rootroot00000000000000#include #include #include static void rtrim_helper(const char *expected, char *buf) { char *res; res = misc__trimblanks(buf); CU_ASSERT_PTR_NOT_NULL(res); if(res){ CU_ASSERT_EQUAL(strlen(buf), strlen(res)); CU_ASSERT_STRING_EQUAL(res, expected); CU_ASSERT_PTR_EQUAL(res, buf); } } static void ltrim_helper(const char *expected, char *buf) { char *res; res = misc__trimblanks(buf); CU_ASSERT_PTR_NOT_NULL(res); if(res){ CU_ASSERT_EQUAL(strlen(expected), strlen(res)); CU_ASSERT_STRING_EQUAL(res, expected); } } static void TEST_null_input(void) { char *res; res = misc__trimblanks(NULL); CU_ASSERT_PTR_NULL(res); } static void TEST_empty_input(void) { char buf[10]; char *res; memset(buf, 0, sizeof(buf)); res = misc__trimblanks(buf); CU_ASSERT_PTR_NOT_NULL(res); if(res){ CU_ASSERT_STRING_EQUAL(res, ""); } } static void TEST_no_blanks(void) { char buf[10] = "noblanks"; rtrim_helper("noblanks", buf); } static void TEST_rtrim(void) { char buf1[20] = "spaces "; char buf2[20] = "spaces "; char buf3[20] = "spaces "; char buf4[20] = "spaces "; char buf5[20] = "tabs\t"; char buf6[20] = "tabs\t\t"; char buf7[20] = "tabs\t\t\t"; char buf8[20] = "tabs\t\t\t\t"; char buf9[20] = "mixed \t"; char buf10[20] = "mixed\t "; char buf11[20] = "mixed\t\t "; char buf12[20] = "mixed \t \t "; rtrim_helper("spaces", buf1); rtrim_helper("spaces", buf2); rtrim_helper("spaces", buf3); rtrim_helper("spaces", buf4); rtrim_helper("tabs", buf5); rtrim_helper("tabs", buf6); rtrim_helper("tabs", buf7); rtrim_helper("tabs", buf8); rtrim_helper("mixed", buf9); rtrim_helper("mixed", buf10); rtrim_helper("mixed", buf11); rtrim_helper("mixed", buf12); } static void TEST_ltrim(void) { char buf1[20] = " spaces"; char buf2[20] = " spaces"; char buf3[20] = " spaces"; char buf4[20] = " spaces"; char buf5[20] = "\ttabs"; char buf6[20] = "\t\ttabs"; char buf7[20] = "\t\t\ttabs"; char buf8[20] = "\t\t\t\ttabs"; char buf9[20] = "\t mixed"; char buf10[20] = " \tmixed"; char buf11[20] = " \t\tmixed"; char buf12[20] = "\t \t mixed"; ltrim_helper("spaces", buf1); ltrim_helper("spaces", buf2); ltrim_helper("spaces", buf3); ltrim_helper("spaces", buf4); ltrim_helper("tabs", buf5); ltrim_helper("tabs", buf6); ltrim_helper("tabs", buf7); ltrim_helper("tabs", buf8); ltrim_helper("mixed", buf9); ltrim_helper("mixed", buf10); ltrim_helper("mixed", buf11); ltrim_helper("mixed", buf12); } static void TEST_btrim(void) { char buf1[20] = " spaces "; char buf2[20] = " spaces "; char buf3[20] = " spaces "; char buf4[20] = " spaces "; char buf5[20] = "\ttabs\t"; char buf6[20] = "\t\ttabs\t\t"; char buf7[20] = "\t\t\ttabs\t\t\t"; char buf8[20] = "\t\t\t\ttabs\t\t\t\t"; char buf9[20] = "\t mixed \t"; char buf10[20] = " \tmixed\t "; char buf11[20] = " \t\tmixed\t\t "; char buf12[20] = "\t \t mixed \t \t "; ltrim_helper("spaces", buf1); ltrim_helper("spaces", buf2); ltrim_helper("spaces", buf3); ltrim_helper("spaces", buf4); ltrim_helper("tabs", buf5); ltrim_helper("tabs", buf6); ltrim_helper("tabs", buf7); ltrim_helper("tabs", buf8); ltrim_helper("mixed", buf9); ltrim_helper("mixed", buf10); ltrim_helper("mixed", buf11); ltrim_helper("mixed", buf12); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_misc_trim_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Misc string trim", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Misc string trim test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Null input", TEST_null_input) || !CU_add_test(test_suite, "Empty input", TEST_empty_input) || !CU_add_test(test_suite, "No blanks", TEST_no_blanks) || !CU_add_test(test_suite, "Right trim", TEST_rtrim) || !CU_add_test(test_suite, "Left trim", TEST_ltrim) || !CU_add_test(test_suite, "Both trim", TEST_btrim) ){ printf("Error adding Misc topic CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/persist_read_stubs.c000066400000000000000000000110031450213760600216530ustar00rootroot00000000000000#include #define WITH_BROKER #include #include #include #include #include #include extern char *last_sub; extern int last_qos; extern uint32_t last_identifier; extern struct mosquitto_db db; struct mosquitto *context__init(mosq_sock_t sock) { struct mosquitto *m; UNUSED(sock); m = mosquitto__calloc(1, sizeof(struct mosquitto)); if(m){ m->msgs_in.inflight_maximum = 20; m->msgs_out.inflight_maximum = 20; m->msgs_in.inflight_quota = 20; m->msgs_out.inflight_quota = 20; } return m; } void db__msg_store_free(struct mosquitto_msg_store *store) { int i; mosquitto__free(store->source_id); mosquitto__free(store->source_username); if(store->dest_ids){ for(i=0; idest_id_count; i++){ mosquitto__free(store->dest_ids[i]); } mosquitto__free(store->dest_ids); } mosquitto__free(store->topic); mosquitto_property_free_all(&store->properties); mosquitto__free(store->payload); mosquitto__free(store); } int db__message_store(const struct mosquitto *source, struct mosquitto_msg_store *stored, uint32_t message_expiry_interval, dbid_t store_id, enum mosquitto_msg_origin origin) { int rc = MOSQ_ERR_SUCCESS; UNUSED(origin); if(source && source->id){ stored->source_id = mosquitto__strdup(source->id); }else{ stored->source_id = mosquitto__strdup(""); } if(!stored->source_id){ rc = MOSQ_ERR_NOMEM; goto error; } if(source && source->username){ stored->source_username = mosquitto__strdup(source->username); if(!stored->source_username){ rc = MOSQ_ERR_NOMEM; goto error; } } if(source){ stored->source_listener = source->listener; } stored->mid = 0; if(message_expiry_interval > 0){ stored->message_expiry_time = time(NULL) + message_expiry_interval; }else{ stored->message_expiry_time = 0; } stored->dest_ids = NULL; stored->dest_id_count = 0; db.msg_store_count++; db.msg_store_bytes += stored->payloadlen; if(!store_id){ stored->db_id = ++db.last_db_id; }else{ stored->db_id = store_id; } db.msg_store = stored; return MOSQ_ERR_SUCCESS; error: db__msg_store_free(stored); return rc; } int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { UNUSED(mosq); UNUSED(priority); UNUSED(fmt); return 0; } time_t mosquitto_time(void) { return 123; } int net__socket_close(struct mosquitto *mosq) { UNUSED(mosq); return MOSQ_ERR_SUCCESS; } int send__pingreq(struct mosquitto *mosq) { UNUSED(mosq); return MOSQ_ERR_SUCCESS; } int mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void* payload, uint8_t qos, bool retain, int access) { UNUSED(context); UNUSED(topic); UNUSED(payloadlen); UNUSED(payload); UNUSED(qos); UNUSED(retain); UNUSED(access); return MOSQ_ERR_SUCCESS; } int acl__find_acls(struct mosquitto *context) { UNUSED(context); return MOSQ_ERR_SUCCESS; } int sub__add(struct mosquitto *context, const char *sub, uint8_t qos, uint32_t identifier, int options, struct mosquitto__subhier **root) { UNUSED(context); UNUSED(options); UNUSED(root); last_sub = strdup(sub); last_qos = qos; last_identifier = identifier; return MOSQ_ERR_SUCCESS; } int db__message_insert(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, uint8_t qos, bool retain, struct mosquitto_msg_store *stored, mosquitto_property *properties, bool update) { UNUSED(context); UNUSED(mid); UNUSED(dir); UNUSED(qos); UNUSED(retain); UNUSED(stored); UNUSED(properties); UNUSED(update); return MOSQ_ERR_SUCCESS; } void db__msg_store_ref_dec(struct mosquitto_msg_store **store) { UNUSED(store); } void db__msg_store_ref_inc(struct mosquitto_msg_store *store) { store->ref_count++; } void db__msg_add_to_inflight_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { UNUSED(msg_data); UNUSED(msg); } void db__msg_add_to_queued_stats(struct mosquitto_msg_data *msg_data, struct mosquitto_client_msg *msg) { UNUSED(msg_data); UNUSED(msg); } void context__add_to_by_id(struct mosquitto *context) { if(context->in_by_id == false){ context->in_by_id = true; HASH_ADD_KEYPTR(hh_id, db.contexts_by_id, context->id, strlen(context->id), context); } } int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time) { UNUSED(context); UNUSED(expiry_time); return 0; } mosquitto-2.0.18/test/unit/persist_read_test.c000066400000000000000000000635121450213760600215060ustar00rootroot00000000000000/* Tests for persistence. * * FIXME - these need to be aggressive about finding failures, at the moment * they are just confirming that good behaviour works. */ #include #include #define WITH_BROKER #define WITH_PERSISTENCE #include "mosquitto_broker_internal.h" #include "persist.h" #include "property_mosq.h" char *last_sub = NULL; int last_qos; uint32_t last_identifier; struct mosquitto_db db; static void TEST_persistence_disabled(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); } static void TEST_empty_file(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/empty.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); } static void TEST_corrupt_header(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/corrupt-header-short.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, 1); config.persistence_filepath = "files/persist_read/corrupt-header-long.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, 1); } static void TEST_unsupported_version(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/unsupported-version.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, 1); } static void TEST_v3_config_ok(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-cfg.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.last_db_id, 0x7856341200000000); } static void TEST_v4_config_ok(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v4-cfg.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.last_db_id, 0x7856341200000000); } static void TEST_v3_config_truncated(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-cfg-truncated.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, 1); CU_ASSERT_EQUAL(db.last_db_id, 0); } static void TEST_v3_config_bad_dbid(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-cfg-bad-dbid.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, 1); CU_ASSERT_EQUAL(db.last_db_id, 0); } static void TEST_v3_bad_chunk(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-bad-chunk.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.last_db_id, 0x17); } static void TEST_v3_message_store(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-message-store.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.msg_store_count, 1); CU_ASSERT_EQUAL(db.msg_store_bytes, 7); CU_ASSERT_PTR_NOT_NULL(db.msg_store); if(db.msg_store){ CU_ASSERT_EQUAL(db.msg_store->db_id, 1); CU_ASSERT_STRING_EQUAL(db.msg_store->source_id, "source_id"); CU_ASSERT_EQUAL(db.msg_store->source_mid, 2); CU_ASSERT_EQUAL(db.msg_store->mid, 0); CU_ASSERT_EQUAL(db.msg_store->qos, 2); CU_ASSERT_EQUAL(db.msg_store->retain, 1); CU_ASSERT_PTR_NOT_NULL(db.msg_store->topic); if(db.msg_store->topic){ CU_ASSERT_STRING_EQUAL(db.msg_store->topic, "topic"); } CU_ASSERT_EQUAL(db.msg_store->payloadlen, 7); if(db.msg_store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(db.msg_store->payload, "payload", 7); } } } static void TEST_v3_client(void) { struct mosquitto__config config; struct mosquitto *context; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-client.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NULL(context->msgs_in.inflight); CU_ASSERT_PTR_NULL(context->msgs_out.inflight); CU_ASSERT_EQUAL(context->last_mid, 0x5287); } } static void TEST_v3_client_message(void) { struct mosquitto__config config; struct mosquitto *context; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-client-message.test-db"; config.max_inflight_messages = 20; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight); if(context->msgs_out.inflight){ CU_ASSERT_PTR_NULL(context->msgs_out.inflight->next); CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->store); if(context->msgs_out.inflight->store){ CU_ASSERT_EQUAL(context->msgs_out.inflight->store->ref_count, 1); CU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->store->source_id, "source_id"); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->source_mid, 2); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->mid, 0); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->qos, 2); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->retain, 1); CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->store->topic); if(context->msgs_out.inflight->store->topic){ CU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->store->topic, "topic"); } CU_ASSERT_EQUAL(context->msgs_out.inflight->store->payloadlen, 7); if(context->msgs_out.inflight->store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(context->msgs_out.inflight->store->payload, "payload", 7); } } CU_ASSERT_EQUAL(context->msgs_out.inflight->mid, 0x73); CU_ASSERT_EQUAL(context->msgs_out.inflight->qos, 1); CU_ASSERT_EQUAL(context->msgs_out.inflight->retain, 0); CU_ASSERT_EQUAL(context->msgs_out.inflight->direction, mosq_md_out); CU_ASSERT_EQUAL(context->msgs_out.inflight->state, mosq_ms_wait_for_puback); CU_ASSERT_EQUAL(context->msgs_out.inflight->dup, 0); CU_ASSERT_PTR_NULL(context->msgs_out.inflight->properties); } } } static void TEST_v3_retain(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; retain__init(); config.persistence = true; config.persistence_filepath = "files/persist_read/v3-retain.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.msg_store_count, 1); CU_ASSERT_EQUAL(db.msg_store_bytes, 7); CU_ASSERT_PTR_NOT_NULL(db.msg_store); if(db.msg_store){ CU_ASSERT_EQUAL(db.msg_store->db_id, 0x54); CU_ASSERT_STRING_EQUAL(db.msg_store->source_id, "source_id"); CU_ASSERT_EQUAL(db.msg_store->source_mid, 2); CU_ASSERT_EQUAL(db.msg_store->mid, 0); CU_ASSERT_EQUAL(db.msg_store->qos, 2); CU_ASSERT_EQUAL(db.msg_store->retain, 1); CU_ASSERT_PTR_NOT_NULL(db.msg_store->topic); if(db.msg_store->topic){ CU_ASSERT_STRING_EQUAL(db.msg_store->topic, "topic"); } CU_ASSERT_EQUAL(db.msg_store->payloadlen, 7); if(db.msg_store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(db.msg_store->payload, "payload", 7); } } CU_ASSERT_PTR_NOT_NULL(db.retains); if(db.retains){ CU_ASSERT_STRING_EQUAL(db.retains->topic, ""); CU_ASSERT_PTR_NOT_NULL(db.retains->children); if(db.retains->children){ CU_ASSERT_STRING_EQUAL(db.retains->children->topic, ""); CU_ASSERT_PTR_NOT_NULL(db.retains->children->children); if(db.retains->children->children){ CU_ASSERT_STRING_EQUAL(db.retains->children->children->topic, "topic"); } } } } static void TEST_v3_sub(void) { struct mosquitto__config config; struct mosquitto *context; int rc; last_sub = NULL; last_qos = -1; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v3-sub.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NOT_NULL(last_sub); if(last_sub){ CU_ASSERT_STRING_EQUAL(last_sub, "subscription") free(last_sub); } CU_ASSERT_EQUAL(last_qos, 1); } } static void TEST_v4_message_store(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v4-message-store.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.msg_store_count, 1); CU_ASSERT_EQUAL(db.msg_store_bytes, 7); CU_ASSERT_PTR_NOT_NULL(db.msg_store); if(db.msg_store){ CU_ASSERT_EQUAL(db.msg_store->db_id, 0xFEDCBA9876543210); CU_ASSERT_STRING_EQUAL(db.msg_store->source_id, "source_id"); CU_ASSERT_EQUAL(db.msg_store->source_mid, 0x88); CU_ASSERT_EQUAL(db.msg_store->mid, 0); CU_ASSERT_EQUAL(db.msg_store->qos, 1); CU_ASSERT_EQUAL(db.msg_store->retain, 0); CU_ASSERT_PTR_NOT_NULL(db.msg_store->topic); if(db.msg_store->topic){ CU_ASSERT_STRING_EQUAL(db.msg_store->topic, "topic"); } CU_ASSERT_EQUAL(db.msg_store->payloadlen, 7); if(db.msg_store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(db.msg_store->payload, "payload", 7); } } } static void TEST_v6_config_ok(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-cfg.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.last_db_id, 0x7856341200000000); } static void TEST_v5_config_truncated(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v5-cfg-truncated.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, 1); CU_ASSERT_EQUAL(db.last_db_id, 0); } static void TEST_v5_bad_chunk(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v5-bad-chunk.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.last_db_id, 0x17); } static void TEST_v6_message_store(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-message-store.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.msg_store_count, 1); CU_ASSERT_EQUAL(db.msg_store_bytes, 7); CU_ASSERT_PTR_NOT_NULL(db.msg_store); if(db.msg_store){ CU_ASSERT_EQUAL(db.msg_store->db_id, 1); CU_ASSERT_STRING_EQUAL(db.msg_store->source_id, "source_id"); CU_ASSERT_EQUAL(db.msg_store->source_mid, 2); CU_ASSERT_EQUAL(db.msg_store->mid, 0); CU_ASSERT_EQUAL(db.msg_store->qos, 2); CU_ASSERT_EQUAL(db.msg_store->retain, 1); CU_ASSERT_STRING_EQUAL(db.msg_store->topic, "topic"); CU_ASSERT_EQUAL(db.msg_store->payloadlen, 7); if(db.msg_store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(db.msg_store->payload, "payload", 7); } CU_ASSERT_PTR_NULL(db.msg_store->properties); } } static void TEST_v6_message_store_props(void) { struct mosquitto__config config; struct mosquitto__listener listener; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); db.config = &config; listener.port = 1883; config.listeners = &listener; config.listener_count = 1; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-message-store-props.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.msg_store_count, 1); CU_ASSERT_EQUAL(db.msg_store_bytes, 7); CU_ASSERT_PTR_NOT_NULL(db.msg_store); if(db.msg_store){ CU_ASSERT_EQUAL(db.msg_store->db_id, 1); CU_ASSERT_STRING_EQUAL(db.msg_store->source_id, "source_id"); CU_ASSERT_EQUAL(db.msg_store->source_mid, 2); CU_ASSERT_EQUAL(db.msg_store->mid, 0); CU_ASSERT_EQUAL(db.msg_store->qos, 2); CU_ASSERT_EQUAL(db.msg_store->retain, 1); CU_ASSERT_STRING_EQUAL(db.msg_store->topic, "topic"); CU_ASSERT_EQUAL(db.msg_store->payloadlen, 7); if(db.msg_store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(db.msg_store->payload, "payload", 7); } CU_ASSERT_PTR_NOT_NULL(db.msg_store->properties); if(db.msg_store->properties){ CU_ASSERT_EQUAL(db.msg_store->properties->identifier, 1); CU_ASSERT_EQUAL(db.msg_store->properties->value.i8, 1); } CU_ASSERT_PTR_NOT_NULL(db.msg_store->source_listener); } } static void TEST_v5_client(void) { struct mosquitto__config config; struct mosquitto *context; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v5-client.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NULL(context->msgs_in.inflight); CU_ASSERT_PTR_NULL(context->msgs_out.inflight); CU_ASSERT_EQUAL(context->last_mid, 0x5287); } } static void TEST_v6_client(void) { struct mosquitto__config config; struct mosquitto *context; struct mosquitto__listener listener; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); db.config = &config; listener.port = 1883; config.per_listener_settings = true; config.listeners = &listener; config.listener_count = 1; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-client.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NULL(context->msgs_in.inflight); CU_ASSERT_PTR_NULL(context->msgs_out.inflight); CU_ASSERT_EQUAL(context->last_mid, 0x5287); CU_ASSERT_EQUAL(context->listener, &listener); CU_ASSERT_PTR_NOT_NULL(context->username); if(context->username){ CU_ASSERT_STRING_EQUAL(context->username, "usrname"); } } } static void TEST_v6_client_message(void) { struct mosquitto__config config; struct mosquitto *context; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-client-message.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight); if(context->msgs_out.inflight){ CU_ASSERT_PTR_NULL(context->msgs_out.inflight->next); CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->store); if(context->msgs_out.inflight->store){ CU_ASSERT_EQUAL(context->msgs_out.inflight->store->ref_count, 1); CU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->store->source_id, "source_id"); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->source_mid, 2); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->mid, 0); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->qos, 2); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->retain, 1); CU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->store->topic, "topic"); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->payloadlen, 7); if(context->msgs_out.inflight->store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(context->msgs_out.inflight->store->payload, "payload", 7); } } CU_ASSERT_EQUAL(context->msgs_out.inflight->mid, 0x73); CU_ASSERT_EQUAL(context->msgs_out.inflight->qos, 1); CU_ASSERT_EQUAL(context->msgs_out.inflight->retain, 0); CU_ASSERT_EQUAL(context->msgs_out.inflight->direction, mosq_md_out); CU_ASSERT_EQUAL(context->msgs_out.inflight->state, mosq_ms_wait_for_puback); CU_ASSERT_EQUAL(context->msgs_out.inflight->dup, 0); CU_ASSERT_PTR_NULL(context->msgs_out.inflight->properties); } } } static void TEST_v6_client_message_props(void) { struct mosquitto__config config; struct mosquitto *context; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-client-message-props.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight); if(context->msgs_out.inflight){ CU_ASSERT_PTR_NULL(context->msgs_out.inflight->next); CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->store); if(context->msgs_out.inflight->store){ CU_ASSERT_EQUAL(context->msgs_out.inflight->store->ref_count, 1); CU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->store->source_id, "source_id"); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->source_mid, 2); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->mid, 0); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->qos, 2); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->retain, 1); CU_ASSERT_STRING_EQUAL(context->msgs_out.inflight->store->topic, "topic"); CU_ASSERT_EQUAL(context->msgs_out.inflight->store->payloadlen, 7); if(context->msgs_out.inflight->store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(context->msgs_out.inflight->store->payload, "payload", 7); } } CU_ASSERT_EQUAL(context->msgs_out.inflight->mid, 0x73); CU_ASSERT_EQUAL(context->msgs_out.inflight->qos, 1); CU_ASSERT_EQUAL(context->msgs_out.inflight->retain, 0); CU_ASSERT_EQUAL(context->msgs_out.inflight->direction, mosq_md_out); CU_ASSERT_EQUAL(context->msgs_out.inflight->state, mosq_ms_wait_for_puback); CU_ASSERT_EQUAL(context->msgs_out.inflight->dup, 0); CU_ASSERT_PTR_NOT_NULL(context->msgs_out.inflight->properties); if(context->msgs_out.inflight->properties){ CU_ASSERT_EQUAL(context->msgs_out.inflight->properties->identifier, 1); CU_ASSERT_EQUAL(context->msgs_out.inflight->properties->value.i8, 1); } } } } static void TEST_v6_retain(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-retain.test-db"; retain__init(); rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(db.msg_store_count, 1); CU_ASSERT_EQUAL(db.msg_store_bytes, 7); CU_ASSERT_PTR_NOT_NULL(db.msg_store); if(db.msg_store){ CU_ASSERT_EQUAL(db.msg_store->db_id, 0x54); CU_ASSERT_STRING_EQUAL(db.msg_store->source_id, "source_id"); CU_ASSERT_EQUAL(db.msg_store->source_mid, 2); CU_ASSERT_EQUAL(db.msg_store->mid, 0); CU_ASSERT_EQUAL(db.msg_store->qos, 2); CU_ASSERT_EQUAL(db.msg_store->retain, 1); CU_ASSERT_STRING_EQUAL(db.msg_store->topic, "topic"); CU_ASSERT_EQUAL(db.msg_store->payloadlen, 7); if(db.msg_store->payloadlen == 7){ CU_ASSERT_NSTRING_EQUAL(db.msg_store->payload, "payload", 7); } } CU_ASSERT_PTR_NOT_NULL(db.retains); if(db.retains){ CU_ASSERT_STRING_EQUAL(db.retains->topic, ""); CU_ASSERT_PTR_NOT_NULL(db.retains->children); if(db.retains->children){ CU_ASSERT_STRING_EQUAL(db.retains->children->topic, ""); CU_ASSERT_PTR_NOT_NULL(db.retains->children->children); if(db.retains->children->children){ CU_ASSERT_STRING_EQUAL(db.retains->children->children->topic, "topic"); } } } } static void TEST_v6_sub(void) { struct mosquitto__config config; struct mosquitto *context; int rc; last_sub = NULL; last_qos = -1; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-sub.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.contexts_by_id); HASH_FIND(hh_id, db.contexts_by_id, "client-id", strlen("client-id"), context); CU_ASSERT_PTR_NOT_NULL(context); if(context){ CU_ASSERT_PTR_NOT_NULL(last_sub); if(last_sub){ CU_ASSERT_STRING_EQUAL(last_sub, "subscription") free(last_sub); } CU_ASSERT_EQUAL(last_qos, 1); CU_ASSERT_EQUAL(last_identifier, 0x7623); } } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_persist_read_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Persist read", NULL, NULL); if(!test_suite){ printf("Error adding CUnit persist read test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Persistence disabled", TEST_persistence_disabled) || !CU_add_test(test_suite, "Empty file", TEST_empty_file) || !CU_add_test(test_suite, "Corrupt header", TEST_corrupt_header) || !CU_add_test(test_suite, "Unsupported version", TEST_unsupported_version) || !CU_add_test(test_suite, "v3 config ok", TEST_v3_config_ok) || !CU_add_test(test_suite, "v3 config bad truncated", TEST_v3_config_truncated) || !CU_add_test(test_suite, "v3 config bad dbid", TEST_v3_config_bad_dbid) || !CU_add_test(test_suite, "v3 bad chunk", TEST_v3_bad_chunk) || !CU_add_test(test_suite, "v3 message store", TEST_v3_message_store) || !CU_add_test(test_suite, "v3 client", TEST_v3_client) || !CU_add_test(test_suite, "v3 client message", TEST_v3_client_message) || !CU_add_test(test_suite, "v3 retain", TEST_v3_retain) || !CU_add_test(test_suite, "v3 sub", TEST_v3_sub) || !CU_add_test(test_suite, "v4 config ok", TEST_v4_config_ok) || !CU_add_test(test_suite, "v4 message store", TEST_v4_message_store) || !CU_add_test(test_suite, "v5 client", TEST_v5_client) || !CU_add_test(test_suite, "v5 config bad truncated", TEST_v5_config_truncated) || !CU_add_test(test_suite, "v5 bad chunk", TEST_v5_bad_chunk) || !CU_add_test(test_suite, "v6 config ok", TEST_v6_config_ok) || !CU_add_test(test_suite, "v6 message store", TEST_v6_message_store) || !CU_add_test(test_suite, "v6 message store+props", TEST_v6_message_store_props) || !CU_add_test(test_suite, "v6 client", TEST_v6_client) || !CU_add_test(test_suite, "v6 client message", TEST_v6_client_message) || !CU_add_test(test_suite, "v6 client message+props", TEST_v6_client_message_props) || !CU_add_test(test_suite, "v6 retain", TEST_v6_retain) || !CU_add_test(test_suite, "v6 sub", TEST_v6_sub) ){ printf("Error adding persist CUnit tests.\n"); return 1; } return 0; } int main(int argc, char *argv[]) { unsigned int fails; UNUSED(argc); UNUSED(argv); if(CU_initialize_registry() != CUE_SUCCESS){ printf("Error initializing CUnit registry.\n"); return 1; } if(0 || init_persist_read_tests() ){ CU_cleanup_registry(); return 1; } CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); fails = CU_get_number_of_failures(); CU_cleanup_registry(); return (int)fails; } mosquitto-2.0.18/test/unit/persist_write_stubs.c000066400000000000000000000050271450213760600221030ustar00rootroot00000000000000#include #define WITH_BROKER #include #include #include #include #include #include extern uint64_t last_retained; extern char *last_sub; extern int last_qos; struct mosquitto *context__init(mosq_sock_t sock) { UNUSED(sock); return mosquitto__calloc(1, sizeof(struct mosquitto)); } int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { UNUSED(mosq); UNUSED(priority); UNUSED(fmt); return 0; } time_t mosquitto_time(void) { return 123; } int net__socket_close(struct mosquitto *mosq) { UNUSED(mosq); return MOSQ_ERR_SUCCESS; } int send__pingreq(struct mosquitto *mosq) { UNUSED(mosq); return MOSQ_ERR_SUCCESS; } int mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void* payload, uint8_t qos, bool retain, int access) { UNUSED(context); UNUSED(topic); UNUSED(payloadlen); UNUSED(payload); UNUSED(qos); UNUSED(retain); UNUSED(access); return MOSQ_ERR_SUCCESS; } int acl__find_acls(struct mosquitto *context) { UNUSED(context); return MOSQ_ERR_SUCCESS; } int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { UNUSED(mosq); UNUSED(mid); UNUSED(topic); UNUSED(payloadlen); UNUSED(payload); UNUSED(qos); UNUSED(retain); UNUSED(dup); UNUSED(cmsg_props); UNUSED(store_props); UNUSED(expiry_interval); return MOSQ_ERR_SUCCESS; } int send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(mid); UNUSED(properties); return MOSQ_ERR_SUCCESS; } int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(mid); UNUSED(reason_code); UNUSED(properties); return MOSQ_ERR_SUCCESS; } int send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(mid); UNUSED(properties); return MOSQ_ERR_SUCCESS; } void context__add_to_by_id(struct mosquitto *context) { if(context->in_by_id == false){ context->in_by_id = true; HASH_ADD_KEYPTR(hh_id, db.contexts_by_id, context->id, strlen(context->id), context); } } int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time) { UNUSED(context); UNUSED(expiry_time); return 0; } mosquitto-2.0.18/test/unit/persist_write_test.c000066400000000000000000000237531450213760600217300ustar00rootroot00000000000000/* Tests for persistence. * * FIXME - these need to be aggressive about finding failures, at the moment * they are just confirming that good behaviour works. */ #include #include #define WITH_BROKER #define WITH_PERSISTENCE #include "mosquitto_broker_internal.h" #include "persist.h" uint64_t last_retained; char *last_sub = NULL; int last_qos; struct mosquitto_db db; /* read entire file into memory */ static int file_read(const char *filename, uint8_t **data, size_t *len) { FILE *fptr; size_t rc; fptr = fopen(filename, "rb"); if(!fptr) return 1; fseek(fptr, 0, SEEK_END); *len = (size_t)ftell(fptr); *data = malloc(*len); if(!(*data)){ fclose(fptr); return 1; } fseek(fptr, 0, SEEK_SET); rc = fread(*data, 1, *len, fptr); fclose(fptr); if(rc == *len){ return 0; }else{ *len = 0; free(*data); return 1; } } /* Crude file diff, only for small files */ static int file_diff(const char *one, const char *two) { size_t len1, len2; uint8_t *data1 = NULL, *data2 = NULL; int rc = 1; if(file_read(one, &data1, &len1)){ return 1; } if(file_read(two, &data2, &len2)){ free(data1); return 1; } if(len1 == len2){ rc = memcmp(data1, data2, len1); } free(data1); free(data2); return rc; } static void TEST_persistence_disabled(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; rc = persist__backup(false); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); config.persistence_filepath = "disabled.db"; rc = persist__backup(false); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); } static void TEST_empty_file(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "empty.db"; rc = persist__backup(false); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_write/empty.test-db", "empty.db")); unlink("empty.db"); } static void TEST_v6_config_ok(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-cfg.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); config.persistence_filepath = "v6-cfg.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v6-cfg.test-db", "v6-cfg.db")); unlink("v6-cfg.db"); } static void TEST_v6_message_store_no_ref(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-message-store.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); config.persistence_filepath = "v6-message-store-no-ref.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_write/v6-message-store-no-ref.test-db", "v6-message-store-no-ref.db")); unlink("v6-message-store-no-ref.db"); } static void TEST_v6_message_store_props(void) { struct mosquitto__config config; struct mosquitto__listener listener; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); db.config = &config; listener.port = 1883; config.per_listener_settings = true; config.listeners = &listener; config.listener_count = 1; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-message-store-props.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); config.persistence_filepath = "v6-message-store-props.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v6-message-store-props.test-db", "v6-message-store-props.db")); unlink("v6-message-store-props.db"); } static void TEST_v6_client(void) { struct mosquitto__config config; struct mosquitto__listener listener; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); db.config = &config; listener.port = 1883; config.per_listener_settings = true; config.listeners = &listener; config.listener_count = 1; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-client.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); config.persistence_filepath = "v6-client.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v6-client.test-db", "v6-client.db")); unlink("v6-client.db"); } static void TEST_v6_client_message(void) { struct mosquitto__config config; struct mosquitto__listener listener; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); db.config = &config; listener.port = 1883; config.per_listener_settings = true; config.listeners = &listener; config.listener_count = 1; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-client-message.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); config.persistence_filepath = "v6-client-message.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v6-client-message.test-db", "v6-client-message.db")); unlink("v6-client-message.db"); } static void TEST_v6_client_message_props(void) { struct mosquitto__config config; struct mosquitto__listener listener; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); db.config = &config; listener.port = 1883; config.per_listener_settings = true; config.listeners = &listener; config.listener_count = 1; config.persistence = true; config.persistence_filepath = "files/persist_read/v6-client-message-props.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.msg_store); if(db.msg_store){ CU_ASSERT_PTR_NOT_NULL(db.msg_store->source_listener); if(db.msg_store->source_listener){ CU_ASSERT_EQUAL(db.msg_store->source_listener->port, 1883); } } config.persistence_filepath = "v6-client-message-props.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v6-client-message-props.test-db", "v6-client-message-props.db")); unlink("v6-client-message-props.db"); } static void TEST_v6_sub(void) { struct mosquitto__config config; struct mosquitto__listener listener; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); db.config = &config; listener.port = 1883; config.per_listener_settings = true; config.listeners = &listener; config.listener_count = 1; db__open(&config); config.persistence = true; config.persistence_filepath = "files/persist_read/v6-sub.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); config.persistence_filepath = "v6-sub.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_read/v6-sub.test-db", "v6-sub.db")); unlink("v6-sub.db"); } #if 0 NOT WORKING static void TEST_v5_full(void) { struct mosquitto__config config; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); db.config = &config; db__open(&config); config.persistence = true; config.persistence_filepath = "files/persist_write/v5-full.test-db"; rc = persist__restore(); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); config.persistence_filepath = "v5-full.db"; rc = persist__backup(true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(0, file_diff("files/persist_write/v5-full.test-db", "v5-full.db")); unlink("v5-full.db"); } #endif /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int main(int argc, char *argv[]) { CU_pSuite test_suite = NULL; unsigned int fails; UNUSED(argc); UNUSED(argv); if(CU_initialize_registry() != CUE_SUCCESS){ printf("Error initializing CUnit registry.\n"); return 1; } test_suite = CU_add_suite("Persist write", NULL, NULL); if(!test_suite){ printf("Error adding CUnit persist write test suite.\n"); CU_cleanup_registry(); return 1; } if(0 || !CU_add_test(test_suite, "Persistence disabled", TEST_persistence_disabled) || !CU_add_test(test_suite, "Empty file", TEST_empty_file) || !CU_add_test(test_suite, "v6 config ok", TEST_v6_config_ok) || !CU_add_test(test_suite, "v6 message store (message has no refs)", TEST_v6_message_store_no_ref) || !CU_add_test(test_suite, "v6 message store + props", TEST_v6_message_store_props) || !CU_add_test(test_suite, "v6 client", TEST_v6_client) || !CU_add_test(test_suite, "v6 client message", TEST_v6_client_message) || !CU_add_test(test_suite, "v6 client message+props", TEST_v6_client_message_props) || !CU_add_test(test_suite, "v6 sub", TEST_v6_sub) //|| !CU_add_test(test_suite, "v5 full", TEST_v5_full) ){ printf("Error adding persist CUnit tests.\n"); CU_cleanup_registry(); return 1; } CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); fails = CU_get_number_of_failures(); CU_cleanup_registry(); return (int)fails; } mosquitto-2.0.18/test/unit/property_add.c000066400000000000000000000571021450213760600204550ustar00rootroot00000000000000#include #include #include "mqtt_protocol.h" #include "property_mosq.h" #include "packet_mosq.h" /* ======================================================================== * BAD IDENTIFIER * ======================================================================== */ static void bad_add_byte_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_byte(&proplist, identifier, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_PTR_NULL(proplist); } static void bad_add_int16_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_int16(&proplist, identifier, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_PTR_NULL(proplist); } static void bad_add_int32_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_int32(&proplist, identifier, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_PTR_NULL(proplist); } static void bad_add_varint_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_varint(&proplist, identifier, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_PTR_NULL(proplist); } static void bad_add_binary_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_binary(&proplist, identifier, "test", 4); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_PTR_NULL(proplist); } static void bad_add_string_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_string(&proplist, identifier, "test"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_PTR_NULL(proplist); } static void bad_add_string_pair_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_string_pair(&proplist, identifier, "key", "value"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_PTR_NULL(proplist); } static void TEST_add_bad_byte(void) { bad_add_byte_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); bad_add_byte_helper(MQTT_PROP_CONTENT_TYPE); bad_add_byte_helper(MQTT_PROP_RESPONSE_TOPIC); bad_add_byte_helper(MQTT_PROP_CORRELATION_DATA); bad_add_byte_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); bad_add_byte_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); bad_add_byte_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); bad_add_byte_helper(MQTT_PROP_SERVER_KEEP_ALIVE); bad_add_byte_helper(MQTT_PROP_AUTHENTICATION_METHOD); bad_add_byte_helper(MQTT_PROP_AUTHENTICATION_DATA); bad_add_byte_helper(MQTT_PROP_WILL_DELAY_INTERVAL); bad_add_byte_helper(MQTT_PROP_RESPONSE_INFORMATION); bad_add_byte_helper(MQTT_PROP_SERVER_REFERENCE); bad_add_byte_helper(MQTT_PROP_REASON_STRING); bad_add_byte_helper(MQTT_PROP_RECEIVE_MAXIMUM); bad_add_byte_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); bad_add_byte_helper(MQTT_PROP_TOPIC_ALIAS); bad_add_byte_helper(MQTT_PROP_USER_PROPERTY); bad_add_byte_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); } static void TEST_add_bad_int16(void) { bad_add_int16_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); bad_add_int16_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); bad_add_int16_helper(MQTT_PROP_CONTENT_TYPE); bad_add_int16_helper(MQTT_PROP_RESPONSE_TOPIC); bad_add_int16_helper(MQTT_PROP_CORRELATION_DATA); bad_add_int16_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); bad_add_int16_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); bad_add_int16_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); bad_add_int16_helper(MQTT_PROP_AUTHENTICATION_METHOD); bad_add_int16_helper(MQTT_PROP_AUTHENTICATION_DATA); bad_add_int16_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); bad_add_int16_helper(MQTT_PROP_WILL_DELAY_INTERVAL); bad_add_int16_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); bad_add_int16_helper(MQTT_PROP_RESPONSE_INFORMATION); bad_add_int16_helper(MQTT_PROP_SERVER_REFERENCE); bad_add_int16_helper(MQTT_PROP_REASON_STRING); bad_add_int16_helper(MQTT_PROP_MAXIMUM_QOS); bad_add_int16_helper(MQTT_PROP_RETAIN_AVAILABLE); bad_add_int16_helper(MQTT_PROP_USER_PROPERTY); bad_add_int16_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); bad_add_int16_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); bad_add_int16_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); bad_add_int16_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_add_bad_int32(void) { bad_add_int32_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); bad_add_int32_helper(MQTT_PROP_CONTENT_TYPE); bad_add_int32_helper(MQTT_PROP_RESPONSE_TOPIC); bad_add_int32_helper(MQTT_PROP_CORRELATION_DATA); bad_add_int32_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); bad_add_int32_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); bad_add_int32_helper(MQTT_PROP_SERVER_KEEP_ALIVE); bad_add_int32_helper(MQTT_PROP_AUTHENTICATION_METHOD); bad_add_int32_helper(MQTT_PROP_AUTHENTICATION_DATA); bad_add_int32_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); bad_add_int32_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); bad_add_int32_helper(MQTT_PROP_RESPONSE_INFORMATION); bad_add_int32_helper(MQTT_PROP_SERVER_REFERENCE); bad_add_int32_helper(MQTT_PROP_REASON_STRING); bad_add_int32_helper(MQTT_PROP_RECEIVE_MAXIMUM); bad_add_int32_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); bad_add_int32_helper(MQTT_PROP_TOPIC_ALIAS); bad_add_int32_helper(MQTT_PROP_MAXIMUM_QOS); bad_add_int32_helper(MQTT_PROP_RETAIN_AVAILABLE); bad_add_int32_helper(MQTT_PROP_USER_PROPERTY); bad_add_int32_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); bad_add_int32_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); bad_add_int32_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_add_bad_varint(void) { bad_add_varint_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); bad_add_varint_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); bad_add_varint_helper(MQTT_PROP_CONTENT_TYPE); bad_add_varint_helper(MQTT_PROP_RESPONSE_TOPIC); bad_add_varint_helper(MQTT_PROP_CORRELATION_DATA); bad_add_varint_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); bad_add_varint_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); bad_add_varint_helper(MQTT_PROP_SERVER_KEEP_ALIVE); bad_add_varint_helper(MQTT_PROP_AUTHENTICATION_METHOD); bad_add_varint_helper(MQTT_PROP_AUTHENTICATION_DATA); bad_add_varint_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); bad_add_varint_helper(MQTT_PROP_WILL_DELAY_INTERVAL); bad_add_varint_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); bad_add_varint_helper(MQTT_PROP_RESPONSE_INFORMATION); bad_add_varint_helper(MQTT_PROP_SERVER_REFERENCE); bad_add_varint_helper(MQTT_PROP_REASON_STRING); bad_add_varint_helper(MQTT_PROP_RECEIVE_MAXIMUM); bad_add_varint_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); bad_add_varint_helper(MQTT_PROP_TOPIC_ALIAS); bad_add_varint_helper(MQTT_PROP_MAXIMUM_QOS); bad_add_varint_helper(MQTT_PROP_RETAIN_AVAILABLE); bad_add_varint_helper(MQTT_PROP_USER_PROPERTY); bad_add_varint_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); bad_add_varint_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); bad_add_varint_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); bad_add_varint_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_add_bad_binary(void) { bad_add_binary_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); bad_add_binary_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); bad_add_binary_helper(MQTT_PROP_CONTENT_TYPE); bad_add_binary_helper(MQTT_PROP_RESPONSE_TOPIC); bad_add_binary_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); bad_add_binary_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); bad_add_binary_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); bad_add_binary_helper(MQTT_PROP_SERVER_KEEP_ALIVE); bad_add_binary_helper(MQTT_PROP_AUTHENTICATION_METHOD); bad_add_binary_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); bad_add_binary_helper(MQTT_PROP_WILL_DELAY_INTERVAL); bad_add_binary_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); bad_add_binary_helper(MQTT_PROP_RESPONSE_INFORMATION); bad_add_binary_helper(MQTT_PROP_SERVER_REFERENCE); bad_add_binary_helper(MQTT_PROP_REASON_STRING); bad_add_binary_helper(MQTT_PROP_RECEIVE_MAXIMUM); bad_add_binary_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); bad_add_binary_helper(MQTT_PROP_TOPIC_ALIAS); bad_add_binary_helper(MQTT_PROP_MAXIMUM_QOS); bad_add_binary_helper(MQTT_PROP_RETAIN_AVAILABLE); bad_add_binary_helper(MQTT_PROP_USER_PROPERTY); bad_add_binary_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); bad_add_binary_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); bad_add_binary_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); bad_add_binary_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_add_bad_string(void) { bad_add_string_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); bad_add_string_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); bad_add_string_helper(MQTT_PROP_CORRELATION_DATA); bad_add_string_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); bad_add_string_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); bad_add_string_helper(MQTT_PROP_SERVER_KEEP_ALIVE); bad_add_string_helper(MQTT_PROP_AUTHENTICATION_DATA); bad_add_string_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); bad_add_string_helper(MQTT_PROP_WILL_DELAY_INTERVAL); bad_add_string_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); bad_add_string_helper(MQTT_PROP_RECEIVE_MAXIMUM); bad_add_string_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); bad_add_string_helper(MQTT_PROP_TOPIC_ALIAS); bad_add_string_helper(MQTT_PROP_MAXIMUM_QOS); bad_add_string_helper(MQTT_PROP_RETAIN_AVAILABLE); bad_add_string_helper(MQTT_PROP_USER_PROPERTY); bad_add_string_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); bad_add_string_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); bad_add_string_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); bad_add_string_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_add_bad_string_pair(void) { bad_add_string_pair_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); bad_add_string_pair_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); bad_add_string_pair_helper(MQTT_PROP_CONTENT_TYPE); bad_add_string_pair_helper(MQTT_PROP_RESPONSE_TOPIC); bad_add_string_pair_helper(MQTT_PROP_CORRELATION_DATA); bad_add_string_pair_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); bad_add_string_pair_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); bad_add_string_pair_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); bad_add_string_pair_helper(MQTT_PROP_SERVER_KEEP_ALIVE); bad_add_string_pair_helper(MQTT_PROP_AUTHENTICATION_METHOD); bad_add_string_pair_helper(MQTT_PROP_AUTHENTICATION_DATA); bad_add_string_pair_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); bad_add_string_pair_helper(MQTT_PROP_WILL_DELAY_INTERVAL); bad_add_string_pair_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); bad_add_string_pair_helper(MQTT_PROP_RESPONSE_INFORMATION); bad_add_string_pair_helper(MQTT_PROP_SERVER_REFERENCE); bad_add_string_pair_helper(MQTT_PROP_REASON_STRING); bad_add_string_pair_helper(MQTT_PROP_RECEIVE_MAXIMUM); bad_add_string_pair_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); bad_add_string_pair_helper(MQTT_PROP_TOPIC_ALIAS); bad_add_string_pair_helper(MQTT_PROP_MAXIMUM_QOS); bad_add_string_pair_helper(MQTT_PROP_RETAIN_AVAILABLE); bad_add_string_pair_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); bad_add_string_pair_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); bad_add_string_pair_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); bad_add_string_pair_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); } /* ======================================================================== * SINGLE ADD * ======================================================================== */ static void single_add_byte_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_byte(&proplist, identifier, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ CU_ASSERT_EQUAL(proplist->identifier, identifier); CU_ASSERT_EQUAL(proplist->value.i8, 1); CU_ASSERT_PTR_NULL(proplist->next); mosquitto_property_free_all(&proplist); } } static void single_add_int16_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_int16(&proplist, identifier, 11234); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ CU_ASSERT_EQUAL(proplist->identifier, identifier); CU_ASSERT_EQUAL(proplist->value.i16, 11234); CU_ASSERT_PTR_NULL(proplist->next); mosquitto_property_free_all(&proplist); } } static void single_add_int32_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_int32(&proplist, identifier, 765432); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ CU_ASSERT_EQUAL(proplist->identifier, identifier); CU_ASSERT_EQUAL(proplist->value.i32, 765432); CU_ASSERT_PTR_NULL(proplist->next); mosquitto_property_free_all(&proplist); } } static void single_add_varint_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_varint(&proplist, identifier, 139123999); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ CU_ASSERT_EQUAL(proplist->identifier, identifier); CU_ASSERT_EQUAL(proplist->value.varint, 139123999); CU_ASSERT_PTR_NULL(proplist->next); mosquitto_property_free_all(&proplist); } } static void single_add_binary_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_binary(&proplist, identifier, "test", 4); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ CU_ASSERT_EQUAL(proplist->identifier, identifier); CU_ASSERT_EQUAL(proplist->value.bin.len, 4); CU_ASSERT_NSTRING_EQUAL(proplist->value.bin.v, "test", 4); CU_ASSERT_PTR_NULL(proplist->next); mosquitto_property_free_all(&proplist); } } static void single_add_string_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_string(&proplist, identifier, "string"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ CU_ASSERT_EQUAL(proplist->identifier, identifier); CU_ASSERT_STRING_EQUAL(proplist->value.s.v, "string"); CU_ASSERT_EQUAL(proplist->value.s.len, strlen("string")); CU_ASSERT_PTR_NULL(proplist->next); mosquitto_property_free_all(&proplist); } } static void single_add_string_pair_helper(int identifier) { mosquitto_property *proplist = NULL; int rc; rc = mosquitto_property_add_string_pair(&proplist, identifier, "key", "value"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ CU_ASSERT_EQUAL(proplist->identifier, identifier); CU_ASSERT_STRING_EQUAL(proplist->name.v, "key"); CU_ASSERT_EQUAL(proplist->name.len, strlen("key")); CU_ASSERT_STRING_EQUAL(proplist->value.s.v, "value"); CU_ASSERT_EQUAL(proplist->value.s.len, strlen("value")); CU_ASSERT_PTR_NULL(proplist->next); mosquitto_property_free_all(&proplist); } } static void TEST_add_single_byte(void) { single_add_byte_helper(MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); single_add_byte_helper(MQTT_PROP_REQUEST_PROBLEM_INFORMATION); single_add_byte_helper(MQTT_PROP_REQUEST_RESPONSE_INFORMATION); single_add_byte_helper(MQTT_PROP_MAXIMUM_QOS); single_add_byte_helper(MQTT_PROP_RETAIN_AVAILABLE); single_add_byte_helper(MQTT_PROP_WILDCARD_SUB_AVAILABLE); single_add_byte_helper(MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); single_add_byte_helper(MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_add_single_int16(void) { single_add_int16_helper(MQTT_PROP_SERVER_KEEP_ALIVE); single_add_int16_helper(MQTT_PROP_RECEIVE_MAXIMUM); single_add_int16_helper(MQTT_PROP_TOPIC_ALIAS_MAXIMUM); single_add_int16_helper(MQTT_PROP_TOPIC_ALIAS); } static void TEST_add_single_int32(void) { single_add_int32_helper(MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); single_add_int32_helper(MQTT_PROP_SESSION_EXPIRY_INTERVAL); single_add_int32_helper(MQTT_PROP_WILL_DELAY_INTERVAL); single_add_int32_helper(MQTT_PROP_MAXIMUM_PACKET_SIZE); } static void TEST_add_single_varint(void) { single_add_varint_helper(MQTT_PROP_SUBSCRIPTION_IDENTIFIER); } static void TEST_add_single_binary(void) { single_add_binary_helper(MQTT_PROP_CORRELATION_DATA); single_add_binary_helper(MQTT_PROP_AUTHENTICATION_DATA); } static void TEST_add_single_string(void) { single_add_string_helper(MQTT_PROP_CONTENT_TYPE); single_add_string_helper(MQTT_PROP_RESPONSE_TOPIC); single_add_string_helper(MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); single_add_string_helper(MQTT_PROP_AUTHENTICATION_METHOD); single_add_string_helper(MQTT_PROP_RESPONSE_INFORMATION); single_add_string_helper(MQTT_PROP_SERVER_REFERENCE); single_add_string_helper(MQTT_PROP_REASON_STRING); } static void TEST_add_single_string_pair(void) { single_add_string_pair_helper(MQTT_PROP_USER_PROPERTY); } /* ======================================================================== * ADD ALL PROPERTIES FOR A COMMAND * ======================================================================== */ static void TEST_add_all_connect(void) { mosquitto_property *proplist = NULL; mosquitto_property *p; int count; int rc; rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string(&proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); p = proplist; count = 0; while(p){ count++; p = p->next; } CU_ASSERT_EQUAL(count, 9); mosquitto_property_free_all(&proplist); } static void TEST_add_all_connack(void) { mosquitto_property *proplist = NULL; mosquitto_property *p; int count; int rc; rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string(&proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "clientid"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 900); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string(&proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_binary(&proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string(&proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string(&proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string(&proplist, MQTT_PROP_REASON_STRING, "reason"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_int16(&proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_MAXIMUM_QOS, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_string_pair(&proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_int32(&proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); p = proplist; count = 0; while(p){ count++; p = p->next; } CU_ASSERT_EQUAL(count, 17); mosquitto_property_free_all(&proplist); } static void TEST_check_length(void) { mosquitto_property *proplist = NULL; int rc; unsigned int len; int varbytes; int i; len = property__get_remaining_length(proplist); CU_ASSERT_EQUAL(len, 1); for(i=1; i<10000; i++){ rc = mosquitto_property_add_byte(&proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist); if(proplist){ len = property__get_remaining_length(proplist); if(i < 64){ varbytes = 1; }else if(i < 8192){ varbytes = 2; }else{ varbytes = 3; } CU_ASSERT_EQUAL(len, varbytes+2*i); }else{ break; } } mosquitto_property_free_all(&proplist); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_property_add_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Property add", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Property add test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Add nothing, check length", TEST_check_length) || !CU_add_test(test_suite, "Add bad byte", TEST_add_bad_byte) || !CU_add_test(test_suite, "Add bad int16", TEST_add_bad_int16) || !CU_add_test(test_suite, "Add bad int32", TEST_add_bad_int32) || !CU_add_test(test_suite, "Add bad varint", TEST_add_bad_varint) || !CU_add_test(test_suite, "Add bad binary", TEST_add_bad_binary) || !CU_add_test(test_suite, "Add bad string", TEST_add_bad_string) || !CU_add_test(test_suite, "Add bad string pair", TEST_add_bad_string_pair) || !CU_add_test(test_suite, "Add single byte", TEST_add_single_byte) || !CU_add_test(test_suite, "Add single int16", TEST_add_single_int16) || !CU_add_test(test_suite, "Add single int32", TEST_add_single_int32) || !CU_add_test(test_suite, "Add single varint", TEST_add_single_varint) || !CU_add_test(test_suite, "Add single binary", TEST_add_single_binary) || !CU_add_test(test_suite, "Add single string", TEST_add_single_string) || !CU_add_test(test_suite, "Add single string pair", TEST_add_single_string_pair) || !CU_add_test(test_suite, "Add all CONNECT", TEST_add_all_connect) || !CU_add_test(test_suite, "Add all CONNACK", TEST_add_all_connack) ){ printf("Error adding Property Add CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/property_read.c000066400000000000000000001642501450213760600206430ustar00rootroot00000000000000#include #include #include "mqtt_protocol.h" #include "property_mosq.h" #include "packet_mosq.h" static void byte_prop_read_helper( int command, uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t identifier, uint8_t value_expected) { struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.i8, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 2); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); } static void duplicate_byte_helper(int command, uint8_t identifier) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 4; /* Proplen = (Identifier + byte)*2 */ payload[1] = identifier; payload[2] = 1; payload[3] = identifier; payload[4] = 0; byte_prop_read_helper(command, payload, 5, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1); } static void bad_byte_helper(int command, uint8_t identifier) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = identifier; payload[2] = 2; /* 0, 1 are only valid values */ byte_prop_read_helper(command, payload, 3, MOSQ_ERR_PROTOCOL, identifier, 0); } static void int32_prop_read_helper( int command, uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t identifier, uint32_t value_expected) { struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); if(rc != rc_expected){ printf("%d / %d\n", rc, rc_expected); } CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.i32, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 5); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); } static void duplicate_int32_helper(int command, uint8_t identifier) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 10; /* Proplen = (Identifier + int32)*2 */ payload[1] = identifier; payload[2] = 1; payload[3] = 1; payload[4] = 1; payload[5] = 1; payload[6] = identifier; payload[7] = 0; payload[8] = 0; payload[9] = 0; payload[10] = 0; int32_prop_read_helper(command, payload, 11, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1); } static void int16_prop_read_helper( int command, uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t identifier, uint16_t value_expected) { struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.i16, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 3); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); } static void duplicate_int16_helper(int command, uint8_t identifier) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 6; /* Proplen = (Identifier + int16)*2 */ payload[1] = identifier; payload[2] = 1; payload[3] = 1; payload[4] = identifier; payload[5] = 0; payload[6] = 0; int16_prop_read_helper(command, payload, 7, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, 1); } static void string_prop_read_helper( int command, uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t identifier, const char *value_expected) { struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(value_expected)); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); } static void duplicate_string_helper(int command, uint8_t identifier) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = identifier; payload[2] = 0; payload[3] = 1; /* 1 length string */ payload[4] = 'h'; payload[5] = identifier; payload[6] = 0; payload[7] = 1; payload[8] = 'h'; string_prop_read_helper(command, payload, 9, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, ""); } static void bad_string_helper(uint8_t identifier) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 6; payload[1] = identifier; payload[2] = 0; payload[3] = 3; /* 1 length string */ payload[4] = 'h'; payload[5] = 0; /* 0 in string not allowed */ payload[6] = 'h'; string_prop_read_helper(CMD_PUBLISH, payload, 7, MOSQ_ERR_MALFORMED_UTF8, identifier, ""); } static void binary_prop_read_helper( int command, uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t identifier, const uint8_t *value_expected, int len_expected) { struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.bin.len, len_expected); CU_ASSERT_EQUAL(memcmp(properties->value.bin.v, value_expected, (size_t)len_expected), 0); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+len_expected); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); } static void duplicate_binary_helper(int command, uint8_t identifier) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = identifier; payload[2] = 0; payload[3] = 1; /* 2 length binary */ payload[4] = 'h'; payload[5] = identifier; payload[6] = 0; payload[7] = 1; payload[8] = 'h'; string_prop_read_helper(command, payload, 9, MOSQ_ERR_DUPLICATE_PROPERTY, identifier, ""); } static void string_pair_prop_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t identifier, const char *name_expected, const char *value_expected, bool expect_multiple) { struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->name.len, strlen(name_expected)); CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); CU_ASSERT_STRING_EQUAL(properties->name.v, name_expected); CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); if(expect_multiple){ CU_ASSERT_PTR_NOT_NULL(properties->next); }else{ CU_ASSERT_PTR_NULL(properties->next); CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(name_expected)+2+strlen(value_expected)); } mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_NULL(properties); } static void varint_prop_read_helper( uint8_t *payload, uint32_t remaining_length, int rc_expected, uint8_t identifier, uint32_t value_expected) { struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = remaining_length; rc = property__read_all(CMD_PUBLISH, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); if(rc != rc_expected){ printf("%d / %d\n", rc, rc_expected); } if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.varint, value_expected); CU_ASSERT_PTR_NULL(properties->next); CU_ASSERT_EQUAL(property__get_length_all(properties), packet__varint_bytes(value_expected)+1); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_NULL(properties); } static void packet_helper_reason_string_user_property(int command) { uint8_t payload[24] = {23, MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(properties); if(properties){ CU_ASSERT_PTR_NOT_NULL(properties->next); p = properties; CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); p = p->next; if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); } mosquitto_property_free_all(&properties); } } /* ======================================================================== * NO PROPERTIES * ======================================================================== */ static void TEST_no_properties(void) { struct mosquitto__packet packet; mosquitto_property *properties = NULL; uint8_t payload[5]; int rc; memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); packet.payload = payload; packet.remaining_length = 1; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_EQUAL(properties, NULL); CU_ASSERT_EQUAL(packet.pos, 1); } static void TEST_truncated(void) { struct mosquitto__packet packet; mosquitto_property *properties = NULL; uint8_t payload[5]; int rc; /* Zero length packet */ memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); packet.payload = payload; packet.remaining_length = 0; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); CU_ASSERT_PTR_EQUAL(properties, NULL); CU_ASSERT_EQUAL(packet.pos, 0); /* Proplen > 0 but not enough data */ memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); payload[0] = 2; packet.payload = payload; packet.remaining_length = 1; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); CU_ASSERT_PTR_EQUAL(properties, NULL); CU_ASSERT_EQUAL(packet.pos, 1); /* Proplen > 0 but not enough data */ memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); payload[0] = 4; payload[1] = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR; packet.payload = payload; packet.remaining_length = 2; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); CU_ASSERT_PTR_EQUAL(properties, NULL); CU_ASSERT_EQUAL(packet.pos, 2); } /* ======================================================================== * INVALID PROPERTY ID * ======================================================================== */ static void TEST_invalid_property_id(void) { struct mosquitto__packet packet; mosquitto_property *properties = NULL; uint8_t payload[5]; int rc; /* ID = 0 */ memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); payload[0] = 4; packet.payload = payload; packet.remaining_length = 2; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); CU_ASSERT_PTR_EQUAL(properties, NULL); CU_ASSERT_EQUAL(packet.pos, 2); /* ID = 4 */ memset(&packet, 0, sizeof(struct mosquitto__packet)); memset(payload, 0, sizeof(payload)); payload[0] = 4; payload[1] = 4; packet.payload = payload; packet.remaining_length = 2; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_MALFORMED_PACKET); CU_ASSERT_PTR_EQUAL(properties, NULL); CU_ASSERT_EQUAL(packet.pos, 2); } /* ======================================================================== * SINGLE PROPERTIES * ======================================================================== */ static void TEST_single_payload_format_indicator(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_PAYLOAD_FORMAT_INDICATOR; payload[2] = 1; byte_prop_read_helper(CMD_PUBLISH, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); } static void TEST_single_request_problem_information(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_REQUEST_PROBLEM_INFORMATION; payload[2] = 1; byte_prop_read_helper(CMD_CONNECT, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); } static void TEST_single_request_response_information(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_REQUEST_RESPONSE_INFORMATION; payload[2] = 1; byte_prop_read_helper(CMD_CONNECT, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); } static void TEST_single_maximum_qos(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_MAXIMUM_QOS; payload[2] = 1; byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, 1); } static void TEST_single_retain_available(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_RETAIN_AVAILABLE; payload[2] = 1; byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, 1); } static void TEST_single_wildcard_subscription_available(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_WILDCARD_SUB_AVAILABLE; payload[2] = 0; byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); } static void TEST_single_subscription_identifier_available(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE; payload[2] = 0; byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); } static void TEST_single_shared_subscription_available(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 2; /* Proplen = Identifier + byte */ payload[1] = MQTT_PROP_SHARED_SUB_AVAILABLE; payload[2] = 1; byte_prop_read_helper(CMD_CONNACK, payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, 1); } static void TEST_single_message_expiry_interval(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 5; /* Proplen = Identifier + int32 */ payload[1] = MQTT_PROP_MESSAGE_EXPIRY_INTERVAL; payload[2] = 0x12; payload[3] = 0x23; payload[4] = 0x34; payload[5] = 0x45; int32_prop_read_helper(CMD_WILL, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12233445); } static void TEST_single_session_expiry_interval(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 5; /* Proplen = Identifier + int32 */ payload[1] = MQTT_PROP_SESSION_EXPIRY_INTERVAL; payload[2] = 0x45; payload[3] = 0x34; payload[4] = 0x23; payload[5] = 0x12; int32_prop_read_helper(CMD_CONNACK, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x45342312); } static void TEST_single_will_delay_interval(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 5; /* Proplen = Identifier + int32 */ payload[1] = MQTT_PROP_WILL_DELAY_INTERVAL; payload[2] = 0x45; payload[3] = 0x34; payload[4] = 0x23; payload[5] = 0x12; int32_prop_read_helper(CMD_WILL, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, 0x45342312); } static void TEST_single_maximum_packet_size(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 5; /* Proplen = Identifier + int32 */ payload[1] = MQTT_PROP_MAXIMUM_PACKET_SIZE; payload[2] = 0x45; payload[3] = 0x34; payload[4] = 0x23; payload[5] = 0x12; int32_prop_read_helper(CMD_CONNECT, payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x45342312); } static void TEST_single_server_keep_alive(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 3; /* Proplen = Identifier + int16 */ payload[1] = MQTT_PROP_SERVER_KEEP_ALIVE; payload[2] = 0x45; payload[3] = 0x34; int16_prop_read_helper(CMD_CONNACK, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, 0x4534); } static void TEST_single_receive_maximum(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 3; /* Proplen = Identifier + int16 */ payload[1] = MQTT_PROP_RECEIVE_MAXIMUM; payload[2] = 0x68; payload[3] = 0x42; int16_prop_read_helper(CMD_CONNACK, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, 0x6842); } static void TEST_single_topic_alias_maximum(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 3; /* Proplen = Identifier + int16 */ payload[1] = MQTT_PROP_TOPIC_ALIAS_MAXIMUM; payload[2] = 0x68; payload[3] = 0x42; int16_prop_read_helper(CMD_CONNECT, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x6842); } static void TEST_single_topic_alias(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 3; /* Proplen = Identifier + int16 */ payload[1] = MQTT_PROP_TOPIC_ALIAS; payload[2] = 0x68; payload[3] = 0x42; int16_prop_read_helper(CMD_PUBLISH, payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, 0x6842); } static void TEST_single_content_type(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_CONTENT_TYPE; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 'h'; payload[5] = 'e'; payload[6] = 'l'; payload[7] = 'l'; payload[8] = 'o'; string_prop_read_helper(CMD_PUBLISH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, "hello"); } static void TEST_single_response_topic(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_RESPONSE_TOPIC; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 'h'; payload[5] = 'e'; payload[6] = 'l'; payload[7] = 'l'; payload[8] = 'o'; string_prop_read_helper(CMD_WILL, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, "hello"); } static void TEST_single_assigned_client_identifier(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 'h'; payload[5] = 'e'; payload[6] = 'l'; payload[7] = 'l'; payload[8] = 'o'; string_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "hello"); } static void TEST_single_authentication_method(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_AUTHENTICATION_METHOD; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 'h'; payload[5] = 'e'; payload[6] = 'l'; payload[7] = 'l'; payload[8] = 'o'; string_prop_read_helper(CMD_AUTH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, "hello"); } static void TEST_single_response_information(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_RESPONSE_INFORMATION; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 'h'; payload[5] = 'e'; payload[6] = 'l'; payload[7] = 'l'; payload[8] = 'o'; string_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, "hello"); } static void TEST_single_server_reference(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_SERVER_REFERENCE; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 'h'; payload[5] = 'e'; payload[6] = 'l'; payload[7] = 'l'; payload[8] = 'o'; string_prop_read_helper(CMD_CONNACK, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, "hello"); } static void TEST_single_reason_string(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_REASON_STRING; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 'h'; payload[5] = 'e'; payload[6] = 'l'; payload[7] = 'l'; payload[8] = 'o'; string_prop_read_helper(CMD_PUBCOMP, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, "hello"); } static void TEST_single_correlation_data(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_CORRELATION_DATA; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 1; payload[5] = 'e'; payload[6] = 0; payload[7] = 'l'; payload[8] = 9; binary_prop_read_helper(CMD_PUBLISH, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, &payload[4], 5); } static void TEST_single_authentication_data(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 8; payload[1] = MQTT_PROP_AUTHENTICATION_DATA; payload[2] = 0x00; payload[3] = 0x05; payload[4] = 1; payload[5] = 'e'; payload[6] = 0; payload[7] = 'l'; payload[8] = 9; binary_prop_read_helper(CMD_CONNECT, payload, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, &payload[4], 5); } static void TEST_single_user_property(void) { uint8_t payload[20]; payload[0] = 9; payload[1] = MQTT_PROP_USER_PROPERTY; payload[2] = 0; payload[3] = 2; payload[4] = 'z'; payload[5] = 'a'; payload[6] = 0; payload[7] = 2; payload[8] = 'b'; payload[9] = 'c'; string_pair_prop_read_helper(payload, 10, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, "za", "bc", false); } static void TEST_single_subscription_identifier(void) { uint8_t payload[20]; payload[0] = 2; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0; varint_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); payload[0] = 2; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0x7F; varint_prop_read_helper(payload, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 127); payload[0] = 3; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0x80; payload[3] = 0x01; varint_prop_read_helper(payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 128); payload[0] = 3; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0xFF; payload[3] = 0x7F; varint_prop_read_helper(payload, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16383); payload[0] = 4; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0x80; payload[3] = 0x80; payload[4] = 0x01; varint_prop_read_helper(payload, 5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16384); payload[0] = 4; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0xFF; payload[3] = 0xFF; payload[4] = 0x7F; varint_prop_read_helper(payload, 5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097151); payload[0] = 5; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0x80; payload[3] = 0x80; payload[4] = 0x80; payload[5] = 0x01; varint_prop_read_helper(payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097152); payload[0] = 5; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0xFF; payload[3] = 0xFF; payload[4] = 0xFF; payload[5] = 0x7F; varint_prop_read_helper(payload, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 268435455); } /* ======================================================================== * DUPLICATE PROPERTIES * ======================================================================== */ static void TEST_duplicate_payload_format_indicator(void) { duplicate_byte_helper(CMD_PUBLISH, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); } static void TEST_duplicate_request_problem_information(void) { duplicate_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_PROBLEM_INFORMATION); } static void TEST_duplicate_request_response_information(void) { duplicate_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_RESPONSE_INFORMATION); } static void TEST_duplicate_maximum_qos(void) { duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_MAXIMUM_QOS); } static void TEST_duplicate_retain_available(void) { duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_RETAIN_AVAILABLE); } static void TEST_duplicate_wildcard_subscription_available(void) { duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_WILDCARD_SUB_AVAILABLE); } static void TEST_duplicate_subscription_identifier_available(void) { duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); } static void TEST_duplicate_shared_subscription_available(void) { duplicate_byte_helper(CMD_CONNACK, MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_duplicate_message_expiry_interval(void) { duplicate_int32_helper(CMD_PUBLISH, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); } static void TEST_duplicate_session_expiry_interval(void) { duplicate_int32_helper(CMD_DISCONNECT, MQTT_PROP_SESSION_EXPIRY_INTERVAL); } static void TEST_duplicate_will_delay_interval(void) { duplicate_int32_helper(CMD_WILL, MQTT_PROP_WILL_DELAY_INTERVAL); } static void TEST_duplicate_maximum_packet_size(void) { duplicate_int32_helper(CMD_CONNECT, MQTT_PROP_MAXIMUM_PACKET_SIZE); } static void TEST_duplicate_server_keep_alive(void) { duplicate_int16_helper(CMD_CONNACK, MQTT_PROP_SERVER_KEEP_ALIVE); } static void TEST_duplicate_receive_maximum(void) { duplicate_int16_helper(CMD_CONNACK, MQTT_PROP_RECEIVE_MAXIMUM); } static void TEST_duplicate_topic_alias_maximum(void) { duplicate_int16_helper(CMD_CONNECT, MQTT_PROP_TOPIC_ALIAS_MAXIMUM); } static void TEST_duplicate_topic_alias(void) { duplicate_int16_helper(CMD_PUBLISH, MQTT_PROP_TOPIC_ALIAS); } static void TEST_duplicate_content_type(void) { duplicate_string_helper(CMD_PUBLISH, MQTT_PROP_CONTENT_TYPE); } static void TEST_duplicate_response_topic(void) { duplicate_string_helper(CMD_PUBLISH, MQTT_PROP_RESPONSE_TOPIC); } static void TEST_duplicate_assigned_client_identifier(void) { duplicate_string_helper(CMD_CONNACK, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); } static void TEST_duplicate_authentication_method(void) { duplicate_string_helper(CMD_AUTH, MQTT_PROP_AUTHENTICATION_METHOD); } static void TEST_duplicate_response_information(void) { duplicate_string_helper(CMD_CONNACK, MQTT_PROP_RESPONSE_INFORMATION); } static void TEST_duplicate_server_reference(void) { duplicate_string_helper(CMD_CONNACK, MQTT_PROP_SERVER_REFERENCE); } static void TEST_duplicate_reason_string(void) { duplicate_string_helper(CMD_PUBACK, MQTT_PROP_REASON_STRING); } static void TEST_duplicate_correlation_data(void) { duplicate_binary_helper(CMD_PUBLISH, MQTT_PROP_CORRELATION_DATA); } static void TEST_duplicate_authentication_data(void) { duplicate_binary_helper(CMD_CONNACK, MQTT_PROP_AUTHENTICATION_DATA); } static void TEST_duplicate_user_property(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 18; /* Proplen = (Identifier + byte)*2 */ payload[1] = MQTT_PROP_USER_PROPERTY; payload[2] = 0; payload[3] = 2; payload[4] = 'a'; payload[5] = 'b'; payload[6] = 0; payload[7] = 2; payload[8] = 'g'; payload[9] = 'h'; payload[10] = MQTT_PROP_USER_PROPERTY; payload[11] = 0; payload[12] = 2; payload[13] = 'c'; payload[14] = 'd'; payload[15] = 0; payload[16] = 2; payload[17] = 'e'; payload[18] = 'f'; string_pair_prop_read_helper(payload, 19, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, "ab", "gh", true); } static void TEST_duplicate_subscription_identifier(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 4; /* Proplen = (Identifier + byte)*2 */ payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0x80; payload[3] = 0x02; payload[4] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[5] = 0x04; varint_prop_read_helper(payload, 5, MOSQ_ERR_MALFORMED_PACKET, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); } /* ======================================================================== * BAD PROPERTY VALUES * ======================================================================== */ static void TEST_bad_request_problem_information(void) { bad_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_PROBLEM_INFORMATION); } static void TEST_bad_request_response_information(void) { bad_byte_helper(CMD_CONNECT, MQTT_PROP_REQUEST_RESPONSE_INFORMATION); } static void TEST_bad_maximum_qos(void) { bad_byte_helper(CMD_CONNACK, MQTT_PROP_MAXIMUM_QOS); } static void TEST_bad_retain_available(void) { bad_byte_helper(CMD_CONNACK, MQTT_PROP_RETAIN_AVAILABLE); } static void TEST_bad_wildcard_sub_available(void) { bad_byte_helper(CMD_CONNACK, MQTT_PROP_WILDCARD_SUB_AVAILABLE); } static void TEST_bad_subscription_id_available(void) { bad_byte_helper(CMD_CONNACK, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); } static void TEST_bad_shared_sub_available(void) { bad_byte_helper(CMD_CONNACK, MQTT_PROP_SHARED_SUB_AVAILABLE); } static void TEST_bad_maximum_packet_size(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 5; /* Proplen = Identifier + int32 */ payload[1] = MQTT_PROP_MAXIMUM_PACKET_SIZE; payload[2] = 0; payload[3] = 0; payload[4] = 0; payload[5] = 0; /* 0 is invalid */ int32_prop_read_helper(CMD_CONNACK, payload, 6, MOSQ_ERR_PROTOCOL, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0); } static void TEST_bad_receive_maximum(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 3; /* Proplen = Identifier + int16 */ payload[1] = MQTT_PROP_RECEIVE_MAXIMUM; payload[2] = 0; payload[3] = 0; /* 0 is invalid */ int32_prop_read_helper(CMD_CONNECT, payload, 4, MOSQ_ERR_PROTOCOL, MQTT_PROP_RECEIVE_MAXIMUM, 0); } static void TEST_bad_topic_alias(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 3; /* Proplen = Identifier + int16 */ payload[1] = MQTT_PROP_TOPIC_ALIAS; payload[2] = 0; payload[3] = 0; /* 0 is invalid */ int32_prop_read_helper(CMD_PUBLISH, payload, 4, MOSQ_ERR_PROTOCOL, MQTT_PROP_TOPIC_ALIAS, 0); } static void TEST_bad_content_type(void) { bad_string_helper(MQTT_PROP_CONTENT_TYPE); } static void TEST_bad_subscription_identifier(void) { uint8_t payload[20]; memset(&payload, 0, sizeof(payload)); payload[0] = 6; payload[1] = MQTT_PROP_SUBSCRIPTION_IDENTIFIER; payload[2] = 0xFF; payload[3] = 0xFF; payload[4] = 0xFF; payload[5] = 0xFF; payload[6] = 0x01; varint_prop_read_helper(payload, 7, MOSQ_ERR_MALFORMED_PACKET, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); } /* ======================================================================== * CONTROL PACKET TESTS * ======================================================================== */ static void TEST_packet_connect(void) { uint8_t payload[] = {0, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, MQTT_PROP_RECEIVE_MAXIMUM, 0x00, 0x05, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x12, 0x45, 0x00, 0x00, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x00, 0x02, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1, MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', MQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e', MQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; payload[0] = sizeof(payload)-1; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); p = properties; CU_ASSERT_PTR_NOT_NULL(properties); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL); CU_ASSERT_EQUAL(p->value.i32, 0x12450000); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RECEIVE_MAXIMUM); CU_ASSERT_EQUAL(p->value.i16, 0x0005); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_PACKET_SIZE); CU_ASSERT_EQUAL(p->value.i32, 0x12450000); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS_MAXIMUM); CU_ASSERT_EQUAL(p->value.i16, 0x0002); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REQUEST_PROBLEM_INFORMATION); CU_ASSERT_EQUAL(p->value.i8, 1); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REQUEST_RESPONSE_INFORMATION); CU_ASSERT_EQUAL(p->value.i8, 1); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD); CU_ASSERT_STRING_EQUAL(p->value.s.v, "none"); CU_ASSERT_EQUAL(p->value.s.len, strlen("none")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA); CU_ASSERT_EQUAL(p->value.bin.v[0], 1); CU_ASSERT_EQUAL(p->value.bin.v[1], 2); CU_ASSERT_EQUAL(p->value.s.len, 2); } } } } } } } } } mosquitto_property_free_all(&properties); } static void TEST_packet_connack(void) { uint8_t payload[] = {0, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, MQTT_PROP_RECEIVE_MAXIMUM, 0x00, 0x05, MQTT_PROP_MAXIMUM_QOS, 1, MQTT_PROP_RETAIN_AVAILABLE, 0, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x12, 0x45, 0x00, 0x00, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, 0x00, 0x02, 'a', 'b', MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x00, 0x02, MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0, MQTT_PROP_SHARED_SUB_AVAILABLE, 0, MQTT_PROP_SERVER_KEEP_ALIVE, 0x00, 0xFF, MQTT_PROP_RESPONSE_INFORMATION, 0x00, 0x03, 'r', 's', 'p', MQTT_PROP_SERVER_REFERENCE, 0x00, 0x04, 's', 'e', 'r', 'v', MQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e', MQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; payload[0] = sizeof(payload)-1; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(CMD_CONNACK, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(properties); p = properties; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL); CU_ASSERT_EQUAL(p->value.i32, 0x12450000); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RECEIVE_MAXIMUM); CU_ASSERT_EQUAL(p->value.i16, 0x0005); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_QOS); CU_ASSERT_EQUAL(p->value.i8, 1); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RETAIN_AVAILABLE); CU_ASSERT_EQUAL(p->value.i8, 0); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MAXIMUM_PACKET_SIZE); CU_ASSERT_EQUAL(p->value.i32, 0x12450000); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER); CU_ASSERT_STRING_EQUAL(p->value.s.v, "ab"); CU_ASSERT_EQUAL(p->value.s.len, strlen("ab")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS_MAXIMUM); CU_ASSERT_EQUAL(p->value.i16, 0x0002); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_WILDCARD_SUB_AVAILABLE); CU_ASSERT_EQUAL(p->value.i8, 0); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE); CU_ASSERT_EQUAL(p->value.i8, 0); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SHARED_SUB_AVAILABLE); CU_ASSERT_EQUAL(p->value.i8, 0); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SERVER_KEEP_ALIVE); CU_ASSERT_EQUAL(p->value.i16, 0x00FF); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RESPONSE_INFORMATION); CU_ASSERT_STRING_EQUAL(p->value.s.v, "rsp"); CU_ASSERT_EQUAL(p->value.s.len, strlen("rsp")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SERVER_REFERENCE); CU_ASSERT_STRING_EQUAL(p->value.s.v, "serv"); CU_ASSERT_EQUAL(p->value.s.len, strlen("serv")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD); CU_ASSERT_STRING_EQUAL(p->value.s.v, "none"); CU_ASSERT_EQUAL(p->value.s.len, strlen("none")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA); CU_ASSERT_EQUAL(p->value.bin.v[0], 1); CU_ASSERT_EQUAL(p->value.bin.v[1], 2); CU_ASSERT_EQUAL(p->value.s.len, 2); } } } } } } } } } } } } } } } } } mosquitto_property_free_all(&properties); } static void TEST_packet_publish(void) { uint8_t payload[] = {0, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, MQTT_PROP_TOPIC_ALIAS, 0x00, 0x02, MQTT_PROP_RESPONSE_TOPIC, 0, 6, 'r', 'e', 's', 'p', 'o', 'n', MQTT_PROP_CORRELATION_DATA, 0x00, 0x02, 1, 2, MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0x04, MQTT_PROP_CONTENT_TYPE, 0, 5, 'e', 'm', 'p', 't', 'y'}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; payload[0] = sizeof(payload)-1; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(CMD_PUBLISH, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); p = properties; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR); CU_ASSERT_EQUAL(p->value.i8, 1); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL); CU_ASSERT_EQUAL(p->value.i32, 0x12450000); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_TOPIC_ALIAS); CU_ASSERT_EQUAL(p->value.i16, 0x0002); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_RESPONSE_TOPIC); CU_ASSERT_STRING_EQUAL(p->value.s.v, "respon"); CU_ASSERT_EQUAL(p->value.s.len, strlen("respon")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_CORRELATION_DATA); CU_ASSERT_EQUAL(p->value.bin.v[0], 1); CU_ASSERT_EQUAL(p->value.bin.v[1], 2); CU_ASSERT_EQUAL(p->value.bin.len, 2); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_IDENTIFIER); CU_ASSERT_EQUAL(p->value.varint, 0x00000004); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_CONTENT_TYPE); CU_ASSERT_STRING_EQUAL(p->value.s.v, "empty"); CU_ASSERT_EQUAL(p->value.s.len, strlen("empty")); } } } } } } } } mosquitto_property_free_all(&properties); } static void TEST_packet_puback(void) { packet_helper_reason_string_user_property(CMD_PUBACK); } static void TEST_packet_pubrec(void) { packet_helper_reason_string_user_property(CMD_PUBREC); } static void TEST_packet_pubrel(void) { packet_helper_reason_string_user_property(CMD_PUBREL); } static void TEST_packet_pubcomp(void) { packet_helper_reason_string_user_property(CMD_PUBCOMP); } static void TEST_packet_subscribe(void) { uint8_t payload[] = {0, MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e', MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0x04}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; payload[0] = sizeof(payload)-1; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(CMD_SUBSCRIBE, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); p = properties; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SUBSCRIPTION_IDENTIFIER); CU_ASSERT_EQUAL(p->value.varint, 0x00000004); } } mosquitto_property_free_all(&properties); } static void TEST_packet_suback(void) { packet_helper_reason_string_user_property(CMD_SUBACK); } static void TEST_packet_unsubscribe(void) { uint8_t payload[] = {0, MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; payload[0] = sizeof(payload)-1; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(CMD_UNSUBSCRIBE, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); p = properties; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); } mosquitto_property_free_all(&properties); } static void TEST_packet_unsuback(void) { packet_helper_reason_string_user_property(CMD_UNSUBACK); } static void TEST_packet_disconnect(void) { uint8_t payload[] = {0, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x12, 0x45, 0x00, 0x00, MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; payload[0] = sizeof(payload)-1; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(CMD_DISCONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); p = properties; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_SESSION_EXPIRY_INTERVAL); CU_ASSERT_EQUAL(p->value.i32, 0x12450000); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); } } } mosquitto_property_free_all(&properties); } static void TEST_packet_auth(void) { uint8_t payload[] = {0, MQTT_PROP_AUTHENTICATION_METHOD, 0x00, 0x04, 'n', 'o', 'n', 'e', MQTT_PROP_AUTHENTICATION_DATA, 0x00, 0x02, 1, 2, MQTT_PROP_REASON_STRING, 0, 6, 'r', 'e', 'a', 's', 'o', 'n', MQTT_PROP_USER_PROPERTY, 0, 4, 'n', 'a', 'm', 'e', 0, 5, 'v', 'a', 'l', 'u', 'e'}; struct mosquitto__packet packet; mosquitto_property *properties, *p; int rc; payload[0] = sizeof(payload)-1; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.payload = payload; packet.remaining_length = sizeof(payload);; rc = property__read_all(CMD_AUTH, &packet, &properties); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); p = properties; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_METHOD); CU_ASSERT_STRING_EQUAL(p->value.s.v, "none"); CU_ASSERT_EQUAL(p->value.s.len, strlen("none")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_AUTHENTICATION_DATA); CU_ASSERT_EQUAL(p->value.bin.v[0], 1); CU_ASSERT_EQUAL(p->value.bin.v[1], 2); CU_ASSERT_EQUAL(p->value.s.len, 2); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NOT_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_REASON_STRING); CU_ASSERT_STRING_EQUAL(p->value.s.v, "reason"); CU_ASSERT_EQUAL(p->value.s.len, strlen("reason")); p = p->next; CU_ASSERT_PTR_NOT_NULL(p); if(p){ CU_ASSERT_PTR_NULL(p->next); CU_ASSERT_EQUAL(p->identifier, MQTT_PROP_USER_PROPERTY); CU_ASSERT_STRING_EQUAL(p->value.s.v, "value"); CU_ASSERT_EQUAL(p->value.s.len, strlen("value")); CU_ASSERT_STRING_EQUAL(p->name.v, "name"); CU_ASSERT_EQUAL(p->name.len, strlen("name")); } } } } mosquitto_property_free_all(&properties); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_property_read_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Property read", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Property read test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Truncated packet", TEST_truncated) || !CU_add_test(test_suite, "Invalid property ID", TEST_invalid_property_id) || !CU_add_test(test_suite, "No properties", TEST_no_properties) || !CU_add_test(test_suite, "Single Payload Format Indicator", TEST_single_payload_format_indicator) || !CU_add_test(test_suite, "Single Request Problem Information", TEST_single_request_problem_information) || !CU_add_test(test_suite, "Single Request Response Information", TEST_single_request_response_information) || !CU_add_test(test_suite, "Single Maximum QoS", TEST_single_maximum_qos) || !CU_add_test(test_suite, "Single Retain Available", TEST_single_retain_available) || !CU_add_test(test_suite, "Single Wildcard Subscription Available", TEST_single_wildcard_subscription_available) || !CU_add_test(test_suite, "Single Subscription Identifier Available", TEST_single_subscription_identifier_available) || !CU_add_test(test_suite, "Single Shared Subscription Available", TEST_single_shared_subscription_available) || !CU_add_test(test_suite, "Single Message Expiry Interval", TEST_single_message_expiry_interval) || !CU_add_test(test_suite, "Single Session Expiry Interval", TEST_single_session_expiry_interval) || !CU_add_test(test_suite, "Single Will Delay Interval", TEST_single_will_delay_interval) || !CU_add_test(test_suite, "Single Maximum Packet Size", TEST_single_maximum_packet_size) || !CU_add_test(test_suite, "Single Server Keep Alive", TEST_single_server_keep_alive) || !CU_add_test(test_suite, "Single Receive Maximum", TEST_single_receive_maximum) || !CU_add_test(test_suite, "Single Topic Alias Maximum", TEST_single_topic_alias_maximum) || !CU_add_test(test_suite, "Single Topic Alias", TEST_single_topic_alias) || !CU_add_test(test_suite, "Single Content Type", TEST_single_content_type) || !CU_add_test(test_suite, "Single Response Topic", TEST_single_response_topic) || !CU_add_test(test_suite, "Single Assigned Client Identifier", TEST_single_assigned_client_identifier) || !CU_add_test(test_suite, "Single Authentication Method", TEST_single_authentication_method) || !CU_add_test(test_suite, "Single Response Information", TEST_single_response_information) || !CU_add_test(test_suite, "Single Server Reference", TEST_single_server_reference) || !CU_add_test(test_suite, "Single Reason String", TEST_single_reason_string) || !CU_add_test(test_suite, "Single Correlation Data", TEST_single_correlation_data) || !CU_add_test(test_suite, "Single Authentication Data", TEST_single_authentication_data) || !CU_add_test(test_suite, "Single User Property", TEST_single_user_property) || !CU_add_test(test_suite, "Single Subscription Identifier", TEST_single_subscription_identifier) || !CU_add_test(test_suite, "Duplicate Payload Format Indicator", TEST_duplicate_payload_format_indicator) || !CU_add_test(test_suite, "Duplicate Request Problem Information", TEST_duplicate_request_problem_information) || !CU_add_test(test_suite, "Duplicate Request Response Information", TEST_duplicate_request_response_information) || !CU_add_test(test_suite, "Duplicate Maximum QoS", TEST_duplicate_maximum_qos) || !CU_add_test(test_suite, "Duplicate Retain Available", TEST_duplicate_retain_available) || !CU_add_test(test_suite, "Duplicate Wildcard Subscription Available", TEST_duplicate_wildcard_subscription_available) || !CU_add_test(test_suite, "Duplicate Subscription Identifier Available", TEST_duplicate_subscription_identifier_available) || !CU_add_test(test_suite, "Duplicate Shared Subscription Available", TEST_duplicate_shared_subscription_available) || !CU_add_test(test_suite, "Duplicate Message Expiry Interval", TEST_duplicate_message_expiry_interval) || !CU_add_test(test_suite, "Duplicate Session Expiry Interval", TEST_duplicate_session_expiry_interval) || !CU_add_test(test_suite, "Duplicate Will Delay Interval", TEST_duplicate_will_delay_interval) || !CU_add_test(test_suite, "Duplicate Maximum Packet Size", TEST_duplicate_maximum_packet_size) || !CU_add_test(test_suite, "Duplicate Server Keep Alive", TEST_duplicate_server_keep_alive) || !CU_add_test(test_suite, "Duplicate Receive Maximum", TEST_duplicate_receive_maximum) || !CU_add_test(test_suite, "Duplicate Topic Alias Maximum", TEST_duplicate_topic_alias_maximum) || !CU_add_test(test_suite, "Duplicate Topic Alias", TEST_duplicate_topic_alias) || !CU_add_test(test_suite, "Duplicate Content Type", TEST_duplicate_content_type) || !CU_add_test(test_suite, "Duplicate Response Topic", TEST_duplicate_response_topic) || !CU_add_test(test_suite, "Duplicate Assigned Client ID", TEST_duplicate_assigned_client_identifier) || !CU_add_test(test_suite, "Duplicate Authentication Method", TEST_duplicate_authentication_method) || !CU_add_test(test_suite, "Duplicate Response Information", TEST_duplicate_response_information) || !CU_add_test(test_suite, "Duplicate Server Reference", TEST_duplicate_server_reference) || !CU_add_test(test_suite, "Duplicate Reason String", TEST_duplicate_reason_string) || !CU_add_test(test_suite, "Duplicate Correlation Data", TEST_duplicate_correlation_data) || !CU_add_test(test_suite, "Duplicate Authentication Data", TEST_duplicate_authentication_data) || !CU_add_test(test_suite, "Duplicate User Property", TEST_duplicate_user_property) || !CU_add_test(test_suite, "Duplicate Subscription Identifier", TEST_duplicate_subscription_identifier) || !CU_add_test(test_suite, "Bad Request Problem Information", TEST_bad_request_problem_information) || !CU_add_test(test_suite, "Bad Request Response Information", TEST_bad_request_response_information) || !CU_add_test(test_suite, "Bad Maximum QoS", TEST_bad_maximum_qos) || !CU_add_test(test_suite, "Bad Retain Available", TEST_bad_retain_available) || !CU_add_test(test_suite, "Bad Wildcard Subscription Available", TEST_bad_wildcard_sub_available) || !CU_add_test(test_suite, "Bad Subscription Identifier Available", TEST_bad_subscription_id_available) || !CU_add_test(test_suite, "Bad Shared Subscription Available", TEST_bad_shared_sub_available) || !CU_add_test(test_suite, "Bad Maximum Packet Size", TEST_bad_maximum_packet_size) || !CU_add_test(test_suite, "Bad Receive Maximum", TEST_bad_receive_maximum) || !CU_add_test(test_suite, "Bad Topic Alias", TEST_bad_topic_alias) || !CU_add_test(test_suite, "Bad Content Type", TEST_bad_content_type) || !CU_add_test(test_suite, "Bad Subscription Identifier", TEST_bad_subscription_identifier) || !CU_add_test(test_suite, "Packet CONNECT", TEST_packet_connect) || !CU_add_test(test_suite, "Packet CONNACK", TEST_packet_connack) || !CU_add_test(test_suite, "Packet PUBLISH", TEST_packet_publish) || !CU_add_test(test_suite, "Packet PUBACK", TEST_packet_puback) || !CU_add_test(test_suite, "Packet PUBREC", TEST_packet_pubrec) || !CU_add_test(test_suite, "Packet PUBREL", TEST_packet_pubrel) || !CU_add_test(test_suite, "Packet PUBCOMP", TEST_packet_pubcomp) || !CU_add_test(test_suite, "Packet SUBSCRIBE", TEST_packet_subscribe) || !CU_add_test(test_suite, "Packet SUBACK", TEST_packet_suback) || !CU_add_test(test_suite, "Packet UNSUBSCRIBE", TEST_packet_unsubscribe) || !CU_add_test(test_suite, "Packet UNSUBACK", TEST_packet_unsuback) || !CU_add_test(test_suite, "Packet DISCONNECT", TEST_packet_disconnect) || !CU_add_test(test_suite, "Packet AUTH", TEST_packet_auth) ){ printf("Error adding Property read CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/property_user_read.c000066400000000000000000000634151450213760600217020ustar00rootroot00000000000000#include #include #include "mqtt_protocol.h" #include "property_mosq.h" #include "packet_mosq.h" static void generate_full_proplist(mosquitto_property **proplist) { int rc; /* This isn't a valid proplist for sending, because it contains every * property. Very useful for testing though. */ rc = mosquitto_property_add_byte(proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string(proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_binary(proplist, MQTT_PROP_CORRELATION_DATA, "correlation-data", strlen("correlation-data")); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_varint(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int32(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 180); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string(proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string(proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string(proplist, MQTT_PROP_REASON_STRING, "reason"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS, 15); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_byte(proplist, MQTT_PROP_MAXIMUM_QOS, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_byte(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_string_pair(proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_byte(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); if(rc != MOSQ_ERR_SUCCESS) return; rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); } static void generate_partial_proplist(mosquitto_property **proplist) { int rc; // BYTE MISSING: MQTT_PROP_PAYLOAD_FORMAT_INDICATOR rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); // STRING MISSING: MQTT_PROP_CONTENT_TYPE rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); // BINARY MISSING: MQTT_PROP_CORRELATION_DATA // VARINT MISSING: MQTT_PROP_SUBSCRIPTION_IDENTIFIER // INT32 MISSING: MQTT_PROP_SESSION_EXPIRY_INTERVAL rc = mosquitto_property_add_string(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); // INT16 MISSING: MQTT_PROP_SERVER_KEEP_ALIVE rc = mosquitto_property_add_string(proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_byte(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_string(proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_string(proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_string(proplist, MQTT_PROP_REASON_STRING, "reason"); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_int16(proplist, MQTT_PROP_TOPIC_ALIAS, 15); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_byte(proplist, MQTT_PROP_MAXIMUM_QOS, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_byte(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); // STRING PAIR MISSING: MQTT_PROP_USER_PROPERTY rc = mosquitto_property_add_int32(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_byte(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); rc = mosquitto_property_add_byte(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); } /* ======================================================================== * SINGLE READ * ======================================================================== */ static void read_byte_helper(const mosquitto_property *proplist, int identifier, uint8_t expected_value) { const mosquitto_property *prop; uint8_t value; prop = mosquitto_property_read_byte(proplist, identifier, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(value, expected_value); } static void read_int16_helper(const mosquitto_property *proplist, int identifier, uint16_t expected_value) { const mosquitto_property *prop; uint16_t value; prop = mosquitto_property_read_int16(proplist, identifier, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(value, expected_value); } static void read_int32_helper(const mosquitto_property *proplist, int identifier, uint32_t expected_value) { const mosquitto_property *prop; uint32_t value; prop = mosquitto_property_read_int32(proplist, identifier, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(value, expected_value); } static void read_varint_helper(const mosquitto_property *proplist, int identifier, uint32_t expected_value) { const mosquitto_property *prop; uint32_t value; prop = mosquitto_property_read_varint(proplist, identifier, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(value, expected_value); } static void read_binary_helper(const mosquitto_property *proplist, int identifier, void *expected_value, uint16_t expected_length) { const mosquitto_property *prop; void *value = NULL; uint16_t length; prop = mosquitto_property_read_binary(proplist, identifier, &value, &length, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(length, expected_length); CU_ASSERT_PTR_NOT_NULL(value); if(value){ CU_ASSERT_NSTRING_EQUAL(value, expected_value, expected_length); } free(value); } static void read_string_helper(const mosquitto_property *proplist, int identifier, char *expected_value) { const mosquitto_property *prop; char *value = NULL; prop = mosquitto_property_read_string(proplist, identifier, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_PTR_NOT_NULL(value); if(value){ CU_ASSERT_STRING_EQUAL(value, expected_value); } free(value); } static void read_string_pair_helper(const mosquitto_property *proplist, int identifier, char *expected_key, char *expected_value) { const mosquitto_property *prop; char *key = NULL, *value = NULL; prop = mosquitto_property_read_string_pair(proplist, identifier, &key, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_PTR_NOT_NULL(key); if(key){ CU_ASSERT_STRING_EQUAL(key, expected_key); } CU_ASSERT_PTR_NOT_NULL(value); if(value){ CU_ASSERT_STRING_EQUAL(value, expected_value); } free(key); free(value); } static void TEST_read_single_byte(void) { int rc; mosquitto_property *proplist = NULL, *proplist_copy = NULL; generate_full_proplist(&proplist); if(!proplist) return; read_byte_helper(proplist, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); read_byte_helper(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); read_byte_helper(proplist, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); read_byte_helper(proplist, MQTT_PROP_MAXIMUM_QOS, 0); read_byte_helper(proplist, MQTT_PROP_RETAIN_AVAILABLE, 0); read_byte_helper(proplist, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); read_byte_helper(proplist, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); read_byte_helper(proplist, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); read_byte_helper(proplist_copy, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); read_byte_helper(proplist_copy, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); read_byte_helper(proplist_copy, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); read_byte_helper(proplist_copy, MQTT_PROP_MAXIMUM_QOS, 0); read_byte_helper(proplist_copy, MQTT_PROP_RETAIN_AVAILABLE, 0); read_byte_helper(proplist_copy, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); read_byte_helper(proplist_copy, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); read_byte_helper(proplist_copy, MQTT_PROP_SHARED_SUB_AVAILABLE, 0); mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } static void TEST_read_single_int16(void) { int rc; mosquitto_property *proplist = NULL, *proplist_copy = NULL; generate_full_proplist(&proplist); if(!proplist) return; read_int16_helper(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, 180); read_int16_helper(proplist, MQTT_PROP_RECEIVE_MAXIMUM, 1024); read_int16_helper(proplist, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); read_int16_helper(proplist, MQTT_PROP_TOPIC_ALIAS, 15); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); read_int16_helper(proplist_copy, MQTT_PROP_SERVER_KEEP_ALIVE, 180); read_int16_helper(proplist_copy, MQTT_PROP_RECEIVE_MAXIMUM, 1024); read_int16_helper(proplist_copy, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 64); read_int16_helper(proplist_copy, MQTT_PROP_TOPIC_ALIAS, 15); mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } static void TEST_read_single_int32(void) { int rc; mosquitto_property *proplist = NULL, *proplist_copy = NULL; generate_full_proplist(&proplist); if(!proplist) return; read_int32_helper(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); read_int32_helper(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); read_int32_helper(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); read_int32_helper(proplist, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); read_int32_helper(proplist_copy, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 3600); read_int32_helper(proplist_copy, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 86400); read_int32_helper(proplist_copy, MQTT_PROP_WILL_DELAY_INTERVAL, 1800); read_int32_helper(proplist_copy, MQTT_PROP_MAXIMUM_PACKET_SIZE, 200000000); mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } static void TEST_read_single_varint(void) { int rc; mosquitto_property *proplist = NULL, *proplist_copy = NULL; generate_full_proplist(&proplist); if(!proplist) return; read_varint_helper(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); read_varint_helper(proplist_copy, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 63); mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } static void TEST_read_single_binary(void) { int rc; mosquitto_property *proplist = NULL, *proplist_copy = NULL; generate_full_proplist(&proplist); if(!proplist) return; read_binary_helper(proplist, MQTT_PROP_CORRELATION_DATA, "correlation-data", strlen("correlation-data")); read_binary_helper(proplist, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); if(proplist_copy){ read_binary_helper(proplist_copy, MQTT_PROP_CORRELATION_DATA, "correlation-data", strlen("correlation-data")); read_binary_helper(proplist_copy, MQTT_PROP_AUTHENTICATION_DATA, "password", strlen("password")); } mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } static void TEST_read_single_string(void) { int rc; mosquitto_property *proplist = NULL, *proplist_copy = NULL; generate_full_proplist(&proplist); if(!proplist) return; read_string_helper(proplist, MQTT_PROP_CONTENT_TYPE, "application/json"); read_string_helper(proplist, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); read_string_helper(proplist, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); read_string_helper(proplist, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); read_string_helper(proplist, MQTT_PROP_RESPONSE_INFORMATION, "response"); read_string_helper(proplist, MQTT_PROP_SERVER_REFERENCE, "localhost"); read_string_helper(proplist, MQTT_PROP_REASON_STRING, "reason"); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); if(proplist_copy){ read_string_helper(proplist_copy, MQTT_PROP_CONTENT_TYPE, "application/json"); read_string_helper(proplist_copy, MQTT_PROP_RESPONSE_TOPIC, "response/topic"); read_string_helper(proplist_copy, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "mosquitto-test"); read_string_helper(proplist_copy, MQTT_PROP_AUTHENTICATION_METHOD, "basic"); read_string_helper(proplist_copy, MQTT_PROP_RESPONSE_INFORMATION, "response"); read_string_helper(proplist_copy, MQTT_PROP_SERVER_REFERENCE, "localhost"); read_string_helper(proplist_copy, MQTT_PROP_REASON_STRING, "reason"); } mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } static void TEST_read_single_string_pair(void) { int rc; mosquitto_property *proplist = NULL, *proplist_copy = NULL; generate_full_proplist(&proplist); if(!proplist) return; read_string_pair_helper(proplist, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); if(proplist_copy){ read_string_pair_helper(proplist_copy, MQTT_PROP_USER_PROPERTY, "user-agent", "mosquitto/test"); } mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } /* ======================================================================== * MISSING READ * ======================================================================== */ static void missing_read_helper(mosquitto_property *proplist) { const mosquitto_property *prop; uint8_t byte_value; uint16_t int16_value; uint32_t int32_value; char *key, *value; uint16_t length; /* MISSING */ prop = mosquitto_property_read_byte(proplist, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &byte_value, false); CU_ASSERT_PTR_NULL(prop); /* NOT MISSING */ prop = mosquitto_property_read_int32(proplist, MQTT_PROP_WILL_DELAY_INTERVAL, &int32_value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(int32_value, 1800); /* MISSING */ value = NULL; prop = mosquitto_property_read_string(proplist, MQTT_PROP_CONTENT_TYPE, &value, false); CU_ASSERT_PTR_NULL(prop); /* NOT MISSING */ value = NULL; prop = mosquitto_property_read_string(proplist, MQTT_PROP_RESPONSE_TOPIC, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_PTR_NOT_NULL(value); if(value){ CU_ASSERT_STRING_EQUAL(value, "response/topic"); free(value); } /* MISSING */ prop = mosquitto_property_read_binary(proplist, MQTT_PROP_CORRELATION_DATA, (void **)&value, &length, false); CU_ASSERT_PTR_NULL(prop); /* NOT MISSING */ prop = mosquitto_property_read_byte(proplist, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, &byte_value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(byte_value, 1); /* MISSING */ prop = mosquitto_property_read_varint(proplist, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &int32_value, false); CU_ASSERT_PTR_NULL(prop); /* NOT MISSING */ value = NULL; prop = mosquitto_property_read_string(proplist, MQTT_PROP_SERVER_REFERENCE, &value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_PTR_NOT_NULL(value); if(value){ CU_ASSERT_STRING_EQUAL(value, "localhost"); free(value); } /* MISSING */ prop = mosquitto_property_read_int32(proplist, MQTT_PROP_SESSION_EXPIRY_INTERVAL, &int32_value, false); CU_ASSERT_PTR_NULL(prop); /* NOT MISSING */ value = NULL; prop = mosquitto_property_read_binary(proplist, MQTT_PROP_AUTHENTICATION_DATA, (void **)&value, &length, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_PTR_NOT_NULL(value); if(value){ CU_ASSERT_NSTRING_EQUAL(value, "password", strlen("password")); CU_ASSERT_EQUAL(length, strlen("password")); free(value); } /* MISSING */ prop = mosquitto_property_read_int16(proplist, MQTT_PROP_SERVER_KEEP_ALIVE, &int16_value, false); CU_ASSERT_PTR_NULL(prop); /* NOT MISSING */ prop = mosquitto_property_read_int16(proplist, MQTT_PROP_RECEIVE_MAXIMUM, &int16_value, false); CU_ASSERT_PTR_NOT_NULL(prop); CU_ASSERT_EQUAL(int16_value, 1024); /* MISSING */ prop = mosquitto_property_read_string_pair(proplist, MQTT_PROP_USER_PROPERTY, &key, &value, false); CU_ASSERT_PTR_NULL(prop); } static void TEST_read_missing(void) { mosquitto_property *proplist = NULL, *proplist_copy = NULL; int rc; generate_partial_proplist(&proplist); if(!proplist) return; missing_read_helper(proplist); rc = mosquitto_property_copy_all(&proplist_copy, proplist); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(proplist_copy); if(proplist_copy){ missing_read_helper(proplist_copy); } mosquitto_property_free_all(&proplist); mosquitto_property_free_all(&proplist_copy); } /* ======================================================================== * STRING TO PROPERTY INFO * ======================================================================== */ static void string_to_property_info_helper(const char *str, int rc_expected, int identifier_expected, int type_expected) { int rc; int identifier, type; rc = mosquitto_string_to_property_info(str, &identifier, &type); CU_ASSERT_EQUAL(rc, rc_expected); if(rc == MOSQ_ERR_SUCCESS){ CU_ASSERT_EQUAL(identifier, identifier_expected); CU_ASSERT_EQUAL(type, type_expected); } } static void TEST_string_to_property_info(void) { string_to_property_info_helper("payload-format-indicator", MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("message-expiry-interval", MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT32); string_to_property_info_helper("content-type", MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, MQTT_PROP_TYPE_STRING); string_to_property_info_helper("response-topic", MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, MQTT_PROP_TYPE_STRING); string_to_property_info_helper("correlation-data", MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, MQTT_PROP_TYPE_BINARY); string_to_property_info_helper("subscription-identifier", MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, MQTT_PROP_TYPE_VARINT); string_to_property_info_helper("session-expiry-interval", MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, MQTT_PROP_TYPE_INT32); string_to_property_info_helper("assigned-client-identifier", MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, MQTT_PROP_TYPE_STRING); string_to_property_info_helper("server-keep-alive", MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, MQTT_PROP_TYPE_INT16); string_to_property_info_helper("authentication-method", MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, MQTT_PROP_TYPE_STRING); string_to_property_info_helper("authentication-data", MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, MQTT_PROP_TYPE_BINARY); string_to_property_info_helper("request-problem-information", MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("will-delay-interval", MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, MQTT_PROP_TYPE_INT32); string_to_property_info_helper("request-response-information", MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("response-information", MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, MQTT_PROP_TYPE_STRING); string_to_property_info_helper("server-reference", MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, MQTT_PROP_TYPE_STRING); string_to_property_info_helper("reason-string", MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, MQTT_PROP_TYPE_STRING); string_to_property_info_helper("receive-maximum", MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, MQTT_PROP_TYPE_INT16); string_to_property_info_helper("topic-alias-maximum", MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, MQTT_PROP_TYPE_INT16); string_to_property_info_helper("topic-alias", MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, MQTT_PROP_TYPE_INT16); string_to_property_info_helper("maximum-qos", MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("retain-available", MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("user-property", MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, MQTT_PROP_TYPE_STRING_PAIR); string_to_property_info_helper("maximum-packet-size", MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, MQTT_PROP_TYPE_INT32); string_to_property_info_helper("wildcard-subscription-available", MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("subscription-identifier-available", MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("shared-subscription-available", MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, MQTT_PROP_TYPE_BYTE); string_to_property_info_helper("payload-format-indicator1", MOSQ_ERR_INVAL, 0, 0); string_to_property_info_helper("payload", MOSQ_ERR_INVAL, 0, 0); string_to_property_info_helper("", MOSQ_ERR_INVAL, 0, 0); string_to_property_info_helper(NULL, MOSQ_ERR_INVAL, 0, 0); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_property_user_read_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Property user read", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Property user read test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Read single byte", TEST_read_single_byte) || !CU_add_test(test_suite, "Read single int16", TEST_read_single_int16) || !CU_add_test(test_suite, "Read single int32", TEST_read_single_int32) || !CU_add_test(test_suite, "Read single varint", TEST_read_single_varint) || !CU_add_test(test_suite, "Read single binary", TEST_read_single_binary) || !CU_add_test(test_suite, "Read single string", TEST_read_single_string) || !CU_add_test(test_suite, "Read single string pair", TEST_read_single_string_pair) || !CU_add_test(test_suite, "Read missing", TEST_read_missing) || !CU_add_test(test_suite, "String to property info", TEST_string_to_property_info) ){ printf("Error adding Property Add CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/property_write.c000066400000000000000000000456611450213760600210660ustar00rootroot00000000000000#include #include #include "mqtt_protocol.h" #include "property_mosq.h" #include "packet_mosq.h" static void byte_prop_write_helper( int command, uint32_t remaining_length, int rc_expected, int identifier, uint8_t value_expected) { mosquitto_property property; struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&property, 0, sizeof(mosquitto_property)); property.identifier = identifier; property.value.i8 = value_expected; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.remaining_length = property__get_length_all(&property)+1; packet.packet_length = packet.remaining_length+10; packet.payload = calloc(packet.remaining_length+10, 1); CU_ASSERT_PTR_NOT_NULL(packet.payload); if(!packet.payload) return; property__write_all(&packet, &property, true); packet.pos = 0; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.i8, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 2); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); free(packet.payload); } static void int32_prop_write_helper( int command, uint32_t remaining_length, int rc_expected, int identifier, uint32_t value_expected) { mosquitto_property property; struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&property, 0, sizeof(mosquitto_property)); property.identifier = identifier; property.value.i32 = value_expected; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.remaining_length = property__get_length_all(&property)+1; packet.packet_length = packet.remaining_length+10; packet.payload = calloc(packet.remaining_length+10, 1); CU_ASSERT_PTR_NOT_NULL(packet.payload); if(!packet.payload) return; property__write_all(&packet, &property, true); packet.pos = 0; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.i32, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 5); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); free(packet.payload); } static void int16_prop_write_helper( int command, uint32_t remaining_length, int rc_expected, int identifier, uint16_t value_expected) { mosquitto_property property; struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&property, 0, sizeof(mosquitto_property)); property.identifier = identifier; property.value.i16 = value_expected; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.remaining_length = property__get_length_all(&property)+1; packet.packet_length = packet.remaining_length+10; packet.payload = calloc(packet.remaining_length+10, 1); CU_ASSERT_PTR_NOT_NULL(packet.payload); if(!packet.payload) return; property__write_all(&packet, &property, true); packet.pos = 0; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.i16, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 3); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); free(packet.payload); } static void string_prop_write_helper( int command, uint32_t remaining_length, int rc_expected, int identifier, const char *value_expected) { mosquitto_property property; struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&property, 0, sizeof(mosquitto_property)); property.identifier = identifier; property.value.s.v = strdup(value_expected); CU_ASSERT_PTR_NOT_NULL(property.value.s.v); if(!property.value.s.v) return; property.value.s.len = (uint16_t)strlen(value_expected); memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.remaining_length = property__get_length_all(&property)+1; packet.packet_length = packet.remaining_length+10; packet.payload = calloc(packet.remaining_length+10, 1); CU_ASSERT_PTR_NOT_NULL(packet.payload); if(!packet.payload) return; property__write_all(&packet, &property, true); packet.pos = 0; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(value_expected)); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); free(property.value.s.v); free(packet.payload); } static void binary_prop_write_helper( int command, uint32_t remaining_length, int rc_expected, int identifier, const uint8_t *value_expected, uint16_t len_expected) { mosquitto_property property; struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&property, 0, sizeof(mosquitto_property)); property.identifier = identifier; property.value.bin.v = malloc(len_expected); CU_ASSERT_PTR_NOT_NULL(property.value.bin.v); if(!property.value.bin.v) return; memcpy(property.value.bin.v, value_expected, len_expected); property.value.bin.len = len_expected; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.remaining_length = property__get_length_all(&property)+1; packet.packet_length = packet.remaining_length+10; packet.payload = calloc(packet.remaining_length+10, 1); CU_ASSERT_PTR_NOT_NULL(packet.payload); if(!packet.payload) return; property__write_all(&packet, &property, true); packet.pos = 0; rc = property__read_all(command, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.bin.len, len_expected); CU_ASSERT_EQUAL(memcmp(properties->value.bin.v, value_expected, len_expected), 0); CU_ASSERT_PTR_EQUAL(properties->next, NULL); CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+len_expected); mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_EQUAL(properties, NULL); free(property.value.bin.v); free(packet.payload); } static void string_pair_prop_write_helper( uint32_t remaining_length, int rc_expected, int identifier, const char *name_expected, const char *value_expected, bool expect_multiple) { mosquitto_property property; struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&property, 0, sizeof(mosquitto_property)); property.identifier = identifier; property.value.s.v = strdup(value_expected); CU_ASSERT_PTR_NOT_NULL(property.value.s.v); if(!property.value.s.v) return; property.value.s.len = (uint16_t)strlen(value_expected); property.name.v = strdup(name_expected); CU_ASSERT_PTR_NOT_NULL(property.name.v); if(!property.name.v) return; property.name.len = (uint16_t)strlen(name_expected); memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.remaining_length = property__get_length_all(&property)+1; packet.packet_length = packet.remaining_length+10; packet.payload = calloc(packet.remaining_length+10, 1); CU_ASSERT_PTR_NOT_NULL(packet.payload); if(!packet.payload) return; property__write_all(&packet, &property, true); packet.pos = 0; rc = property__read_all(CMD_CONNECT, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); CU_ASSERT_EQUAL(packet.pos, remaining_length); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->name.len, strlen(name_expected)); CU_ASSERT_EQUAL(properties->value.s.len, strlen(value_expected)); CU_ASSERT_STRING_EQUAL(properties->name.v, name_expected); CU_ASSERT_STRING_EQUAL(properties->value.s.v, value_expected); if(expect_multiple){ CU_ASSERT_PTR_NOT_NULL(properties->next); }else{ CU_ASSERT_PTR_NULL(properties->next); CU_ASSERT_EQUAL(property__get_length_all(properties), 1+2+strlen(name_expected)+2+strlen(value_expected)); } mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_NULL(properties); free(property.value.s.v); free(property.name.v); free(packet.payload); } static void varint_prop_write_helper( uint32_t remaining_length, int rc_expected, int identifier, uint32_t value_expected) { mosquitto_property property; struct mosquitto__packet packet; mosquitto_property *properties; int rc; memset(&property, 0, sizeof(mosquitto_property)); property.identifier = identifier; property.value.varint = value_expected; memset(&packet, 0, sizeof(struct mosquitto__packet)); packet.remaining_length = property__get_length_all(&property)+1; CU_ASSERT_EQUAL(packet.remaining_length, remaining_length); packet.packet_length = packet.remaining_length+10; packet.payload = calloc(packet.remaining_length+10, 1); CU_ASSERT_PTR_NOT_NULL(packet.payload); if(!packet.payload) return; property__write_all(&packet, &property, true); packet.pos = 0; rc = property__read_all(CMD_PUBLISH, &packet, &properties); CU_ASSERT_EQUAL(rc, rc_expected); if(properties){ CU_ASSERT_EQUAL(properties->identifier, identifier); CU_ASSERT_EQUAL(properties->value.varint, value_expected); CU_ASSERT_PTR_NULL(properties->next); if(value_expected < 128){ CU_ASSERT_EQUAL(property__get_length_all(properties), 2); }else if(value_expected < 16384){ CU_ASSERT_EQUAL(property__get_length_all(properties), 3); }else if(value_expected < 2097152){ CU_ASSERT_EQUAL(property__get_length_all(properties), 4); }else if(value_expected < 268435456){ CU_ASSERT_EQUAL(property__get_length_all(properties), 5); }else{ CU_FAIL("Incorrect varint value."); } mosquitto_property_free_all(&properties); } CU_ASSERT_PTR_NULL(properties); free(packet.payload); } /* ======================================================================== * BAD IDENTIFIER * ======================================================================== */ static void TEST_bad_identifier(void) { mosquitto_property property; struct mosquitto__packet packet; uint8_t payload[10]; int rc; memset(&property, 0, sizeof(mosquitto_property)); memset(&packet, 0, sizeof(struct mosquitto__packet)); property.identifier = 0xFFFF; packet.packet_length = 10; packet.remaining_length = 8; packet.payload = payload; rc = property__write_all(&packet, &property, true); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); } /* ======================================================================== * SINGLE PROPERTIES * ======================================================================== */ static void TEST_single_payload_format_indicator(void) { byte_prop_write_helper(CMD_PUBLISH, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, 1); } static void TEST_single_request_problem_information(void) { byte_prop_write_helper(CMD_CONNECT, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_PROBLEM_INFORMATION, 1); } static void TEST_single_request_response_information(void) { byte_prop_write_helper(CMD_CONNECT, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_REQUEST_RESPONSE_INFORMATION, 1); } static void TEST_single_maximum_qos(void) { byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_QOS, 1); } static void TEST_single_retain_available(void) { byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_RETAIN_AVAILABLE, 1); } static void TEST_single_wildcard_subscription_available(void) { byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_WILDCARD_SUB_AVAILABLE, 0); } static void TEST_single_subscription_identifier_available(void) { byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_ID_AVAILABLE, 0); } static void TEST_single_shared_subscription_available(void) { byte_prop_write_helper(CMD_CONNACK, 3, MOSQ_ERR_SUCCESS, MQTT_PROP_SHARED_SUB_AVAILABLE, 1); } static void TEST_single_message_expiry_interval(void) { int32_prop_write_helper(CMD_PUBLISH, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, 0x12233445); } static void TEST_single_session_expiry_interval(void) { int32_prop_write_helper(CMD_CONNACK, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_SESSION_EXPIRY_INTERVAL, 0x45342312); } static void TEST_single_will_delay_interval(void) { int32_prop_write_helper(CMD_WILL, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_WILL_DELAY_INTERVAL, 0x45342312); } static void TEST_single_maximum_packet_size(void) { int32_prop_write_helper(CMD_CONNECT, 6, MOSQ_ERR_SUCCESS, MQTT_PROP_MAXIMUM_PACKET_SIZE, 0x45342312); } static void TEST_single_server_keep_alive(void) { int16_prop_write_helper(CMD_CONNACK, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_KEEP_ALIVE, 0x4534); } static void TEST_single_receive_maximum(void) { int16_prop_write_helper(CMD_CONNACK, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_RECEIVE_MAXIMUM, 0x6842); } static void TEST_single_topic_alias_maximum(void) { int16_prop_write_helper(CMD_CONNECT, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS_MAXIMUM, 0x6842); } static void TEST_single_topic_alias(void) { int16_prop_write_helper(CMD_PUBLISH, 4, MOSQ_ERR_SUCCESS, MQTT_PROP_TOPIC_ALIAS, 0x6842); } static void TEST_single_content_type(void) { string_prop_write_helper(CMD_PUBLISH, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CONTENT_TYPE, "hello"); } static void TEST_single_response_topic(void) { string_prop_write_helper(CMD_WILL, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_TOPIC, "hello"); } static void TEST_single_assigned_client_identifier(void) { string_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_ASSIGNED_CLIENT_IDENTIFIER, "hello"); } static void TEST_single_authentication_method(void) { string_prop_write_helper(CMD_CONNECT, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_METHOD, "hello"); } static void TEST_single_response_information(void) { string_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_RESPONSE_INFORMATION, "hello"); } static void TEST_single_server_reference(void) { string_prop_write_helper(CMD_CONNACK, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_SERVER_REFERENCE, "hello"); } static void TEST_single_reason_string(void) { string_prop_write_helper(CMD_PUBREC, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_REASON_STRING, "hello"); } static void TEST_single_correlation_data(void) { uint8_t payload[5] = {1, 'e', 0, 'l', 9}; binary_prop_write_helper(CMD_PUBLISH, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_CORRELATION_DATA, payload, 5); } static void TEST_single_authentication_data(void) { uint8_t payload[5] = {1, 'e', 0, 'l', 9}; binary_prop_write_helper(CMD_CONNECT, 9, MOSQ_ERR_SUCCESS, MQTT_PROP_AUTHENTICATION_DATA, payload, 5); } static void TEST_single_user_property(void) { string_pair_prop_write_helper(10, MOSQ_ERR_SUCCESS, MQTT_PROP_USER_PROPERTY, "za", "bc", false); } static void TEST_single_subscription_identifier(void) { varint_prop_write_helper(3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 0); varint_prop_write_helper(3, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 127); varint_prop_write_helper(4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 128); varint_prop_write_helper(4, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16383); varint_prop_write_helper(5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 16384); varint_prop_write_helper(5, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097151); varint_prop_write_helper(6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 2097152); varint_prop_write_helper(6, MOSQ_ERR_SUCCESS, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, 268435455); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_property_write_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Property write", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Property write test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Bad identifier", TEST_bad_identifier) || !CU_add_test(test_suite, "Single Payload Format Indicator", TEST_single_payload_format_indicator) || !CU_add_test(test_suite, "Single Request Problem Information", TEST_single_request_problem_information) || !CU_add_test(test_suite, "Single Request Response Information", TEST_single_request_response_information) || !CU_add_test(test_suite, "Single Maximum QoS", TEST_single_maximum_qos) || !CU_add_test(test_suite, "Single Retain Available", TEST_single_retain_available) || !CU_add_test(test_suite, "Single Wildcard Subscription Available", TEST_single_wildcard_subscription_available) || !CU_add_test(test_suite, "Single Subscription Identifier Available", TEST_single_subscription_identifier_available) || !CU_add_test(test_suite, "Single Shared Subscription Available", TEST_single_shared_subscription_available) || !CU_add_test(test_suite, "Single Message Expiry Interval", TEST_single_message_expiry_interval) || !CU_add_test(test_suite, "Single Session Expiry Interval", TEST_single_session_expiry_interval) || !CU_add_test(test_suite, "Single Will Delay Interval", TEST_single_will_delay_interval) || !CU_add_test(test_suite, "Single Maximum Packet Size", TEST_single_maximum_packet_size) || !CU_add_test(test_suite, "Single Server Keep Alive", TEST_single_server_keep_alive) || !CU_add_test(test_suite, "Single Receive Maximum", TEST_single_receive_maximum) || !CU_add_test(test_suite, "Single Topic Alias Maximum", TEST_single_topic_alias_maximum) || !CU_add_test(test_suite, "Single Topic Alias", TEST_single_topic_alias) || !CU_add_test(test_suite, "Single Content Type", TEST_single_content_type) || !CU_add_test(test_suite, "Single Response Topic", TEST_single_response_topic) || !CU_add_test(test_suite, "Single Assigned Client Identifier", TEST_single_assigned_client_identifier) || !CU_add_test(test_suite, "Single Authentication Method", TEST_single_authentication_method) || !CU_add_test(test_suite, "Single Response Information", TEST_single_response_information) || !CU_add_test(test_suite, "Single Server Reference", TEST_single_server_reference) || !CU_add_test(test_suite, "Single Reason String", TEST_single_reason_string) || !CU_add_test(test_suite, "Single Correlation Data", TEST_single_correlation_data) || !CU_add_test(test_suite, "Single Authentication Data", TEST_single_authentication_data) || !CU_add_test(test_suite, "Single User Property", TEST_single_user_property) || !CU_add_test(test_suite, "Single Subscription Identifier", TEST_single_subscription_identifier) ){ printf("Error adding Property read CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/publish_test.c000066400000000000000000000016771450213760600204740ustar00rootroot00000000000000#include #include #include #include static void TEST_maximum_packet_size(void) { struct mosquitto mosq; int rc; memset(&mosq, 0, sizeof(struct mosquitto)); mosq.maximum_packet_size = 5; rc = mosquitto_publish(&mosq, NULL, "topic/oversize", strlen("payload"), "payload", 0, 0); CU_ASSERT_EQUAL(rc, MOSQ_ERR_OVERSIZE_PACKET); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_publish_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Publish", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Publish test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "v5: Maximum packet size", TEST_maximum_packet_size) ){ printf("Error adding Publish CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/stubs.c000066400000000000000000000007741450213760600171240ustar00rootroot00000000000000#include "config.h" #include #include struct mosquitto_db{ }; int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { UNUSED(mosq); UNUSED(priority); UNUSED(fmt); return 0; } time_t mosquitto_time(void) { return 123; } int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq) { UNUSED(db); UNUSED(mosq); return MOSQ_ERR_SUCCESS; } int send__pingreq(struct mosquitto *mosq) { UNUSED(mosq); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/test/unit/subs_stubs.c000066400000000000000000000105461450213760600201560ustar00rootroot00000000000000#include #define WITH_BROKER #include #include #include #include #include #include #if 0 extern uint64_t last_retained; extern char *last_sub; extern int last_qos; struct mosquitto *context__init(mosq_sock_t sock) { return mosquitto__calloc(1, sizeof(struct mosquitto)); } int db__message_insert(struct mosquitto *context, uint16_t mid, enum mosquitto_msg_direction dir, uint8_t qos, bool retain, struct mosquitto_msg_store *stored, mosquitto_property *properties) { return MOSQ_ERR_SUCCESS; } void db__msg_store_ref_dec(struct mosquitto_msg_store **store) { } void db__msg_store_ref_inc(struct mosquitto_msg_store *store) { store->ref_count++; } #endif int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { UNUSED(mosq); UNUSED(priority); UNUSED(fmt); return 0; } time_t mosquitto_time(void) { return 123; } #if 0 int net__socket_close(struct mosquitto *mosq) { return MOSQ_ERR_SUCCESS; } int send__pingreq(struct mosquitto *mosq) { return MOSQ_ERR_SUCCESS; } int mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_tn payloadlen, void* payload, uint8_t qos, bool retain, int access) { return MOSQ_ERR_SUCCESS; } int acl__find_acls(struct mosquitto *context) { return MOSQ_ERR_SUCCESS; } #endif int send__publish(struct mosquitto *mosq, uint16_t mid, const char *topic, uint32_t payloadlen, const void *payload, uint8_t qos, bool retain, bool dup, const mosquitto_property *cmsg_props, const mosquitto_property *store_props, uint32_t expiry_interval) { UNUSED(mosq); UNUSED(mid); UNUSED(topic); UNUSED(payloadlen); UNUSED(payload); UNUSED(qos); UNUSED(retain); UNUSED(dup); UNUSED(cmsg_props); UNUSED(store_props); UNUSED(expiry_interval); return MOSQ_ERR_SUCCESS; } int send__pubcomp(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(mid); UNUSED(properties); return MOSQ_ERR_SUCCESS; } int send__pubrec(struct mosquitto *mosq, uint16_t mid, uint8_t reason_code, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(mid); UNUSED(reason_code); UNUSED(properties); return MOSQ_ERR_SUCCESS; } int send__pubrel(struct mosquitto *mosq, uint16_t mid, const mosquitto_property *properties) { UNUSED(mosq); UNUSED(mid); UNUSED(properties); return MOSQ_ERR_SUCCESS; } int mosquitto_acl_check(struct mosquitto *context, const char *topic, uint32_t payloadlen, void* payload, uint8_t qos, bool retain, int access) { UNUSED(context); UNUSED(topic); UNUSED(payloadlen); UNUSED(payload); UNUSED(qos); UNUSED(retain); UNUSED(access); return MOSQ_ERR_SUCCESS; } uint16_t mosquitto__mid_generate(struct mosquitto *mosq) { static uint16_t mid = 1; UNUSED(mosq); return ++mid; } int mosquitto_property_add_varint(mosquitto_property **proplist, int identifier, uint32_t value) { UNUSED(proplist); UNUSED(identifier); UNUSED(value); return MOSQ_ERR_SUCCESS; } int persist__backup(bool shutdown) { UNUSED(shutdown); return MOSQ_ERR_SUCCESS; } int persist__restore(void) { return MOSQ_ERR_SUCCESS; } void mosquitto_property_free_all(mosquitto_property **properties) { UNUSED(properties); } int retain__init(void) { return MOSQ_ERR_SUCCESS; } void retain__clean(struct mosquitto__retainhier **retainhier) { UNUSED(retainhier); } int retain__queue(struct mosquitto *context, const char *sub, uint8_t sub_qos, uint32_t subscription_identifier) { UNUSED(context); UNUSED(sub); UNUSED(sub_qos); UNUSED(subscription_identifier); return MOSQ_ERR_SUCCESS; } int retain__store(const char *topic, struct mosquitto_msg_store *stored, char **split_topics) { UNUSED(topic); UNUSED(stored); UNUSED(split_topics); return MOSQ_ERR_SUCCESS; } void util__decrement_receive_quota(struct mosquitto *mosq) { if(mosq->msgs_in.inflight_quota > 0){ mosq->msgs_in.inflight_quota--; } } void util__decrement_send_quota(struct mosquitto *mosq) { if(mosq->msgs_out.inflight_quota > 0){ mosq->msgs_out.inflight_quota--; } } void util__increment_receive_quota(struct mosquitto *mosq) { mosq->msgs_in.inflight_quota++; } void util__increment_send_quota(struct mosquitto *mosq) { mosq->msgs_out.inflight_quota++; } int session_expiry__add_from_persistence(struct mosquitto *context, time_t expiry_time) { UNUSED(context); UNUSED(expiry_time); return 0; } mosquitto-2.0.18/test/unit/subs_test.c000066400000000000000000000055301450213760600177720ustar00rootroot00000000000000/* Tests for subscription adding/removing * * FIXME - these need to be aggressive about finding failures, at the moment * they are just confirming that good behaviour works. */ #include #include #define WITH_BROKER #define WITH_PERSISTENCE #include "mosquitto_broker_internal.h" #include "memory_mosq.h" struct mosquitto_db db; static void hier_quick_check(struct mosquitto__subhier **sub, struct mosquitto *context, const char *topic) { if(sub != NULL){ CU_ASSERT_EQUAL((*sub)->topic_len, strlen(topic)); CU_ASSERT_PTR_NOT_NULL((*sub)->topic); if((*sub)->topic){ CU_ASSERT_STRING_EQUAL((*sub)->topic, topic); } if(context){ CU_ASSERT_PTR_NOT_NULL((*sub)->subs); if((*sub)->subs){ CU_ASSERT_PTR_EQUAL((*sub)->subs->context, context); CU_ASSERT_PTR_NULL((*sub)->subs->next); } }else{ CU_ASSERT_PTR_NULL((*sub)->subs); } (*sub) = (*sub)->children; } } static void TEST_sub_add_single(void) { struct mosquitto__config config; struct mosquitto__listener listener; struct mosquitto context; struct mosquitto__subhier *sub; int rc; memset(&db, 0, sizeof(struct mosquitto_db)); memset(&config, 0, sizeof(struct mosquitto__config)); memset(&listener, 0, sizeof(struct mosquitto__listener)); memset(&context, 0, sizeof(struct mosquitto)); context.id = "client"; db.config = &config; listener.port = 1883; config.listeners = &listener; config.listener_count = 1; db__open(&config); rc = sub__add(&context, "a/b/c/d/e", 0, 0, 0, &db.subs); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_PTR_NOT_NULL(db.subs); if(db.subs){ sub = db.subs; hier_quick_check(&sub, NULL, ""); hier_quick_check(&sub, NULL, ""); hier_quick_check(&sub, NULL, "a"); hier_quick_check(&sub, NULL, "b"); hier_quick_check(&sub, NULL, "c"); hier_quick_check(&sub, NULL, "d"); hier_quick_check(&sub, &context, "e"); CU_ASSERT_PTR_NULL(sub); } mosquitto__free(context.subs); db__close(); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int main(int argc, char *argv[]) { CU_pSuite test_suite = NULL; unsigned int fails; UNUSED(argc); UNUSED(argv); if(CU_initialize_registry() != CUE_SUCCESS){ printf("Error initializing CUnit registry.\n"); return 1; } test_suite = CU_add_suite("Subs", NULL, NULL); if(!test_suite){ printf("Error adding CUnit Subs test suite.\n"); CU_cleanup_registry(); return 1; } if(0 || !CU_add_test(test_suite, "Sub add single", TEST_sub_add_single) ){ printf("Error adding Subs CUnit tests.\n"); CU_cleanup_registry(); return 1; } CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); fails = CU_get_number_of_failures(); CU_cleanup_registry(); return (int)fails; } mosquitto-2.0.18/test/unit/test.c000066400000000000000000000021641450213760600167360ustar00rootroot00000000000000#include "config.h" #include #include #include int init_datatype_read_tests(void); int init_datatype_write_tests(void); int init_property_add_tests(void); int init_property_read_tests(void); int init_property_user_read_tests(void); int init_property_write_tests(void); int init_utf8_tests(void); int init_util_topic_tests(void); int init_misc_trim_tests(void); int main(int argc, char *argv[]) { unsigned int fails; UNUSED(argc); UNUSED(argv); if(CU_initialize_registry() != CUE_SUCCESS){ printf("Error initializing CUnit registry.\n"); return 1; } if(0 || init_utf8_tests() || init_datatype_read_tests() || init_datatype_write_tests() || init_property_add_tests() || init_property_read_tests() || init_property_user_read_tests() || init_property_write_tests() || init_util_topic_tests() || init_misc_trim_tests() ){ CU_cleanup_registry(); return 1; } CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); fails = CU_get_number_of_failures(); CU_cleanup_registry(); return (int)fails; } mosquitto-2.0.18/test/unit/tls_stubs.c000066400000000000000000000010241450213760600177730ustar00rootroot00000000000000#include "config.h" #include #include int tls_ex_index_mosq; struct mosquitto_db{ }; int log__printf(struct mosquitto *mosq, unsigned int priority, const char *fmt, ...) { UNUSED(mosq); UNUSED(priority); UNUSED(fmt); return 0; } time_t mosquitto_time(void) { return 123; } int net__socket_close(struct mosquitto_db *db, struct mosquitto *mosq) { UNUSED(db); UNUSED(mosq); return MOSQ_ERR_SUCCESS; } int send__pingreq(struct mosquitto *mosq) { UNUSED(mosq); return MOSQ_ERR_SUCCESS; } mosquitto-2.0.18/test/unit/tls_test.c000066400000000000000000000053541450213760600176240ustar00rootroot00000000000000#include #include #define WITH_TLS #include "tls_mosq.c" //static int mosquitto__cmp_hostname_wildcard(char *certname, const char *hostname) void hostname_cmp_helper(char *certname, const char *hostname, int expected) { int rc = mosquitto__cmp_hostname_wildcard(certname, hostname); CU_ASSERT_EQUAL(rc, expected); if(rc != expected){ printf("%d || %d\n", rc, expected); } } void TEST_tls_hostname_compare_null(void) { hostname_cmp_helper(NULL, "localhost", 1); hostname_cmp_helper("localhost", NULL, 1); hostname_cmp_helper(NULL, NULL, 1); } void TEST_tls_hostname_compare_simple(void) { hostname_cmp_helper("localhost", "localhost", 0); hostname_cmp_helper("localhost", "localhose", 15); } void TEST_tls_hostname_compare_bad_wildcard_format(void) { hostname_cmp_helper("**localhost", "localhost", 1); hostname_cmp_helper("*,localhost", "localhost", 1); hostname_cmp_helper("*.", "localhost", 1); } void TEST_tls_hostname_compare_invalid_wildcard(void) { hostname_cmp_helper("*.com", "example.com", 1); hostname_cmp_helper("*.com", "example.org", 1); hostname_cmp_helper("*.org", "example.org", 1); } void TEST_tls_hostname_compare_good_wildcard(void) { hostname_cmp_helper("*.example.com", "test.example.com", 0); hostname_cmp_helper("*.example.com", "test.example.org", -12); hostname_cmp_helper("*.example.org", "test.example.org", 0); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int main(int argc, char *argv[]) { CU_pSuite test_suite = NULL; unsigned int fails; UNUSED(argc); UNUSED(argv); if(CU_initialize_registry() != CUE_SUCCESS){ printf("Error initializing CUnit registry.\n"); return 1; } test_suite = CU_add_suite("Subs", NULL, NULL); if(!test_suite){ printf("Error adding CUnit TLS test suite.\n"); CU_cleanup_registry(); return 1; } if(0 || !CU_add_test(test_suite, "TLS hostname compare null", TEST_tls_hostname_compare_null) || !CU_add_test(test_suite, "TLS hostname compare simple", TEST_tls_hostname_compare_simple) || !CU_add_test(test_suite, "TLS hostname compare bad wildcard format", TEST_tls_hostname_compare_bad_wildcard_format) || !CU_add_test(test_suite, "TLS hostname compare invalid wildcard", TEST_tls_hostname_compare_invalid_wildcard) || !CU_add_test(test_suite, "TLS hostname compare good wildcard", TEST_tls_hostname_compare_good_wildcard) ){ printf("Error adding TLS CUnit tests.\n"); CU_cleanup_registry(); return 1; } CU_basic_set_mode(CU_BRM_VERBOSE); CU_basic_run_tests(); fails = CU_get_number_of_failures(); CU_cleanup_registry(); return (int)fails; } mosquitto-2.0.18/test/unit/utf8.c000066400000000000000000000461711450213760600166530ustar00rootroot00000000000000#include #include #include "mosquitto.h" /* Test data taken from * http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt but modified for * updated standard (no 5, 6 byte lengths) */ static void utf8_helper_len(const char *text, int len, int expected) { int result; result = mosquitto_validate_utf8(text, len); CU_ASSERT_EQUAL(result, expected); } static void utf8_helper(const char *text, int expected) { utf8_helper_len(text, (int)strlen(text), expected); } static void TEST_utf8_empty(void) { utf8_helper_len(NULL, 0, MOSQ_ERR_INVAL); } static void TEST_utf8_valid(void) { /* 1 Some correct UTF-8 text */ utf8_helper("", MOSQ_ERR_SUCCESS); utf8_helper("You should see the Greek word 'kosme': \"κόσμε\"", MOSQ_ERR_SUCCESS); } static void TEST_utf8_truncated(void) { uint8_t buf[4]; /* As per boundary condition tests, but less one character */ buf[0] = 0xC2; buf[1] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); buf[0] = 0xE0; buf[1] = 0xA0; buf[2] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); buf[0] = 0xF0; buf[1] = 0x90; buf[2] = 0x80; buf[3] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } static void TEST_utf8_boundary_conditions(void) { /* 2 Boundary condition test cases */ /* 2.1 First possible sequence of a certain length */ utf8_helper_len("2.1.1 1 byte (U-00000000): \"\0\"", 39, MOSQ_ERR_MALFORMED_UTF8); utf8_helper("2.1.2 2 bytes (U-00000080): \"€\"", MOSQ_ERR_MALFORMED_UTF8); /* control char */ utf8_helper("2.1.3 3 bytes (U-00000800): \"ࠀ\"", MOSQ_ERR_SUCCESS); utf8_helper("2.1.4 4 bytes (U-00010000): \"𐀀\"", MOSQ_ERR_SUCCESS); /* 2.2 Last possible sequence of a certain length */ utf8_helper("2.2.1 1 byte (U-0000007F): \"\"", MOSQ_ERR_MALFORMED_UTF8); /* control char */ utf8_helper("2.2.2 2 bytes (U-000007FF): \"߿\"", MOSQ_ERR_SUCCESS); /* Non character */ utf8_helper("2.2.3 3 bytes (U-0000FFFF): \"￿\"", MOSQ_ERR_MALFORMED_UTF8); /* Non character */ utf8_helper("2.2.4 4 bytes (U-0010FFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); /* 2.3 Other boundary conditions */ utf8_helper("2.3.1 U-0000D7FF = ed 9f bf = \"퟿\"", MOSQ_ERR_SUCCESS); utf8_helper("2.3.2 U-0000E000 = ee 80 80 = \"\"", MOSQ_ERR_SUCCESS); utf8_helper("2.3.3 U-0000FFFD = ef bf bd = \"�\"", MOSQ_ERR_SUCCESS); /* Non character */ utf8_helper("2.3.4 U-0010FFFF = f4 8f bf bf = \"􏿿\"", MOSQ_ERR_MALFORMED_UTF8); /* This used to be valid in pre-2003 utf-8 */ utf8_helper("2.3.5 U-00110000 = f4 90 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); } static void TEST_utf8_malformed_sequences(void) { uint8_t buf[100]; int i; /* 3 Malformed sequences */ /* 3.1 Unexpected continuation bytes */ utf8_helper("3.1.1 First continuation byte 0x80: \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.1.2 Last continuation byte 0xbf: \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.1.3 2 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.1.4 3 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.1.5 4 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.1.6 5 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.1.7 6 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.1.8 7 continuation bytes: \"\"", MOSQ_ERR_MALFORMED_UTF8); /* 3.1.9 Sequence of all 64 possible continuation bytes (0x80-0xbf): */ memset(buf, 0, sizeof(buf)); for(i=0x80; i<0x90; i++){ buf[i-0x80] = (uint8_t)i; } utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); memset(buf, 0, sizeof(buf)); for(i=0x90; i<0xa0; i++){ buf[i-0x90] = (uint8_t)i; } utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); for(i=0x80; i<0xA0; i++){ buf[0] = (uint8_t)i; buf[1] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } for(i=0xA0; i<0xC0; i++){ buf[0] = (uint8_t)i; buf[1] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } /* 3.2 Lonely start characters */ /* 3.2.1 All 32 first bytes of 2-byte sequences (0xc0-0xdf), each followed by a space character: */ utf8_helper(" ", MOSQ_ERR_MALFORMED_UTF8); for(i=0xC0; i<0xE0; i++){ buf[0] = (uint8_t)i; buf[1] = ' '; buf[2] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } /* 3.2.2 All 16 first bytes of 3-byte sequences (0xe0-0xef), each followed by a space character: */ utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); for(i=0xe0; i<0xf0; i++){ buf[0] = (uint8_t)i; buf[1] = ' '; buf[2] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } /* 3.2.3 All 8 first bytes of 4-byte sequences (0xf0-0xf7), each followed by a space character: */ utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); for(i=0xF0; i<0xF8; i++){ buf[0] = (uint8_t)i; buf[1] = ' '; buf[2] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } /* 3.2.4 All 4 first bytes of 5-byte sequences (0xf8-0xfb), each followed by a space character: */ utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); for(i=0xF8; i<0xFC; i++){ buf[0] = (uint8_t)i; buf[1] = ' '; buf[2] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } /* 3.2.5 All 2 first bytes of 6-byte sequences (0xfc-0xfd), each followed by a space character: */ utf8_helper("\" \"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper(" ", MOSQ_ERR_MALFORMED_UTF8); utf8_helper(" ", MOSQ_ERR_MALFORMED_UTF8); for(i=0xFC; i<0xFE; i++){ buf[0] = (uint8_t)i; buf[1] = ' '; buf[2] = 0; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } /* 3.3 Sequences with last continuation byte missing All bytes of an incomplete sequence should be signalled as a single malformed sequence, i.e., you should see only a single replacement character in each of the next 10 tests. (Characters as in section 2) */ utf8_helper("3.3.1 2-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.2 3-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.3 4-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.4 5-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.5 6-byte sequence with last byte missing (U+0000): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.6 2-byte sequence with last byte missing (U-000007FF): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.7 3-byte sequence with last byte missing (U-0000FFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.8 4-byte sequence with last byte missing (U-001FFFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.9 5-byte sequence with last byte missing (U-03FFFFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.3.10 6-byte sequence with last byte missing (U-7FFFFFFF): \"\"", MOSQ_ERR_MALFORMED_UTF8); /* 3.4 Concatenation of incomplete sequences All the 10 sequences of 3.3 concatenated, you should see 10 malformed sequences being signalled:*/ utf8_helper("\"\"", MOSQ_ERR_MALFORMED_UTF8); /* 3.5 Impossible bytes The following two bytes cannot appear in a correct UTF-8 string */ utf8_helper("3.5.1 fe = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.5.2 ff = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("3.5.3 fe fe ff ff = \"\"", MOSQ_ERR_MALFORMED_UTF8); } static void TEST_utf8_overlong_encoding(void) { /* 4 Overlong sequences The following sequences are not malformed according to the letter of the Unicode 2.0 standard. However, they are longer then necessary and a correct UTF-8 encoder is not allowed to produce them. A "safe UTF-8 decoder" should reject them just like malformed sequences for two reasons: (1) It helps to debug applications if overlong sequences are not treated as valid representations of characters, because this helps to spot problems more quickly. (2) Overlong sequences provide alternative representations of characters, that could maliciously be used to bypass filters that check only for ASCII characters. For instance, a 2-byte encoded line feed (LF) would not be caught by a line counter that counts only 0x0a bytes, but it would still be processed as a line feed by an unsafe UTF-8 decoder later in the pipeline. From a security point of view, ASCII compatibility of UTF-8 sequences means also, that ASCII characters are *only* allowed to be represented by ASCII bytes in the range 0x00-0x7f. To ensure this aspect of ASCII compatibility, use only "safe UTF-8 decoders" that reject overlong UTF-8 sequences for which a shorter encoding exists. */ /* 4.1 Examples of an overlong ASCII character With a safe UTF-8 decoder, all of the following five overlong representations of the ASCII character slash ("/") should be rejected like a malformed UTF-8 sequence, for instance by substituting it with a replacement character. If you see a slash below, you do not have a safe UTF-8 decoder! */ utf8_helper("4.1.1 U+002F = c0 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.1.2 U+002F = e0 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.1.3 U+002F = f0 80 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.1.4 U+002F = f8 80 80 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.1.5 U+002F = fc 80 80 80 80 af = \"\"", MOSQ_ERR_MALFORMED_UTF8); /* 4.2 Maximum overlong sequences Below you see the highest Unicode value that is still resulting in an overlong sequence if represented with the given number of bytes. This is a boundary test for safe UTF-8 decoders. All five characters should be rejected like malformed UTF-8 sequences. */ utf8_helper("4.2.1 U-0000007F = c1 bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.2.2 U-000007FF = e0 9f bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.2.3 U-0000FFFF = f0 8f bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.2.4 U-001FFFFF = f8 87 bf bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.2.5 U-03FFFFFF = fc 83 bf bf bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); /* 4.3 Overlong representation of the NUL character The following five sequences should also be rejected like malformed UTF-8 sequences and should not be treated like the ASCII NUL character. */ utf8_helper("4.3.1 U+0000 = c0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.3.2 U+0000 = e0 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.3.3 U+0000 = f0 80 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.3.4 U+0000 = f8 80 80 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("4.3.5 U+0000 = fc 80 80 80 80 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); } static void TEST_utf8_illegal_code_positions(void) { /* 5 Illegal code positions The following UTF-8 sequences should be rejected like malformed sequences, because they never represent valid ISO 10646 characters and a UTF-8 decoder that accepts them might introduce security problems comparable to overlong UTF-8 sequences. */ /* 5.1 Single UTF-16 surrogates */ utf8_helper("5.1.1 U+D800 = ed a0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.1.2 U+DB7F = ed ad bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.1.3 U+DB80 = ed ae 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.1.4 U+DBFF = ed af bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.1.5 U+DC00 = ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.1.6 U+DF80 = ed be 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.1.7 U+DFFF = ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); /* 5.2 Paired UTF-16 surrogates */ utf8_helper("5.2.1 U+D800 U+DC00 = ed a0 80 ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.2.2 U+D800 U+DFFF = ed a0 80 ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.2.3 U+DB7F U+DC00 = ed ad bf ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.2.4 U+DB7F U+DFFF = ed ad bf ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.2.5 U+DB80 U+DC00 = ed ae 80 ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.2.6 U+DB80 U+DFFF = ed ae 80 ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.2.7 U+DBFF U+DC00 = ed af bf ed b0 80 = \"\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.2.8 U+DBFF U+DFFF = ed af bf ed bf bf = \"\"", MOSQ_ERR_MALFORMED_UTF8); /* 5.3 Noncharacter code positions The following "noncharacters" are "reserved for internal use" by applications, and according to older versions of the Unicode Standard "should never be interchanged". Unicode Corrigendum #9 dropped the latter restriction. Nevertheless, their presence in incoming UTF-8 data can remain a potential security risk, depending on what use is made of these codes subsequently. Examples of such internal use: - Some file APIs with 16-bit characters may use the integer value -1 = U+FFFF to signal an end-of-file (EOF) or error condition. - In some UTF-16 receivers, code point U+FFFE might trigger a byte-swap operation (to convert between UTF-16LE and UTF-16BE). With such internal use of noncharacters, it may be desirable and safer to block those code points in UTF-8 decoders, as they should never occur legitimately in incoming UTF-8 data, and could trigger unsafe behaviour in subsequent processing. Particularly problematic noncharacters in 16-bit applications: */ utf8_helper("5.3.1 U+FFFE = ef bf be = \"￾\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("5.3.2 U+FFFF = ef bf bf = \"￿\"", MOSQ_ERR_MALFORMED_UTF8); /* Other noncharacters: */ /* FIXME - these need splitting up into separate tests. */ utf8_helper("5.3.3 U+FDD0 .. U+FDEF = \"﷐﷑﷒﷓﷔﷕﷖﷗﷘﷙﷚﷛﷜﷝﷞﷟﷠﷡﷢﷣﷤﷥﷦﷧﷨﷩﷪﷫﷬﷭﷮﷯\"", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷐", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷑", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷒", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷓", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷔", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷕", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷖", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷗", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷘", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷙", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷚", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷛", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷜", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷝", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷞", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷟", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷠", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷡", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷢", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷣", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷤", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷥", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷦", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷧", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷨", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷩", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷪", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷫", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷬", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷭", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷮", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("﷯", MOSQ_ERR_MALFORMED_UTF8); /* 5.3.4 U+nFFFE U+nFFFF (for n = 1..10) */ utf8_helper("🿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("🿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("𯿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("𯿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("𿿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("𿿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񏿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񏿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񟿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񟿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񯿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񯿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񿿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("񿿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򏿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򏿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򟿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򟿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򯿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򯿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򿿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("򿿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󏿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󏿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󟿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󟿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󯿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󯿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󿿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("󿿿", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("􏿾", MOSQ_ERR_MALFORMED_UTF8); utf8_helper("􏿿", MOSQ_ERR_MALFORMED_UTF8); } void TEST_utf8_control_characters(void) { uint8_t buf[10]; int i; /* U+0001 to U+001F are single byte control characters */ for(i=0x01; i<0x20; i++){ buf[0] = (uint8_t)i; buf[1] = '\0'; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } /* U+007F is a single byte control character */ buf[0] = 0x7F; buf[1] = '\0'; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); /* U+0080 to U+009F are two byte control characters */ for(i=0x80; i<0xA0; i++){ buf[0] = 0xC2; buf[1] = (uint8_t)i; buf[2] = '\0'; utf8_helper((char *)buf, MOSQ_ERR_MALFORMED_UTF8); } } void TEST_utf8_mqtt_1_5_4_2(void) { uint8_t buf[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', '\0'}; utf8_helper_len((char *)buf, 9, MOSQ_ERR_SUCCESS); buf[3] = '\0'; utf8_helper_len((char *)buf, 9, MOSQ_ERR_MALFORMED_UTF8); } void TEST_utf8_mqtt_1_5_4_3(void) { uint8_t buf[10] = {'a', 'b', 0xEF, 0xBB, 0xBF, 'f', 'g', 'h', 'i', '\0'}; utf8_helper_len((char *)buf, 9, MOSQ_ERR_SUCCESS); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_utf8_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("UTF-8", NULL, NULL); if(!test_suite){ printf("Error adding CUnit test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "UTF-8 empty", TEST_utf8_empty) || !CU_add_test(test_suite, "UTF-8 valid", TEST_utf8_valid) || !CU_add_test(test_suite, "UTF-8 truncated", TEST_utf8_truncated) || !CU_add_test(test_suite, "UTF-8 boundary conditions", TEST_utf8_boundary_conditions) || !CU_add_test(test_suite, "UTF-8 malformed sequences", TEST_utf8_malformed_sequences) || !CU_add_test(test_suite, "UTF-8 overlong encoding", TEST_utf8_overlong_encoding) || !CU_add_test(test_suite, "UTF-8 illegal code positions", TEST_utf8_illegal_code_positions) || !CU_add_test(test_suite, "UTF-8 control characters", TEST_utf8_control_characters) || !CU_add_test(test_suite, "UTF-8 MQTT-1.5.4-2", TEST_utf8_mqtt_1_5_4_2) || !CU_add_test(test_suite, "UTF-8 MQTT-1.5.4-3", TEST_utf8_mqtt_1_5_4_3) ){ printf("Error adding UTF-8 CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/test/unit/util_topic_test.c000066400000000000000000000243551450213760600211770ustar00rootroot00000000000000#include #include #include static void match_helper(const char *sub, const char *topic) { int rc; bool match; rc = mosquitto_topic_matches_sub(sub, topic, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(match, true); if(match == false){ printf("1: %s:%s\n", sub, topic); } rc = mosquitto_topic_matches_sub2(sub, strlen(sub), topic, strlen(topic), &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_SUCCESS); CU_ASSERT_EQUAL(match, true); if(match == false){ printf("2: %s:%s\n", sub, topic); } } static void no_match_helper(int rc_expected, const char *sub, const char *topic) { int rc; bool match; rc = mosquitto_topic_matches_sub(sub, topic, &match); CU_ASSERT_EQUAL(rc, rc_expected); if(rc != rc_expected){ printf("%d:%d %s:%s\n", rc, rc_expected, sub, topic); } CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub2(sub, strlen(sub), topic, strlen(topic), &match); CU_ASSERT_EQUAL(rc, rc_expected); if(rc != rc_expected){ printf("%d:%d %s:%s\n", rc, rc_expected, sub, topic); } CU_ASSERT_EQUAL(match, false); } /* ======================================================================== * EMPTY INPUT * ======================================================================== */ static void TEST_empty_input(void) { int rc; bool match; rc = mosquitto_topic_matches_sub("sub", NULL, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub(NULL, "topic", &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub(NULL, NULL, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub("sub", "", &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub("", "topic", &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub("", "", &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub2("sub", 3, NULL, 0, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub2(NULL, 0, "topic", 5, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub2(NULL, 0, NULL, 0, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub2("sub", 3, "", 0, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub2("", 0, "topic", 5, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); rc = mosquitto_topic_matches_sub2("", 0, "", 0, &match); CU_ASSERT_EQUAL(rc, MOSQ_ERR_INVAL); CU_ASSERT_EQUAL(match, false); } /* ======================================================================== * VALID MATCHING AND NON-MATCHING * ======================================================================== */ static void TEST_valid_matching(void) { match_helper("foo/#", "foo/"); match_helper("foo/#", "foo"); match_helper("foo//bar", "foo//bar"); match_helper("foo//+", "foo//bar"); match_helper("foo/+/+/baz", "foo///baz"); match_helper("foo/bar/+", "foo/bar/"); match_helper("foo/bar", "foo/bar"); match_helper("foo/+", "foo/bar"); match_helper("foo/+/baz", "foo/bar/baz"); match_helper("A/B/+/#", "A/B/B/C"); match_helper("foo/+/#", "foo/bar/baz"); match_helper("foo/+/#", "foo/bar"); match_helper("#", "foo/bar/baz"); match_helper("#", "foo/bar/baz"); match_helper("#", "/foo/bar"); match_helper("/#", "/foo/bar"); } static void TEST_invalid_but_matching(void) { /* Matching here is "naive treatment of the wildcards would produce a * match". They shouldn't really match, they should fail. */ no_match_helper(MOSQ_ERR_INVAL, "+foo", "+foo"); no_match_helper(MOSQ_ERR_INVAL, "fo+o", "fo+o"); no_match_helper(MOSQ_ERR_INVAL, "foo+", "foo+"); no_match_helper(MOSQ_ERR_INVAL, "+foo/bar", "+foo/bar"); no_match_helper(MOSQ_ERR_INVAL, "foo+/bar", "foo+/bar"); no_match_helper(MOSQ_ERR_INVAL, "foo/+bar", "foo/+bar"); no_match_helper(MOSQ_ERR_INVAL, "foo/bar+", "foo/bar+"); no_match_helper(MOSQ_ERR_INVAL, "+foo", "afoo"); no_match_helper(MOSQ_ERR_INVAL, "fo+o", "foao"); no_match_helper(MOSQ_ERR_INVAL, "foo+", "fooa"); no_match_helper(MOSQ_ERR_INVAL, "+foo/bar", "afoo/bar"); no_match_helper(MOSQ_ERR_INVAL, "foo+/bar", "fooa/bar"); no_match_helper(MOSQ_ERR_INVAL, "foo/+bar", "foo/abar"); no_match_helper(MOSQ_ERR_INVAL, "foo/bar+", "foo/bara"); no_match_helper(MOSQ_ERR_INVAL, "#foo", "#foo"); no_match_helper(MOSQ_ERR_INVAL, "fo#o", "fo#o"); no_match_helper(MOSQ_ERR_INVAL, "foo#", "foo#"); no_match_helper(MOSQ_ERR_INVAL, "#foo/bar", "#foo/bar"); no_match_helper(MOSQ_ERR_INVAL, "foo#/bar", "foo#/bar"); no_match_helper(MOSQ_ERR_INVAL, "foo/#bar", "foo/#bar"); no_match_helper(MOSQ_ERR_INVAL, "foo/bar#", "foo/bar#"); no_match_helper(MOSQ_ERR_INVAL, "foo+", "fooa"); no_match_helper(MOSQ_ERR_INVAL, "foo/+", "foo/+"); no_match_helper(MOSQ_ERR_INVAL, "foo/#", "foo/+"); no_match_helper(MOSQ_ERR_INVAL, "foo/+", "foo/bar/+"); no_match_helper(MOSQ_ERR_INVAL, "foo/#", "foo/bar/+"); no_match_helper(MOSQ_ERR_INVAL, "foo/+", "foo/#"); no_match_helper(MOSQ_ERR_INVAL, "foo/#", "foo/#"); no_match_helper(MOSQ_ERR_INVAL, "foo/+", "foo/bar/#"); no_match_helper(MOSQ_ERR_INVAL, "foo/#", "foo/bar/#"); } static void TEST_valid_no_matching(void) { no_match_helper(MOSQ_ERR_SUCCESS, "test/6/#", "test/3"); no_match_helper(MOSQ_ERR_SUCCESS, "foo/bar", "foo"); no_match_helper(MOSQ_ERR_SUCCESS, "foo/+", "foo/bar/baz"); no_match_helper(MOSQ_ERR_SUCCESS, "foo/+/baz", "foo/bar/bar"); no_match_helper(MOSQ_ERR_SUCCESS, "foo/+/#", "fo2/bar/baz"); no_match_helper(MOSQ_ERR_SUCCESS, "/#", "foo/bar"); no_match_helper(MOSQ_ERR_SUCCESS, "#", "$SYS/bar"); no_match_helper(MOSQ_ERR_SUCCESS, "$BOB/bar", "$SYS/bar"); } static void TEST_invalid(void) { no_match_helper(MOSQ_ERR_INVAL, "foo#", "foo"); no_match_helper(MOSQ_ERR_INVAL, "fo#o/", "foo"); no_match_helper(MOSQ_ERR_INVAL, "foo#", "fooa"); no_match_helper(MOSQ_ERR_INVAL, "foo+", "foo"); no_match_helper(MOSQ_ERR_INVAL, "foo/#a", "foo"); no_match_helper(MOSQ_ERR_INVAL, "#a", "foo"); no_match_helper(MOSQ_ERR_INVAL, "foo/#abc", "foo"); no_match_helper(MOSQ_ERR_INVAL, "#abc", "foo"); no_match_helper(MOSQ_ERR_INVAL, "/#a", "foo/bar"); } /* ======================================================================== * PUB TOPIC CHECK * ======================================================================== */ static void pub_topic_helper(const char *topic, int rc_expected) { int rc; rc = mosquitto_pub_topic_check(topic); CU_ASSERT_EQUAL(rc, rc_expected); rc = mosquitto_pub_topic_check2(topic, strlen(topic)); CU_ASSERT_EQUAL(rc, rc_expected); } static void TEST_pub_topic_valid(void) { pub_topic_helper("pub/topic", MOSQ_ERR_SUCCESS); pub_topic_helper("pub//topic", MOSQ_ERR_SUCCESS); pub_topic_helper("pub/ /topic", MOSQ_ERR_SUCCESS); } static void TEST_pub_topic_invalid(void) { pub_topic_helper("+pub/topic", MOSQ_ERR_INVAL); pub_topic_helper("pub+/topic", MOSQ_ERR_INVAL); pub_topic_helper("pub/+topic", MOSQ_ERR_INVAL); pub_topic_helper("pub/topic+", MOSQ_ERR_INVAL); pub_topic_helper("pub/topic/+", MOSQ_ERR_INVAL); pub_topic_helper("#pub/topic", MOSQ_ERR_INVAL); pub_topic_helper("pub#/topic", MOSQ_ERR_INVAL); pub_topic_helper("pub/#topic", MOSQ_ERR_INVAL); pub_topic_helper("pub/topic#", MOSQ_ERR_INVAL); pub_topic_helper("pub/topic/#", MOSQ_ERR_INVAL); pub_topic_helper("+/pub/topic", MOSQ_ERR_INVAL); } /* ======================================================================== * SUB TOPIC CHECK * ======================================================================== */ static void sub_topic_helper(const char *topic, int rc_expected) { int rc; rc = mosquitto_sub_topic_check(topic); CU_ASSERT_EQUAL(rc, rc_expected); rc = mosquitto_sub_topic_check2(topic, strlen(topic)); CU_ASSERT_EQUAL(rc, rc_expected); } static void TEST_sub_topic_valid(void) { sub_topic_helper("sub/topic", MOSQ_ERR_SUCCESS); sub_topic_helper("sub//topic", MOSQ_ERR_SUCCESS); sub_topic_helper("sub/ /topic", MOSQ_ERR_SUCCESS); sub_topic_helper("sub/+/topic", MOSQ_ERR_SUCCESS); sub_topic_helper("+/+/+", MOSQ_ERR_SUCCESS); sub_topic_helper("+", MOSQ_ERR_SUCCESS); sub_topic_helper("sub/topic/#", MOSQ_ERR_SUCCESS); sub_topic_helper("sub//topic/#", MOSQ_ERR_SUCCESS); sub_topic_helper("sub/ /topic/#", MOSQ_ERR_SUCCESS); sub_topic_helper("sub/+/topic/#", MOSQ_ERR_SUCCESS); sub_topic_helper("+/+/+/#", MOSQ_ERR_SUCCESS); sub_topic_helper("#", MOSQ_ERR_SUCCESS); } static void TEST_sub_topic_invalid(void) { sub_topic_helper("+sub/topic", MOSQ_ERR_INVAL); sub_topic_helper("sub+/topic", MOSQ_ERR_INVAL); sub_topic_helper("sub/+topic", MOSQ_ERR_INVAL); sub_topic_helper("sub/topic+", MOSQ_ERR_INVAL); sub_topic_helper("#sub/topic", MOSQ_ERR_INVAL); sub_topic_helper("sub#/topic", MOSQ_ERR_INVAL); sub_topic_helper("sub/#topic", MOSQ_ERR_INVAL); sub_topic_helper("sub/topic#", MOSQ_ERR_INVAL); sub_topic_helper("#/sub/topic", MOSQ_ERR_INVAL); } /* ======================================================================== * TEST SUITE SETUP * ======================================================================== */ int init_util_topic_tests(void) { CU_pSuite test_suite = NULL; test_suite = CU_add_suite("Util topic", NULL, NULL); if(!test_suite){ printf("Error adding CUnit util topic test suite.\n"); return 1; } if(0 || !CU_add_test(test_suite, "Matching: Empty input", TEST_empty_input) || !CU_add_test(test_suite, "Matching: Valid matching", TEST_valid_matching) || !CU_add_test(test_suite, "Matching: Valid no matching", TEST_valid_no_matching) || !CU_add_test(test_suite, "Matching: Invalid but matching", TEST_invalid_but_matching) || !CU_add_test(test_suite, "Matching: Invalid", TEST_invalid) || !CU_add_test(test_suite, "Pub topic: Valid", TEST_pub_topic_valid) || !CU_add_test(test_suite, "Pub topic: Invalid", TEST_pub_topic_invalid) || !CU_add_test(test_suite, "Sub topic: Valid", TEST_sub_topic_valid) || !CU_add_test(test_suite, "Sub topic: Invalid", TEST_sub_topic_invalid) ){ printf("Error adding util topic CUnit tests.\n"); return 1; } return 0; } mosquitto-2.0.18/travis-configure.sh000077500000000000000000000001531450213760600174770ustar00rootroot00000000000000#!/bin/bash if [ "$TRAVIS_OS_NAME" == "osx" ]; then cmake -DOPENSSL_ROOT_DIR=/usr/local/opt/openssl . fi mosquitto-2.0.18/travis-install.sh000077500000000000000000000007331450213760600171700ustar00rootroot00000000000000#!/bin/bash if [ "$TRAVIS_OS_NAME" == "linux" ]; then sudo apt-get update -qq sudo apt-get install -y debhelper libc-ares-dev libssl-dev libwrap0-dev python-all python3-all uthash-dev xsltproc docbook-xsl libcunit1-dev git clone https://github.com/DaveGamble/cJSON make -C cJSON sudo make PREFIX=/usr -C cJSON install fi if [ "$TRAVIS_OS_NAME" == "osx" ]; then HOMEBREW_NO_AUTO_UPDATE=1 brew install c-ares cjson openssl libwebsockets fi sudo pip install paho-mqtt mosquitto-2.0.18/www/000077500000000000000000000000001450213760600144765ustar00rootroot00000000000000mosquitto-2.0.18/www/README.md000066400000000000000000000001151450213760600157520ustar00rootroot00000000000000This is the mosquitto website, it can be built with `nikola`: `nikola build`mosquitto-2.0.18/www/conf.py000066400000000000000000002014041450213760600157760ustar00rootroot00000000000000# -*- coding: utf-8 -*- from __future__ import unicode_literals import time # !! This is the configuration of Nikola. !! # # !! You should edit it to your liking. !! # # ! Some settings can be different in different languages. # ! A comment stating (translatable) is used to denote those. # ! There are two ways to specify a translatable setting: # ! (a) BLOG_TITLE = "My Blog" # ! (b) BLOG_TITLE = {"en": "My Blog", "es": "Mi Blog"} # ! Option (a) is used when you don't want that setting translated. # ! Option (b) is used for settings that are different in different languages. # Data about this site BLOG_AUTHOR = "Mosquitto Project" # (translatable) BLOG_TITLE = "Eclipse Mosquitto" # (translatable) # This is the main URL for your site. It will be used # in a prominent link. Don't forget the protocol (http/https)! SITE_URL = "https://mosquitto.org/" # This is the URL where Nikola's output will be deployed. # If not set, defaults to SITE_URL # BASE_URL = "https://example.com/" BLOG_EMAIL = "roger@atchoo.org" BLOG_DESCRIPTION = "An open source MQTT server" # (translatable) # What is the default language? DEFAULT_LANG = "en" # What other languages do you have? # The format is {"translationcode" : "path/to/translation" } # the path will be used as a prefix for the generated pages location TRANSLATIONS = { DEFAULT_LANG: "", # Example for another language: # "es": "./es", } # What will translated input files be named like? # If you have a page something.rst, then something.pl.rst will be considered # its Polish translation. # (in the above example: path == "something", ext == "rst", lang == "pl") # this pattern is also used for metadata: # something.meta -> something.pl.meta TRANSLATIONS_PATTERN = "{path}.{lang}.{ext}" # Links for the sidebar / navigation bar. (translatable) # This is a dict. The keys are languages, and values are tuples. # # For regular links: # ('https://getnikola.com/', 'Nikola Homepage') # # For submenus: # ( # ( # ('https://apple.com/', 'Apple'), # ('https://orange.com/', 'Orange'), # ), # 'Fruits' # ) # # WARNING: Support for submenus is theme-dependent. # Only one level of submenus is supported. # WARNING: Some themes, including the default Bootstrap 3 theme, # may present issues if the menu is too large. # (in bootstrap3, the navbar can grow too large and cover contents.) # WARNING: If you link to directories, make sure to follow # ``STRIP_INDEXES``. If it’s set to ``True``, end your links # with a ``/``, otherwise end them with ``/index.html`` — or # else they won’t be highlighted when active. NAVIGATION_LINKS = { DEFAULT_LANG: ( ("/", "Home"), #("/about/", "About"), ("/blog/", "Blog"), ("/download/", "Download"), #("/development/", "Development"), #("/community/", "Community"), #("/sponsoring/", "Sponsoring"), ( ( ("/documentation/", "All"), ("/roadmap/", "Roadmap"), ("/api/", "API"), ("/man/libmosquitto-3.html", "libmosquitto"), ("/man/mosquitto-8.html", "mosquitto"), ("/man/mosquitto-conf-5.html", "mosquitto.conf"), ("/man/mosquitto_ctrl-1.html", "mosquitto_ctrl"), ("/man/mosquitto_ctrl_dynsec-1.html", "mosquitto_ctrl_dynsec"), ("/man/mosquitto_passwd-1.html", "mosquitto_passwd"), ("/man/mosquitto_pub-1.html", "mosquitto_pub"), ("/man/mosquitto_rr-1.html", "mosquitto_rr"), ("/man/mosquitto_sub-1.html", "mosquitto_sub"), ("/man/mosquitto-tls-7.html", "mosquitto-tls"), ("/man/mqtt-7.html", "mqtt"), ), "Documentation", ) ), } # Name of the theme to use. THEME = "mosquitto" #THEME = "bootstrap3" # Primary color of your theme. This will be used to customize your theme and # auto-generate related colors in POSTS_SECTION_COLORS. Must be a HEX value. THEME_COLOR = '#3c5280' # POSTS and PAGES contains (wildcard, destination, template) tuples. # (translatable) # # The wildcard is used to generate a list of source files # (whatever/thing.rst, for example). # # That fragment could have an associated metadata file (whatever/thing.meta), # and optionally translated files (example for Spanish, with code "es"): # whatever/thing.es.rst and whatever/thing.es.meta # # This assumes you use the default TRANSLATIONS_PATTERN. # # From those files, a set of HTML fragment files will be generated: # cache/whatever/thing.html (and maybe cache/whatever/thing.html.es) # # These files are combined with the template to produce rendered # pages, which will be placed at # output/TRANSLATIONS[lang]/destination/pagename.html # # where "pagename" is the "slug" specified in the metadata file. # The page might also be placed in /destination/pagename/index.html # if PRETTY_URLS are enabled. # # The difference between POSTS and PAGES is that POSTS are added # to feeds, indexes, tag lists and archives and are considered part # of a blog, while PAGES are just independent HTML pages. # # Finally, note that destination can be translated, i.e. you can # specify a different translation folder per language. Example: # PAGES = ( # ("pages/*.rst", {"en": "pages", "de": "seiten"}, "story.tmpl"), # ("pages/*.md", {"en": "pages", "de": "seiten"}, "story.tmpl"), # ) POSTS = ( ("posts/*.rst", "blog", "post.tmpl"), ("posts/*.txt", "blog", "post.tmpl"), ("posts/*.html", "blog", "post.tmpl"), ("posts/*.md", "blog", "post.tmpl"), ) PAGES = ( ("pages/*.rst", "", "story.tmpl"), ("pages/*.txt", "", "story.tmpl"), ("pages/*.html", "", "story.tmpl"), ("pages/*.md", "", "story.tmpl"), ("pages/documentation/*.md", "", "story.tmpl"), ("man/*.xml", "man", "story.tmpl"), ) # Below this point, everything is optional # Post's dates are considered in UTC by default, if you want to use # another time zone, please set TIMEZONE to match. Check the available # list from Wikipedia: # https://en.wikipedia.org/wiki/List_of_tz_database_time_zones # (e.g. 'Europe/Zurich') # Also, if you want to use a different time zone in some of your posts, # you can use the ISO 8601/RFC 3339 format (ex. 2012-03-30T23:00:00+02:00) TIMEZONE = "Europe/London" # If you want to use ISO 8601 (also valid RFC 3339) throughout Nikola # (especially in new_post), set this to True. # Note that this does not affect DATE_FORMAT. # FORCE_ISO8601 = False # Date format used to display post dates. (translatable) # (str used by datetime.datetime.strftime) # DATE_FORMAT = '%Y-%m-%d %H:%M' # Date format used to display post dates, if local dates are used. (translatable) # (str used by moment.js) # JS_DATE_FORMAT = 'YYYY-MM-DD HH:mm' # Date fanciness. # # 0 = using DATE_FORMAT and TIMEZONE # 1 = using JS_DATE_FORMAT and local user time (via moment.js) # 2 = using a string like “2 days ago” # # Your theme must support it, bootstrap and bootstrap3 already do. # DATE_FANCINESS = 0 # While Nikola can select a sensible locale for each language, # sometimes explicit control can come handy. # In this file we express locales in the string form that # python's locales will accept in your OS, by example # "en_US.utf8" in Unix-like OS, "English_United States" in Windows. # LOCALES = dict mapping language --> explicit locale for the languages # in TRANSLATIONS. You can omit one or more keys. # LOCALE_FALLBACK = locale to use when an explicit locale is unavailable # LOCALE_DEFAULT = locale to use for languages not mentioned in LOCALES; if # not set the default Nikola mapping is used. # LOCALES = {} # LOCALE_FALLBACK = None # LOCALE_DEFAULT = None # One or more folders containing files to be copied as-is into the output. # The format is a dictionary of {source: relative destination}. # Default is: # FILES_FOLDERS = {'files': ''} # Which means copy 'files' into 'output' # One or more folders containing code listings to be processed and published on # the site. The format is a dictionary of {source: relative destination}. # Default is: # LISTINGS_FOLDERS = {'listings': 'listings'} # Which means process listings from 'listings' into 'output/listings' # A mapping of languages to file-extensions that represent that language. # Feel free to add or delete extensions to any list, but don't add any new # compilers unless you write the interface for it yourself. # # 'rest' is reStructuredText # 'markdown' is MarkDown # 'html' assumes the file is HTML and just copies it COMPILERS = { "rest": ('.rst', '.txt'), "markdown": ('.md', '.mdown', '.markdown'), "textile": ('.textile',), "txt2tags": ('.t2t',), "bbcode": ('.bb',), "wiki": ('.wiki',), "ipynb": ('.ipynb',), "html": ('.html', '.htm'), # PHP files are rendered the usual way (i.e. with the full templates). # The resulting files have .php extensions, making it possible to run # them without reconfiguring your server to recognize them. "php": ('.php',), # Pandoc detects the input from the source filename # but is disabled by default as it would conflict # with many of the others. "docbookmanpage": ('.xml',), } # Create by default posts in one file format? # Set to False for two-file posts, with separate metadata. # ONE_FILE_POSTS = True # Use date-based path when creating posts? # Can be enabled on a per-post basis with `nikola new_post -d`. # The setting is ignored when creating pages (`-d` still works). NEW_POST_DATE_PATH = True # What format to use when creating posts with date paths? # Default is '%Y/%m/%d', other possibilities include '%Y' or '%Y/%m'. NEW_POST_DATE_PATH_FORMAT = '%Y/%m' # If this is set to True, the DEFAULT_LANG version will be displayed for # untranslated posts. # If this is set to False, then posts that are not translated to a language # LANG will not be visible at all in the pages in that language. # Formerly known as HIDE_UNTRANSLATED_POSTS (inverse) # SHOW_UNTRANSLATED_POSTS = True # Nikola supports logo display. If you have one, you can put the URL here. # Final output is . # The URL may be relative to the site root. # LOGO_URL = '' # If you want to hide the title of your website (for example, if your logo # already contains the text), set this to False. SHOW_BLOG_TITLE = False # Writes tag cloud data in form of tag_cloud_data.json. # Warning: this option will change its default value to False in v8! WRITE_TAG_CLOUD = True # Generate pages for each section. The site must have at least two sections # for this option to take effect. It wouldn't build for just one section. POSTS_SECTIONS = True # Setting this to False generates a list page instead of an index. Indexes # are the default and will apply GENERATE_ATOM if set. # POSTS_SECTIONS_ARE_INDEXES = True # Final locations are: # output / TRANSLATION[lang] / SECTION_PATH / SECTION_NAME / index.html (list of posts for a section) # output / TRANSLATION[lang] / SECTION_PATH / SECTION_NAME / rss.xml (RSS feed for a section) # (translatable) # SECTION_PATH = "" # Each post and section page will have an associated color that can be used # to style them with a recognizable color detail across your site. A color # is assigned to each section based on shifting the hue of your THEME_COLOR # at least 7.5 % while leaving the lightness and saturation untouched in the # HUSL colorspace. You can overwrite colors by assigning them colors in HEX. # POSTS_SECTION_COLORS = { # DEFAULT_LANG: { # 'posts': '#49b11bf', # 'reviews': '#ffe200', # }, # } # Associate a description with a section. For use in meta description on # section index pages or elsewhere in themes. # POSTS_SECTION_DESCRIPTIONS = { # DEFAULT_LANG: { # 'how-to': 'Learn how-to things properly with these amazing tutorials.', # }, # } # Sections are determined by their output directory as set in POSTS by default, # but can alternatively be determined from file metadata instead. # POSTS_SECTION_FROM_META = False # Names are determined from the output directory name automatically or the # metadata label. Unless overwritten below, names will use title cased and # hyphens replaced by spaces. # POSTS_SECTION_NAME = { # DEFAULT_LANG: { # 'posts': 'Blog Posts', # 'uncategorized': 'Odds and Ends', # }, # } # Titles for per-section index pages. Can be either one string where "{name}" # is substituted or the POSTS_SECTION_NAME, or a dict of sections. Note # that the INDEX_PAGES option is also applied to section page titles. # POSTS_SECTION_TITLE = { # DEFAULT_LANG: { # 'how-to': 'How-to and Tutorials', # }, # } # Paths for different autogenerated bits. These are combined with the # translation paths. # Final locations are: # output / TRANSLATION[lang] / TAG_PATH / index.html (list of tags) # output / TRANSLATION[lang] / TAG_PATH / tag.html (list of posts for a tag) # output / TRANSLATION[lang] / TAG_PATH / tag.xml (RSS feed for a tag) # (translatable) TAG_PATH = "blog/categories" # By default, the list of tags is stored in # output / TRANSLATION[lang] / TAG_PATH / index.html # (see explanation for TAG_PATH). This location can be changed to # output / TRANSLATION[lang] / TAGS_INDEX_PATH # with an arbitrary relative path TAGS_INDEX_PATH. # (translatable) # TAGS_INDEX_PATH = "tags.html" # If TAG_PAGES_ARE_INDEXES is set to True, each tag's page will contain # the posts themselves. If set to False, it will be just a list of links. # TAG_PAGES_ARE_INDEXES = False # Set descriptions for tag pages to make them more interesting. The # default is no description. The value is used in the meta description # and displayed underneath the tag list or index page’s title. # TAG_PAGES_DESCRIPTIONS = { # DEFAULT_LANG: { # "blogging": "Meta-blog posts about blogging about blogging.", # "open source": "My contributions to my many, varied, ever-changing, and eternal libre software projects." # }, # } # Set special titles for tag pages. The default is "Posts about TAG". # TAG_PAGES_TITLES = { # DEFAULT_LANG: { # "blogging": "Meta-posts about blogging", # "open source": "Posts about open source software" # }, # } # If you do not want to display a tag publicly, you can mark it as hidden. # The tag will not be displayed on the tag list page, the tag cloud and posts. # Tag pages will still be generated. HIDDEN_TAGS = ['mathjax'] # Only include tags on the tag list/overview page if there are at least # TAGLIST_MINIMUM_POSTS number of posts or more with every tag. Every tag # page is still generated, linked from posts, and included in the sitemap. # However, more obscure tags can be hidden from the tag index page. # TAGLIST_MINIMUM_POSTS = 1 # Final locations are: # output / TRANSLATION[lang] / CATEGORY_PATH / index.html (list of categories) # output / TRANSLATION[lang] / CATEGORY_PATH / CATEGORY_PREFIX category.html (list of posts for a category) # output / TRANSLATION[lang] / CATEGORY_PATH / CATEGORY_PREFIX category.xml (RSS feed for a category) # (translatable) # CATEGORY_PATH = "categories" # CATEGORY_PREFIX = "cat_" # By default, the list of categories is stored in # output / TRANSLATION[lang] / CATEGORY_PATH / index.html # (see explanation for CATEGORY_PATH). This location can be changed to # output / TRANSLATION[lang] / CATEGORIES_INDEX_PATH # with an arbitrary relative path CATEGORIES_INDEX_PATH. # (translatable) # CATEGORIES_INDEX_PATH = "categories.html" # If CATEGORY_ALLOW_HIERARCHIES is set to True, categories can be organized in # hierarchies. For a post, the whole path in the hierarchy must be specified, # using a forward slash ('/') to separate paths. Use a backslash ('\') to escape # a forward slash or a backslash (i.e. '\//\\' is a path specifying the # subcategory called '\' of the top-level category called '/'). CATEGORY_ALLOW_HIERARCHIES = False # If CATEGORY_OUTPUT_FLAT_HIERARCHY is set to True, the output written to output # contains only the name of the leaf category and not the whole path. CATEGORY_OUTPUT_FLAT_HIERARCHY = False # If CATEGORY_PAGES_ARE_INDEXES is set to True, each category's page will contain # the posts themselves. If set to False, it will be just a list of links. # CATEGORY_PAGES_ARE_INDEXES = False # Set descriptions for category pages to make them more interesting. The # default is no description. The value is used in the meta description # and displayed underneath the category list or index page’s title. # CATEGORY_PAGES_DESCRIPTIONS = { # DEFAULT_LANG: { # "blogging": "Meta-blog posts about blogging about blogging.", # "open source": "My contributions to my many, varied, ever-changing, and eternal libre software projects." # }, # } # Set special titles for category pages. The default is "Posts about CATEGORY". # CATEGORY_PAGES_TITLES = { # DEFAULT_LANG: { # "blogging": "Meta-posts about blogging", # "open source": "Posts about open source software" # }, # } # If you do not want to display a category publicly, you can mark it as hidden. # The category will not be displayed on the category list page. # Category pages will still be generated. HIDDEN_CATEGORIES = [] # If ENABLE_AUTHOR_PAGES is set to True and there is more than one # author, author pages are generated. # ENABLE_AUTHOR_PAGES = True # Path to author pages. Final locations are: # output / TRANSLATION[lang] / AUTHOR_PATH / index.html (list of authors) # output / TRANSLATION[lang] / AUTHOR_PATH / author.html (list of posts by an author) # output / TRANSLATION[lang] / AUTHOR_PATH / author.xml (RSS feed for an author) # (translatable) AUTHOR_PATH = "blog/authors" # If AUTHOR_PAGES_ARE_INDEXES is set to True, each author's page will contain # the posts themselves. If set to False, it will be just a list of links. # AUTHOR_PAGES_ARE_INDEXES = False # Set descriptions for author pages to make them more interesting. The # default is no description. The value is used in the meta description # and displayed underneath the author list or index page’s title. # AUTHOR_PAGES_DESCRIPTIONS = { # DEFAULT_LANG: { # "Juanjo Conti": "Python coder and writer.", # "Roberto Alsina": "Nikola father." # }, # } # If you do not want to display an author publicly, you can mark it as hidden. # The author will not be displayed on the author list page and posts. # Tag pages will still be generated. HIDDEN_AUTHORS = ['Guest'] # Final location for the main blog page and sibling paginated pages is # output / TRANSLATION[lang] / INDEX_PATH / index-*.html # (translatable) INDEX_PATH = "blog" # Optional HTML that displayed on “main” blog index.html files. # May be used for a greeting. (translatable) FRONT_INDEX_HEADER = { DEFAULT_LANG: '' } # Create per-month archives instead of per-year # CREATE_MONTHLY_ARCHIVE = False # Create one large archive instead of per-year # CREATE_SINGLE_ARCHIVE = False # Create year, month, and day archives each with a (long) list of posts # (overrides both CREATE_MONTHLY_ARCHIVE and CREATE_SINGLE_ARCHIVE) # CREATE_FULL_ARCHIVES = False # If monthly archives or full archives are created, adds also one archive per day # CREATE_DAILY_ARCHIVE = False # Create previous, up, next navigation links for archives # CREATE_ARCHIVE_NAVIGATION = False # Final locations for the archives are: # output / TRANSLATION[lang] / ARCHIVE_PATH / ARCHIVE_FILENAME # output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / index.html # output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / MONTH / index.html # output / TRANSLATION[lang] / ARCHIVE_PATH / YEAR / MONTH / DAY / index.html # ARCHIVE_PATH = "" # ARCHIVE_FILENAME = "archive.html" # If ARCHIVES_ARE_INDEXES is set to True, each archive page which contains a list # of posts will contain the posts themselves. If set to False, it will be just a # list of links. # ARCHIVES_ARE_INDEXES = False # URLs to other posts/pages can take 3 forms: # rel_path: a relative URL to the current page/post (default) # full_path: a URL with the full path from the root # absolute: a complete URL (that includes the SITE_URL) # URL_TYPE = 'rel_path' # If USE_BASE_TAG is True, then all HTML files will include # something like to help # the browser resolve relative links. # Most people don’t need this tag; major websites don’t use it. Use # only if you know what you’re doing. If this is True, your website # will not be fully usable by manually opening .html files in your web # browser (`nikola serve` or `nikola auto` is mandatory). Also, if you # have mirrors of your site, they will point to SITE_URL everywhere. USE_BASE_TAG = False # Final location for the blog main RSS feed is: # output / TRANSLATION[lang] / RSS_PATH / rss.xml # (translatable) # RSS_PATH = "" # Slug the Tag URL. Easier for users to type, special characters are # often removed or replaced as well. # SLUG_TAG_PATH = True # Slug the Author URL. Easier for users to type, special characters are # often removed or replaced as well. # SLUG_AUTHOR_PATH = True # A list of redirection tuples, [("foo/from.html", "/bar/to.html")]. # # A HTML file will be created in output/foo/from.html that redirects # to the "/bar/to.html" URL. notice that the "from" side MUST be a # relative URL. # # If you don't need any of these, just set to [] REDIRECTIONS = [ \ ["2009/12/version-0-2-released/index.html", "/blog/2009/12/version-0-2-released"], \ ["2009/12/version-0-3-released/index.html", "/blog/2009/12/version-0-3-released"], \ ["2010/01/mailing-list-irc/index.html", "/blog/2010/01/mailing-list-irc"], \ ["2010/01/version-0-4-1-released/index.html", "/blog/2010/01/version-0-4-1-released"], \ ["2010/01/version-0-4-released/index.html", "/blog/2010/01/version-0-4-released"], \ ["2010/02/version-0-4-2-released/index.html", "/blog/2010/02/version-0-4-2-released"], \ ["2010/03/google-powermeter/index.html", "/blog/2010/03/google-powermeter"], \ ["2010/03/upgrading-to-0-5-1/index.html", "/blog/2010/03/upgrading-to-0-5-1"], \ ["2010/03/version-0-5-1-released/index.html", "/blog/2010/03/version-0-5-1-released"], \ ["2010/03/version-0-5-2-released/index.html", "/blog/2010/03/version-0-5-2-released"], \ ["2010/03/version-0-5-3-released/index.html", "/blog/2010/03/version-0-5-3-released"], \ ["2010/03/version-0-5-4-released/index.html", "/blog/2010/03/version-0-5-4-released"], \ ["2010/04/help-wanted-rpm-packaging/index.html", "/blog/2010/04/help-wanted-rpm-packaging"], \ ["2010/04/mind-control-mqtt/index.html", "/blog/2010/04/mind-control-mqtt"], \ ["2010/04/oggcamp/index.html", "/blog/2010/04/oggcamp"], \ ["2010/05/fedora-packages-available/index.html", "/blog/2010/05/fedora-packages-available"], \ ["2010/05/gentoo-ebuilds-available/index.html", "/blog/2010/05/gentoo-ebuilds-available"], \ ["2010/05/mosquitto-org/index.html", "/blog/2010/05/mosquitto-org"], \ ["2010/05/mqtt-push-on-android/index.html", "/blog/2010/05/mqtt-push-on-android"], \ ["2010/05/mqtt-wiki/index.html", "/blog/2010/05/mqtt-wiki"], \ ["2010/05/version-0-6-1-released/index.html", "/blog/2010/05/version-0-6-1-released"], \ ["2010/05/version-0-6-released/index.html", "/blog/2010/05/version-0-6-released"], \ ["2010/06/automation-has-the-oven-warmed-up-yet/index.html", "/blog/2010/06/automation-has-the-oven-warmed-up-yet"], \ ["2010/06/google-powermeter-step-by-step/index.html", "/blog/2010/06/google-powermeter-step-by-step"], \ ["2010/06/mosquitto-0-7rc1/index.html", "/blog/2010/06/mosquitto-0-7rc1"], \ ["2010/06/version-0-7-released/index.html", "/blog/2010/06/version-0-7-released"], \ ["2010/07/mosquitto-on-opensuse-11-3/index.html", "/blog/2010/07/mosquitto-on-opensuse-11-3"], \ ["2010/07/mqtt-client-library/index.html", "/blog/2010/07/mqtt-client-library"], \ ["2010/08/compiling-mosquitto-on-mac-os-x/index.html", "/blog/2010/08/compiling-mosquitto-on-mac-os-x"], \ ["2010/08/mosquitto-running-on-mac-os-x/index.html", "/blog/2010/08/mosquitto-running-on-mac-os-x"], \ ["2010/08/mqtt-v3-1/index.html", "/blog/2010/08/mqtt-v3-1"], \ ["2010/08/version-0-8-1-released/index.html", "/blog/2010/08/version-0-8-1-released"], \ ["2010/08/version-0-8-2/index.html", "/blog/2010/08/version-0-8-2"], \ ["2010/08/version-0-8-released/index.html", "/blog/2010/08/version-0-8-released"], \ ["2010/09/debian-packages/index.html", "/blog/2010/09/debian-packages"], \ ["2010/09/mqtt-with-php/index.html", "/blog/2010/09/mqtt-with-php"], \ ["2010/10/man-page-translations/index.html", "/blog/2010/10/man-page-translations"], \ ["2010/10/one-year-old/index.html", "/blog/2010/10/one-year-old"], \ ["2010/10/version-0-8-3-released/index.html", "/blog/2010/10/version-0-8-3-released"], \ ["2010/11/distro-packaging/index.html", "/blog/2010/11/distro-packaging"], \ ["2010/11/mosquitto-0-9test2/index.html", "/blog/2010/11/mosquitto-0-9test2"], \ ["2010/11/version-0-9-released/index.html", "/blog/2010/11/version-0-9-released"], \ ["2010/12/version-0-9-1-released/index.html", "/blog/2010/12/version-0-9-1-released"], \ ["2011/01/mosquitto-for-slackware/index.html", "/blog/2011/01/mosquitto-for-slackware"], \ ["2011/01/mqtt-news/index.html", "/blog/2011/01/mqtt-news"], \ ["2011/02/lightweight-messaging-and-linux/index.html", "/blog/2011/02/lightweight-messaging-and-linux"], \ ["2011/02/mosquitto-on-maemo/index.html", "/blog/2011/02/mosquitto-on-maemo"], \ ["2011/02/mqtt-on-android/index.html", "/blog/2011/02/mqtt-on-android"], \ ["2011/02/version-0-9-2-released/index.html", "/blog/2011/02/version-0-9-2-released"], \ ["2011/03/api-documentation/index.html", "/blog/2011/03/api-documentation"], \ ["2011/03/mosquitto-in-mac-homebrew/index.html", "/blog/2011/03/mosquitto-in-mac-homebrew"], \ ["2011/03/version-0-9-3-released/index.html", "/blog/2011/03/version-0-9-3-released"], \ ["2011/04/version-0-10-released/index.html", "/blog/2011/04/version-0-10-released"], \ ["2011/05/mqtt-ontology/index.html", "/blog/2011/05/mqtt-ontology"], \ ["2011/05/version-0-10-1-released/index.html", "/blog/2011/05/version-0-10-1-released"], \ ["2011/06/nanode-a-cheap-networked-arduino-clone/index.html", "/blog/2011/06/nanode-a-cheap-networked-arduino-clone"], \ ["2011/06/version-0-10-2-released/index.html", "/blog/2011/06/version-0-10-2-released"], \ ["2011/06/version-0-11-1-released/index.html", "/blog/2011/06/version-0-11-1-released"], \ ["2011/06/version-0-11-2-released/index.html", "/blog/2011/06/version-0-11-2-released"], \ ["2011/06/version-0-11-released/index.html", "/blog/2011/06/version-0-11-released"], \ ["2011/07/debian-and-ubuntu-packaging/index.html", "/blog/2011/07/debian-and-ubuntu-packaging"], \ ["2011/07/lua-mqtt-client/index.html", "/blog/2011/07/lua-mqtt-client"], \ ["2011/07/mosquitto-on-qnx/index.html", "/blog/2011/07/mosquitto-on-qnx"], \ ["2011/07/version-0-11-3-released/index.html", "/blog/2011/07/version-0-11-3-released"], \ ["2011/07/version-0-12-released/index.html", "/blog/2011/07/version-0-12-released"], \ ["2011/07/wireshark-mqtt-decoder/index.html", "/blog/2011/07/wireshark-mqtt-decoder"], \ ["2011/08/arch-linux-package/index.html", "/blog/2011/08/arch-linux-package"], \ ["2011/08/facebook-using-mqtt/index.html", "/blog/2011/08/facebook-using-mqtt"], \ ["2011/08/mosquitto-on-openwrt/index.html", "/blog/2011/08/mosquitto-on-openwrt"], \ ["2011/08/mqtt-standardisation/index.html", "/blog/2011/08/mqtt-standardisation"], \ ["2011/09/version-0-13-released/index.html", "/blog/2011/09/version-0-13-released"], \ ["2011/10/mqtt-power-usage-on-android/index.html", "/blog/2011/10/mqtt-power-usage-on-android"], \ ["2011/10/two/index.html", "/blog/2011/10/two"], \ ["2011/11/android-mqtt-example-project/index.html", "/blog/2011/11/android-mqtt-example-project"], \ ["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"], \ ["2011/11/new-linux-repositories/index.html", "/blog/2011/11/new-linux-repositories"], \ ["2011/11/version-0-14-1-released/index.html", "/blog/2011/11/version-0-14-1-released"], \ ["2011/11/version-0-14-2-released/index.html", "/blog/2011/11/version-0-14-2-released"], \ ["2011/11/version-0-14-released/index.html", "/blog/2011/11/version-0-14-released"], \ ["2011/12/mqtt-on-nanode/index.html", "/blog/2011/12/mqtt-on-nanode"], \ ["2011/12/version-0-14-3-released/index.html", "/blog/2011/12/version-0-14-3-released"], \ ["2012/01/challenge-web-based-mqtt-graphing/index.html", "/blog/2012/01/challenge-web-based-mqtt-graphing"], \ ["2012/01/do-you-use-mqtt/index.html", "/blog/2012/01/do-you-use-mqtt"], \ ["2012/01/mosquitto-test-server/index.html", "/blog/2012/01/mosquitto-test-server"], \ ["2012/01/version-0-14-4-released/index.html", "/blog/2012/01/version-0-14-4-released"], \ ["2012/02/mqtt2pachube/index.html", "/blog/2012/02/mqtt2pachube"], \ ["2012/02/version-0-15-released/index.html", "/blog/2012/02/version-0-15-released"], \ ["2012/03/quick-start-guide-for-mqtt-with-pachube/index.html", "/blog/2012/03/quick-start-guide-for-mqtt-with-pachube"], \ ["2012/03/upcoming-incompatible-library-changes/index.html", "/blog/2012/03/upcoming-incompatible-library-changes"], \ ["2012/05/python-client-module-available-for-testing/index.html", "/blog/2012/05/python-client-module-available-for-testing"], \ ["2012/06/ipv6-on-test-server/index.html", "/blog/2012/06/ipv6-on-test-server"], \ ["2012/06/ssl-support-on-test-server/index.html", "/blog/2012/06/ssl-support-on-test-server"], \ ["2012/07/upcoming-release/index.html", "/blog/2012/07/upcoming-release"], \ ["2012/08/baby/index.html", "/blog/2012/08/baby"], \ ["2012/08/bugfix-coming-soon/index.html", "/blog/2012/08/bugfix-coming-soon"], \ ["2012/08/version-1-0-1-released/index.html", "/blog/2012/08/version-1-0-1-released"], \ ["2012/08/version-1-0-2-released/index.html", "/blog/2012/08/version-1-0-2-released"], \ ["2012/08/version-1-0-released/index.html", "/blog/2012/08/version-1-0-released"], \ ["2012/09/updating-password-files/index.html", "/blog/2012/09/updating-password-files"], \ ["2012/09/version-1-0-3-released/index.html", "/blog/2012/09/version-1-0-3-released"], \ ["2012/10/version-1-0-4-released/index.html", "/blog/2012/10/version-1-0-4-released"], \ ["2012/11/making-mosquitto-packages-for-debian-yourself/index.html", "/blog/2012/11/making-mosquitto-packages-for-debian-yourself"], \ ["2012/11/version-1-0-5-released/index.html", "/blog/2012/11/version-1-0-5-released"], \ ["2012/12/libmosquitto-go-bindings/index.html", "/blog/2012/12/libmosquitto-go-bindings"], \ ["2012/12/version-1-1-released/index.html", "/blog/2012/12/version-1-1-released"], \ ["2013/01/mosquitto-debian-repository/index.html", "/blog/2013/01/mosquitto-debian-repository"], \ ["2013/01/version-1-1-1-released/index.html", "/blog/2013/01/version-1-1-1-released"], \ ["2013/01/version-1-1-2-released/index.html", "/blog/2013/01/version-1-1-2-released"], \ ["2013/02/mqtt-standardisation-oasis-call-for-participation/index.html", "/blog/2013/02/mqtt-standardisation-oasis-call-for-participation"], \ ["2013/02/version-1-1-3-released/index.html", "/blog/2013/02/version-1-1-3-released"], \ ["2013/04/some-interesting-mqtt-things/index.html", "/blog/2013/04/some-interesting-mqtt-things"], \ ["2013/05/mosquitto-javascript-client-deprecated/index.html", "/blog/2013/05/mosquitto-javascript-client-deprecated"], \ ["2013/07/authentication-plugins/index.html", "/blog/2013/07/authentication-plugins"], \ ["2013/07/version-1-2-near-complete/index.html", "/blog/2013/07/version-1-2-near-complete"], \ ["2013/08/mosquitto-on-fedora/index.html", "/blog/2013/08/mosquitto-on-fedora"], \ ["2013/08/mqtt-watchdir/index.html", "/blog/2013/08/mqtt-watchdir"], \ ["2013/08/version-1-2-released/index.html", "/blog/2013/08/version-1-2-released"], \ ["2013/09/version-1-2-1-released/index.html", "/blog/2013/09/version-1-2-1-released"], \ ["2013/10/version-1-2-2-released/index.html", "/blog/2013/10/version-1-2-2-released"], \ ["2013/12/paho-mqtt-python-client/index.html", "/blog/2013/12/paho-mqtt-python-client"], \ ["2013/12/version-1-2-3-released/index.html", "/blog/2013/12/version-1-2-3-released"], \ ["2014/03/version-1-3-1-released/index.html", "/blog/2014/03/version-1-3-1-released"], \ ["2014/03/version-1-3-released/index.html", "/blog/2014/03/version-1-3-released"], \ ["2014/05/new-arrival/index.html", "/blog/2014/05/new-arrival"], \ ["2014/07/version-1-3-2-released/index.html", "/blog/2014/07/version-1-3-2-released"], \ ["2014/08/version-1-3-3-released/index.html", "/blog/2014/08/version-1-3-3-released"], \ ["2014/08/version-1-3-4-released/index.html", "/blog/2014/08/version-1-3-4-released"], \ ["2014/10/mosquitto-and-poodle/index.html", "/blog/2014/10/mosquitto-and-poodle"], \ ["2014/10/unintended-change-of-behaviour-in-1-3-4/index.html", "/blog/2014/10/unintended-change-of-behaviour-in-1-3-4"], \ ["2014/10/version-1-3-5-released/index.html", "/blog/2014/10/version-1-3-5-released"], \ ["2015/01/seeking-sponsorship/index.html", "/blog/2015/01/seeking-sponsorship"], \ ["2015/02/version-1-4-released/index.html", "/blog/2015/02/version-1-4-released"], \ ["2015/04/version-1-4-1-released/index.html", "/blog/2015/04/version-1-4-1-released"], \ ["2015/05/mosquitto-and-current-unreleased-libwebsockets-branch/index.html", "/blog/2015/05/mosquitto-and-current-unreleased-libwebsockets-branch"], \ ["2015/05/version-1-4-2-released/index.html", "/blog/2015/05/version-1-4-2-released"], \ ["2015/08/version-1-4-3-released/index.html", "/blog/2015/08/version-1-4-3-released"], \ ["2015/09/version-1-4-4-released/index.html", "/blog/2015/09/version-1-4-4-released"], \ ["2015/11/version-1-4-5-released/index.html", "/blog/2015/11/version-1-4-5-released"], \ ["2015/12/using-lets-encrypt-certificates-with-mosquitto/index.html", "/blog/2015/12/using-lets-encrypt-certificates-with-mosquitto"], \ ["2015/12/version-1-4-7-released/index.html", "/blog/2015/12/version-1-4-7-released"], \ ["2016/01/test6-mosquitto-org/index.html", "/blog/2016/01/test6-mosquitto-org"], \ ["2016/02/version-1-4-8-released/index.html", "/blog/2016/02/version-1-4-8-released"], \ ["2016/03/logo-contest-results-for-shortlisting/index.html", "/blog/2016/03/logo-contest-results-for-shortlisting"], \ ["2016/03/logo-contest/index.html", "/blog/2016/03/logo-contest"], \ ["2016/03/repository-moved-to-github/index.html", "/blog/2016/03/repository-moved-to-github"], \ ["2016/05/stickers/index.html", "/blog/2016/05/stickers"], \ ["2016/06/version-1-4-9-released/index.html", "/blog/2016/06/version-1-4-9-released"], \ ["2016/08/mqtt-v5-draft-features/index.html", "/blog/2016/08/mqtt-v5-draft-features"], \ ["2016/08/version-1-4-10-released/index.html", "/blog/2016/08/version-1-4-10-released"], \ ["2016/12/pre-christmas-update/index.html", "/blog/2016/12/pre-christmas-update"], \ ["2017/02/version-1-4-11-released/index.html", "/blog/2017/02/version-1-4-11-released"], \ ["2017/03/for-the-final-time/index.html", "/blog/2017/03/for-the-final-time"], \ ["2017/05/security-advisory-cve-2017-7650/index.html", "/blog/2017/05/security-advisory-cve-2017-7650"], \ ["2017/06/citing-eclipse-mosquitto/index.html", "/blog/2017/06/citing-eclipse-mosquitto"], \ ["2017/06/security-advisory-cve-2017-9868/index.html", "/blog/2017/06/security-advisory-cve-2017-9868"], \ ["2017/07/version-1-4-13-released/index.html", "/blog/2017/07/version-1-4-13-released"], \ ["2017/07/version-1-4-14-released/index.html", "/blog/2017/07/version-1-4-14-released"], \ ["2018/01/mosquitto-debian-repo-key-updated/index.html", "/blog/2018/01/mosquitto-debian-repo-key-updated"] \ ] ## Presets of commands to execute to deploy. Can be anything, for # example, you may use rsync: # "rsync -rav --delete output/ joe@my.site:/srv/www/site" # And then do a backup, or run `nikola ping` from the `ping` # plugin (`nikola plugin -i ping`). Or run `nikola check -l`. # You may also want to use github_deploy (see below). # You can define multiple presets and specify them as arguments # to `nikola deploy`. If no arguments are specified, a preset # named `default` will be executed. You can use as many presets # in a `nikola deploy` command as you like. # DEPLOY_COMMANDS = { # 'default': [ # "rsync -rav --delete output/ joe@my.site:/srv/www/site", # ] # } # github_deploy configuration # For more details, read the manual: # https://getnikola.com/handbook.html#deploying-to-github # You will need to configure the deployment branch on GitHub. GITHUB_SOURCE_BRANCH = 'src' GITHUB_DEPLOY_BRANCH = 'master' # The name of the remote where you wish to push to, using github_deploy. GITHUB_REMOTE_NAME = 'origin' # Whether or not github_deploy should commit to the source branch automatically # before deploying. GITHUB_COMMIT_SOURCE = True # Where the output site should be located # If you don't use an absolute path, it will be considered as relative # to the location of conf.py OUTPUT_FOLDER = '/home/mosqorg/site/mosquitto.org' # where the "cache" of partial generated content should be located # default: 'cache' # CACHE_FOLDER = 'cache' # Filters to apply to the output. # A directory where the keys are either: a file extensions, or # a tuple of file extensions. # # And the value is a list of commands to be applied in order. # # Each command must be either: # # A string containing a '%s' which will # be replaced with a filename. The command *must* produce output # in place. # # Or: # # A python callable, which will be called with the filename as # argument. # # By default, only .php files uses filters to inject PHP into # Nikola’s templates. All other filters must be enabled through FILTERS. # # Many filters are shipped with Nikola. A list is available in the manual: # # # from nikola import filters # FILTERS = { # ".html": [filters.typogrify], # ".js": [filters.closure_compiler], # ".jpg": ["jpegoptim --strip-all -m75 -v %s"], # } # Executable for the "yui_compressor" filter (defaults to 'yui-compressor'). # YUI_COMPRESSOR_EXECUTABLE = 'yui-compressor' # Executable for the "closure_compiler" filter (defaults to 'closure-compiler'). # CLOSURE_COMPILER_EXECUTABLE = 'closure-compiler' # Executable for the "optipng" filter (defaults to 'optipng'). # OPTIPNG_EXECUTABLE = 'optipng' # Executable for the "jpegoptim" filter (defaults to 'jpegoptim'). # JPEGOPTIM_EXECUTABLE = 'jpegoptim' # Executable for the "html_tidy_withconfig", "html_tidy_nowrap", # "html_tidy_wrap", "html_tidy_wrap_attr" and "html_tidy_mini" filters # (defaults to 'tidy5'). # HTML_TIDY_EXECUTABLE = 'tidy5' # Expert setting! Create a gzipped copy of each generated file. Cheap server- # side optimization for very high traffic sites or low memory servers. # GZIP_FILES = False # File extensions that will be compressed # GZIP_EXTENSIONS = ('.txt', '.htm', '.html', '.css', '.js', '.json', '.atom', '.xml') # Use an external gzip command? None means no. # Example: GZIP_COMMAND = "pigz -k {filename}" # GZIP_COMMAND = None # Make sure the server does not return a "Accept-Ranges: bytes" header for # files compressed by this option! OR make sure that a ranged request does not # return partial content of another representation for these resources. Do not # use this feature if you do not understand what this means. # Compiler to process LESS files. # LESS_COMPILER = 'lessc' # A list of options to pass to the LESS compiler. # Final command is: LESS_COMPILER LESS_OPTIONS file.less # LESS_OPTIONS = [] # Compiler to process Sass files. # SASS_COMPILER = 'sass' # A list of options to pass to the Sass compiler. # Final command is: SASS_COMPILER SASS_OPTIONS file.s(a|c)ss # SASS_OPTIONS = [] # ############################################################################# # Image Gallery Options # ############################################################################# # One or more folders containing galleries. The format is a dictionary of # {"source": "relative_destination"}, where galleries are looked for in # "source/" and the results will be located in # "OUTPUT_PATH/relative_destination/gallery_name" # Default is: # GALLERY_FOLDERS = {"galleries": "galleries"} # More gallery options: # THUMBNAIL_SIZE = 180 # MAX_IMAGE_SIZE = 1280 # USE_FILENAME_AS_TITLE = True # EXTRA_IMAGE_EXTENSIONS = [] # # If set to False, it will sort by filename instead. Defaults to True # GALLERY_SORT_BY_DATE = True # If set to True, EXIF data will be copied when an image is thumbnailed or # resized. (See also EXIF_WHITELIST) # PRESERVE_EXIF_DATA = False # If you have enabled PRESERVE_EXIF_DATA, this option lets you choose EXIF # fields you want to keep in images. (See also PRESERVE_EXIF_DATA) # # For a full list of field names, please see here: # http://www.cipa.jp/std/documents/e/DC-008-2012_E.pdf # # This is a dictionary of lists. Each key in the dictionary is the # name of a IDF, and each list item is a field you want to preserve. # If you have a IDF with only a '*' item, *EVERY* item in it will be # preserved. If you don't want to preserve anything in a IDF, remove it # from the setting. By default, no EXIF information is kept. # Setting the whitelist to anything other than {} implies # PRESERVE_EXIF_DATA is set to True # To preserve ALL EXIF data, set EXIF_WHITELIST to {"*": "*"} # EXIF_WHITELIST = {} # Some examples of EXIF_WHITELIST settings: # Basic image information: # EXIF_WHITELIST['0th'] = [ # "Orientation", # "XResolution", # "YResolution", # ] # If you want to keep GPS data in the images: # EXIF_WHITELIST['GPS'] = ["*"] # Embedded thumbnail information: # EXIF_WHITELIST['1st'] = ["*"] # Folders containing images to be used in normal posts or pages. # IMAGE_FOLDERS is a dictionary of the form {"source": "destination"}, # where "source" is the folder containing the images to be published, and # "destination" is the folder under OUTPUT_PATH containing the images copied # to the site. Thumbnail images will be created there as well. # To reference the images in your posts, include a leading slash in the path. # For example, if IMAGE_FOLDERS = {'images': 'images'}, write # # .. image:: /images/tesla.jpg # # See the Nikola Handbook for details (in the “Embedding Images” and # “Thumbnails” sections) # Images will be scaled down according to IMAGE_THUMBNAIL_SIZE and MAX_IMAGE_SIZE # options, but will have to be referenced manually to be visible on the site # (the thumbnail has ``.thumbnail`` added before the file extension by default, # but a different naming template can be configured with IMAGE_THUMBNAIL_FORMAT). IMAGE_FOLDERS = {'images': 'images'} # IMAGE_THUMBNAIL_SIZE = 400 # IMAGE_THUMBNAIL_FORMAT = '{name}.thumbnail{ext}' # ############################################################################# # HTML fragments and diverse things that are used by the templates # ############################################################################# # Data about post-per-page indexes. # INDEXES_PAGES defaults to ' old posts, page %d' or ' page %d' (translated), # depending on the value of INDEXES_PAGES_MAIN. # # (translatable) If the following is empty, defaults to BLOG_TITLE: # INDEXES_TITLE = "" # # (translatable) If the following is empty, defaults to ' [old posts,] page %d' (see above): # INDEXES_PAGES = "" # # If the following is True, INDEXES_PAGES is also displayed on the main (the # newest) index page (index.html): # INDEXES_PAGES_MAIN = False # # If the following is True, index-1.html has the oldest posts, index-2.html the # second-oldest posts, etc., and index.html has the newest posts. This ensures # that all posts on index-x.html will forever stay on that page, now matter how # many new posts are added. # If False, index-1.html has the second-newest posts, index-2.html the third-newest, # and index-n.html the oldest posts. When this is active, old posts can be moved # to other index pages when new posts are added. # INDEXES_STATIC = True # # (translatable) If PRETTY_URLS is set to True, this setting will be used to create # prettier URLs for index pages, such as page/2/index.html instead of index-2.html. # Valid values for this settings are: # * False, # * a list or tuple, specifying the path to be generated, # * a dictionary mapping languages to lists or tuples. # Every list or tuple must consist of strings which are used to combine the path; # for example: # ['page', '{number}', '{index_file}'] # The replacements # {number} --> (logical) page number; # {old_number} --> the page number inserted into index-n.html before (zero for # the main page); # {index_file} --> value of option INDEX_FILE # are made. # Note that in case INDEXES_PAGES_MAIN is set to True, a redirection will be created # for the full URL with the page number of the main page to the normal (shorter) main # page URL. # INDEXES_PRETTY_PAGE_URL = False # # If the following is true, a page range navigation will be inserted to indices. # Please note that this will undo the effect of INDEXES_STATIC, as all index pages # must be recreated whenever the number of pages changes. # SHOW_INDEX_PAGE_NAVIGATION = False # If the following is True, a meta name="generator" tag is added to pages. The # generator tag is used to specify the software used to generate the page # (it promotes Nikola). # META_GENERATOR_TAG = True # Color scheme to be used for code blocks. If your theme provides # "assets/css/code.css" this is ignored. Leave empty to disable. # Can be any of: # algol, algol_nu, autumn, borland, bw, colorful, default, emacs, friendly, # fruity, igor, lovelace, manni, monokai, murphy, native, paraiso-dark, # paraiso-light, pastie, perldoc, rrt, tango, trac, vim, vs, xcode # This list MAY be incomplete since pygments adds styles every now and then. # Check with list(pygments.styles.get_all_styles()) in an interpreter. # CODE_COLOR_SCHEME = 'default' # FAVICONS contains (name, file, size) tuples. # Used to create favicon link like this: # FAVICONS = ( ("icon", "/favicon-16x16.png", "16x16"), ("icon", "/favicon-32x32.png", "32x32"), ) # Show teasers (instead of full posts) in indexes? Defaults to False. # INDEX_TEASERS = False # HTML fragments with the Read more... links. # The following tags exist and are replaced for you: # {link} A link to the full post page. # {read_more} The string “Read more” in the current language. # {reading_time} An estimate of how long it will take to read the post. # {remaining_reading_time} An estimate of how long it will take to read the post, sans the teaser. # {min_remaining_read} The string “{remaining_reading_time} min remaining to read” in the current language. # {paragraph_count} The amount of paragraphs in the post. # {remaining_paragraph_count} The amount of paragraphs in the post, sans the teaser. # {post_title} The title of the post. # {{ A literal { (U+007B LEFT CURLY BRACKET) # }} A literal } (U+007D RIGHT CURLY BRACKET) # 'Read more...' for the index page, if INDEX_TEASERS is True (translatable) INDEX_READ_MORE_LINK = '

{read_more}…

' # 'Read more...' for the feeds, if FEED_TEASERS is True (translatable) FEED_READ_MORE_LINK = '

{read_more}… ({min_remaining_read})

' # Append a URL query to the FEED_READ_MORE_LINK in Atom and RSS feeds. Advanced # option used for traffic source tracking. # Minimum example for use with Piwik: "pk_campaign=feed" # The following tags exist and are replaced for you: # {feedRelUri} A relative link to the feed. # {feedFormat} The name of the syndication format. # Example using replacement for use with Google Analytics: # "utm_source={feedRelUri}&utm_medium=nikola_feed&utm_campaign={feedFormat}_feed" FEED_LINKS_APPEND_QUERY = False # A HTML fragment describing the license, for the sidebar. # (translatable) LICENSE = "" # I recommend using the Creative Commons' wizard: # https://creativecommons.org/choose/ # LICENSE = """ # # Creative Commons License BY-NC-SA""" # A small copyright notice for the page footer (in HTML). # (translatable) CONTENT_FOOTER = 'Contents © {date} {author} - Powered by Nikola {license}' # Things that will be passed to CONTENT_FOOTER.format(). This is done # for translatability, as dicts are not formattable. Nikola will # intelligently format the setting properly. # The setting takes a dict. The keys are languages. The values are # tuples of tuples of positional arguments and dicts of keyword arguments # to format(). For example, {'en': (('Hello'), {'target': 'World'})} # results in CONTENT_FOOTER['en'].format('Hello', target='World'). # WARNING: If you do not use multiple languages with CONTENT_FOOTER, this # still needs to be a dict of this format. (it can be empty if you # do not need formatting) # (translatable) CONTENT_FOOTER_FORMATS = { DEFAULT_LANG: ( (), { "email": BLOG_EMAIL, "author": BLOG_AUTHOR, "date": time.gmtime().tm_year, "license": LICENSE } ) } # A simple copyright tag for inclusion in RSS feeds that works just # like CONTENT_FOOTER and CONTENT_FOOTER_FORMATS RSS_COPYRIGHT = 'Contents © {date} {author} {license}' RSS_COPYRIGHT_PLAIN = 'Contents © {date} {author} {license}' RSS_COPYRIGHT_FORMATS = CONTENT_FOOTER_FORMATS # To use comments, you can choose between different third party comment # systems. The following comment systems are supported by Nikola: # disqus, facebook, googleplus, intensedebate, isso, livefyre, muut # You can leave this option blank to disable comments. COMMENT_SYSTEM = "" # And you also need to add your COMMENT_SYSTEM_ID which # depends on what comment system you use. The default is # "nikolademo" which is a test account for Disqus. More information # is in the manual. #COMMENT_SYSTEM_ID = "mosquitto" # Enable annotations using annotateit.org? # If set to False, you can still enable them for individual posts and pages # setting the "annotations" metadata. # If set to True, you can disable them for individual posts and pages using # the "noannotations" metadata. # ANNOTATIONS = False # Create index.html for page folders? # WARNING: if a page would conflict with the index file (usually # caused by setting slug to `index`), the PAGE_INDEX # will not be generated for that directory. # PAGE_INDEX = False # Enable comments on pages (i.e. not posts)? # COMMENTS_IN_PAGES = False # Enable comments on picture gallery pages? # COMMENTS_IN_GALLERIES = False # What file should be used for directory indexes? # Defaults to index.html # Common other alternatives: default.html for IIS, index.php # INDEX_FILE = "index.html" # If a link ends in /index.html, drop the index.html part. # http://mysite/foo/bar/index.html => http://mysite/foo/bar/ # (Uses the INDEX_FILE setting, so if that is, say, default.html, # it will instead /foo/default.html => /foo) # (Note: This was briefly STRIP_INDEX_HTML in v 5.4.3 and 5.4.4) STRIP_INDEXES = True # Should the sitemap list directories which only include other directories # and no files. # Default to True # If this is False # e.g. /2012 includes only /01, /02, /03, /04, ...: don't add it to the sitemap # if /2012 includes any files (including index.html)... add it to the sitemap # SITEMAP_INCLUDE_FILELESS_DIRS = True # List of files relative to the server root (!) that will be asked to be excluded # from indexing and other robotic spidering. * is supported. Will only be effective # if SITE_URL points to server root. The list is used to exclude resources from # /robots.txt and /sitemap.xml, and to inform search engines about /sitemapindex.xml. # ROBOTS_EXCLUSIONS = ["/archive.html", "/category/*.html"] # Instead of putting files in .html, put them in /index.html. # No web server configuration is required. Also enables STRIP_INDEXES. # This can be disabled on a per-page/post basis by adding # .. pretty_url: False # to the metadata. PRETTY_URLS = True # If True, publish future dated posts right away instead of scheduling them. # Defaults to False. # FUTURE_IS_NOW = False # If True, future dated posts are allowed in deployed output # Only the individual posts are published/deployed; not in indexes/sitemap # Generally, you want FUTURE_IS_NOW and DEPLOY_FUTURE to be the same value. # DEPLOY_FUTURE = False # If False, draft posts will not be deployed # DEPLOY_DRAFTS = True # Allows scheduling of posts using the rule specified here (new_post -s) # Specify an iCal Recurrence Rule: http://www.kanzaki.com/docs/ical/rrule.html # SCHEDULE_RULE = '' # If True, use the scheduling rule to all posts by default # SCHEDULE_ALL = False # Do you want a add a Mathjax config file? # MATHJAX_CONFIG = "" # If you want support for the $.$ syntax (which may conflict with running # text!), just use this config: # MATHJAX_CONFIG = """ # # """ # Want to use KaTeX instead of MathJax? While KaTeX may not support every # feature yet, it's faster and the output looks better. # USE_KATEX = False # KaTeX auto-render settings. If you want support for the $.$ syntax (wihch may # conflict with running text!), just use this config: # KATEX_AUTO_RENDER = """ # delimiters: [ # {left: "$$", right: "$$", display: true}, # {left: "\\\[", right: "\\\]", display: true}, # {left: "$", right: "$", display: false}, # {left: "\\\(", right: "\\\)", display: false} # ] # """ # Do you want to customize the nbconversion of your IPython notebook? # IPYNB_CONFIG = {} # With the following example configuration you can use a custom jinja template # called `toggle.tpl` which has to be located in your site/blog main folder: # IPYNB_CONFIG = {'Exporter':{'template_file': 'toggle'}} # What Markdown extensions to enable? # You will also get gist, nikola and podcast because those are # done in the code, hope you don't mind ;-) # Note: most Nikola-specific extensions are done via the Nikola plugin system, # with the MarkdownExtension class and should not be added here. # The default is ['fenced_code', 'codehilite'] #MARKDOWN_EXTENSIONS = ['fenced_code', 'codehilite', 'extra', 'toc'] MARKDOWN_EXTENSIONS = ['markdown.extensions.fenced_code', 'markdown.extensions.codehilite', 'markdown.extensions.extra', 'markdown.extensions.toc'] # Options to be passed to markdown extensions (See https://python-markdown.github.io/reference/) # Default is {} (no config at all) MARKDOWN_EXTENSION_CONFIGS = { DEFAULT_LANG: { 'markdown.extensions.toc':{ 'toc_depth':2 } } } # Extra options to pass to the pandoc command. # by default, it's empty, is a list of strings, for example # ['-F', 'pandoc-citeproc', '--bibliography=/Users/foo/references.bib'] # Pandoc does not demote headers by default. To enable this, you can use, for example # ['--base-header-level=2'] # PANDOC_OPTIONS = [] # Social buttons. This is sample code for AddThis (which was the default for a # long time). Insert anything you want here, or even make it empty (which is # the default right now) # (translatable) # SOCIAL_BUTTONS_CODE = """ # #
# Share #
  • #
  • #
  • #
  • #
#
# # # """ # Show link to source for the posts? # Formerly known as HIDE_SOURCELINK (inverse) SHOW_SOURCELINK = False # Copy the source files for your pages? # Setting it to False implies SHOW_SOURCELINK = False COPY_SOURCES = False # Modify the number of Post per Index Page # Defaults to 10 # INDEX_DISPLAY_POST_COUNT = 10 # By default, Nikola generates RSS files for the website and for tags, and # links to it. Set this to False to disable everything RSS-related. # GENERATE_RSS = True # By default, Nikola does not generates Atom files for indexes and links to # them. Generate Atom for tags by setting TAG_PAGES_ARE_INDEXES to True. # Atom feeds are built based on INDEX_DISPLAY_POST_COUNT and not FEED_LENGTH # Switch between plain-text summaries and full HTML content using the # FEED_TEASER option. FEED_LINKS_APPEND_QUERY is also respected. Atom feeds # are generated even for old indexes and have pagination link relations # between each other. Old Atom feeds with no changes are marked as archived. # GENERATE_ATOM = False # Only include teasers in Atom and RSS feeds. Disabling include the full # content. Defaults to True. # FEED_TEASERS = True # Strip HTML from Atom and RSS feed summaries and content. Defaults to False. # FEED_PLAIN = False # Number of posts in Atom and RSS feeds. # FEED_LENGTH = 10 # Include preview image as a
at the top of the entry. # Requires FEED_PLAIN = False. If the preview image is found in the content, # it will not be included again. Image will be included as-is, aim to optmize # the image source for Feedly, Apple News, Flipboard, and other popular clients. # FEED_PREVIEWIMAGE = True # RSS_LINK is a HTML fragment to link the RSS or Atom feeds. If set to None, # the base.tmpl will use the feed Nikola generates. However, you may want to # change it for a FeedBurner feed or something else. # RSS_LINK = None # A search form to search this site, for the sidebar. You can use a Google # custom search (https://www.google.com/cse/) # Or a DuckDuckGo search: https://duckduckgo.com/search_box.html # Default is no search form. # (translatable) # SEARCH_FORM = "" # # This search form works for any site and looks good in the "site" theme where # it appears on the navigation bar: # # SEARCH_FORM = """ # # # # """ % SITE_URL # # If you prefer a Google search form, here's an example that should just work: # SEARCH_FORM = """ # # # # """ % SITE_URL # Use content distribution networks for jQuery, twitter-bootstrap css and js, # and html5shiv (for older versions of Internet Explorer) # If this is True, jQuery and html5shiv are served from the Google CDN and # Bootstrap is served from BootstrapCDN (provided by MaxCDN) # Set this to False if you want to host your site without requiring access to # external resources. # USE_CDN = False # Check for USE_CDN compatibility. # If you are using custom themes, have configured the CSS properly and are # receiving warnings about incompatibility but believe they are incorrect, you # can set this to False. # USE_CDN_WARNING = True # Extra things you want in the pages HEAD tag. This will be added right # before # (translatable) # EXTRA_HEAD_DATA = "" # Google Analytics or whatever else you use. Added to the bottom of # in the default template (base.tmpl). # (translatable) # BODY_END = "" # The possibility to extract metadata from the filename by using a # regular expression. # To make it work you need to name parts of your regular expression. # The following names will be used to extract metadata: # - title # - slug # - date # - tags # - link # - description # # An example re is the following: # '.*\/(?P\d{4}-\d{2}-\d{2})-(?P.*)-(?P.*)\.rst' # (Note the '.*\/' in the beginning -- matches source paths relative to conf.py) # FILE_METADATA_REGEXP = None # If you hate "Filenames with Capital Letters and Spaces.md", you should # set this to true. UNSLUGIFY_TITLES = True # Additional metadata that is added to a post when creating a new_post # ADDITIONAL_METADATA = {} # Nikola supports Open Graph Protocol data for enhancing link sharing and # discoverability of your site on Facebook, Google+, and other services. # Open Graph is enabled by default. # USE_OPEN_GRAPH = True # Nikola supports Twitter Card summaries, but they are disabled by default. # They make it possible for you to attach media to Tweets that link # to your content. # # IMPORTANT: # Please note, that you need to opt-in for using Twitter Cards! # To do this please visit https://cards-dev.twitter.com/validator # # Uncomment and modify to following lines to match your accounts. # Images displayed come from the `previewimage` meta tag. # You can specify the card type by using the `card` parameter in TWITTER_CARD. # TWITTER_CARD = { # # 'use_twitter_cards': True, # enable Twitter Cards # # 'card': 'summary', # Card type, you can also use 'summary_large_image', # # see https://dev.twitter.com/cards/types # # 'site': '@website', # twitter nick for the website # # 'creator': '@username', # Username for the content creator / author. # } # If webassets is installed, bundle JS and CSS into single files to make # site loading faster in a HTTP/1.1 environment but is not recommended for # HTTP/2.0 when caching is used. Defaults to True. USE_BUNDLES = False # Plugins you don't want to use. Be careful :-) # DISABLED_PLUGINS = ["render_galleries"] # Special settings to disable only parts of the indexes plugin (to allow RSS # but no blog indexes, or to allow blog indexes and Atom but no site-wide RSS). # Use with care. # DISABLE_INDEXES_PLUGIN_INDEX_AND_ATOM_FEED = False # DISABLE_INDEXES_PLUGIN_RSS_FEED = False # Add the absolute paths to directories containing plugins to use them. # For example, the `plugins` directory of your clone of the Nikola plugins # repository. # EXTRA_PLUGINS_DIRS = [] # Add the absolute paths to directories containing themes to use them. # For example, the `v7` directory of your clone of the Nikola themes # repository. # EXTRA_THEMES_DIRS = [] # List of regular expressions, links matching them will always be considered # valid by "nikola check -l" # LINK_CHECK_WHITELIST = [] # If set to True, enable optional hyphenation in your posts (requires pyphen) # Enabling hyphenation has been shown to break math support in some cases, # use with caution. # HYPHENATE = False # The <hN> tags in HTML generated by certain compilers (reST/Markdown) # will be demoted by that much (1 → h1 will become h2 and so on) # This was a hidden feature of the Markdown and reST compilers in the # past. Useful especially if your post titles are in <h1> tags too, for # example. # (defaults to 1.) # DEMOTE_HEADERS = 1 # Docutils, by default, will perform a transform in your documents # extracting unique titles at the top of your document and turning # them into metadata. This surprises a lot of people, and setting # this option to True will prevent it. # NO_DOCUTILS_TITLE_TRANSFORM = False # If you don’t like slugified file names ([a-z0-9] and a literal dash), # and would prefer to use all the characters your file system allows. # USE WITH CARE! This is also not guaranteed to be perfect, and may # sometimes crash Nikola, your web server, or eat your cat. # USE_SLUGIFY = True # Templates will use those filters, along with the defaults. # Consult your engine's documentation on filters if you need help defining # those. # TEMPLATE_FILTERS = {} # Put in global_context things you want available on all your templates. # It can be anything, data, functions, modules, etc. GLOBAL_CONTEXT = {} # Add functions here and they will be called with template # GLOBAL_CONTEXT as parameter when the template is about to be # rendered GLOBAL_CONTEXT_FILLER = [] ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/�������������������������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0015600�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/android-chrome-192x192.png�����������������������������������������������0000664�0000000�0000000�00000007605�14502137606�0022146�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������e5���gAMA�� a���sRGB���� pHYs����)Jv���PLTELiq9Qx<S@V_qK`PeD[Wkhy޽Զ𕠻v{oǰ̬WԺ(ǣq</��� tRNS�cLl7Ė<�� IDATx\{:e.VQ|`J&!vBN2sd2z?W߾Pq;AquU/?ʅ5'? l\x m+_*�ݓ*ۮreO/G^]v�] e @*޾�| ;YsFzنQo~Fuð{e8`9�m㭹G?p�ơY:y�03f?kSyv:A^9cj_~58 @6>K O07Xߏ{�-s?\vhYAFmxkV(�?*z^kk~A�Sj7]FuEfZӴ&1 WiP{GȦVӨ\f!�.}Q@M}Q"`?HsaԑyOL u~Va`0C2nu?7�L]-0uXm$fKFV1Q#�76yl]� j@.5 GSGoANOoPZ�Z:FjX)R?S:'&%9F6Iq隽�&Qʢ˳j*| `((BXFL:47R � w@ .=dKL92 �\S6iZjWXAOj[雔U,�0CStHIMgթ_NSPS@Υ=sZ>Ħ@E= >9!ߢO8,V�tX 2=�T, �d*^|=|H i �ڐi6v"kIND0=1G0>c+LAށ F7)Lzc':LPRįlR�pf "؆$�u0CD37WhR#y衕 Mx#VJPGVB+^ �A`(+<G?(o[0WAg6U2A\D#3:]`p1;LħTEDx � ^P KHH/LK0/C4@c&K<QOY/ 5]`4Ԃ+&]PË~inurHUv 6r3#D׎*M\:;BN�~Y�tQq .J = <DzFpO!PdF'H>&�`Adv`3 P&TlV!&^Zjހ�_g`r;�2A0@8x1c �0oZ(%]'Ncg`j@D�؇kmo᪩]�qx51!#`{&ivc2 M0xrDY:�bjmP|-țTr�g�`sM `ܯc߲īnF btHqCa`ƯZ|{N03N/ @C(`�ք vǺqz� �l Lj펽ʬTw��1pr�p"s'K˧|V4. ? .m�l9c7hUT~ls,"~5llbC0Pn�#[~d�0�n,䛀Af`a�Jxa~4bV g)%-.\ba�;fBM6H́wNWRq9q5HSA$zPCA`rk IRNMvfͲQ4PyEeV lʐs!6=RHhj60o 6V-f@ E )슳UQ-=2/>0`Driu}aiBZN<Bvs\$b*_T&NW(ޢ$kG&{~Dkb0}G&FY%È;Af%xUìD@o̽icQ JvU=O'X ee��z!\<E.6ي}aZl$)1�9J Y&Y[ 2-\Rm]X m?�ݢ_Ow�mցu@ΝV{IQz:-!P"!B;d,[q|�sm=ڦ6 HB2-sdXԧ]GS7Q59BuF3*Wx$B@^J] mh;B+5TWGv7-5@n'֤:#xxVzze�HQ#ِYT hBL"RRu@50nB/[:.5hM8BsC6$n-8IBĿrh$�Sdy!AJ3{ںst'ߣ`GCp,mJy)ba$�" S H X5R�tp͛XJZ3dA\'A< Q&E6_岺#e B˜ K5籂1w8dAk|P,A d�I5oB6vG1_JFVGz`Q] <O'Skn6rtZgˇ�6ޱE}(Gwb\Z(� a!&Ѵ,{@چq`VNM~a >>GQN9H8OMm.�e �AB` N�ą^;߉n> whd':gf nec742�X\8贈�e ղA&-Tؽh.3hId5�eR-O}rOMfFmǨZKP@`,EN ]%9�cɇBWhv;#Z|Jb 9��:e-tʃ4V:7 \m/9:@Cz9(FRzqY `biC&@Y<bTePrR n&Kx��X,�PR ,qSN1-R.xJ .rt  �u@// F06D^�zo5M(vz�R\y<tW䱩聖)KXL&?Dykd[r*+{宨eXS^uips\.$-X}yv{�.ps^8 p�g@,6)r Z$938 �@�} q|{G_u%e2 O�y@ (:2_%2|E;[*/gc`$oV k~z ;+yGYV���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`���������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/android-chrome-512x512.png�����������������������������������������������0000664�0000000�0000000�00000024513�14502137606�0022133�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������æ$���gAMA�� a���sRGB���� pHYs���� ���PLTELiq9Qx=TSgBYJ`쉕cu✦[nypмԯiz;cŠԹ,y���tRNS�Ƭ8ik�� �IDATx]iH t036lsüydsCKwF]JE=FO?|JnO>>=>F{xOoӲ/J >o>?wZ~SCі /!0>Aa9hm߽ d_{?Kl0Ȫ{B`d7FVP l[ 8YY8] ZN/`?JHh %�K/:zA �~Q@FhQ�FX�` `-@؍<,X08`?µcöolsCkA-{&0F�8� 4��9!d}0'lL >E6  l3�� �f�3�`f�03�� �f�3�`f�03�� �f�3�`f�03�� �f5`5ݭ`'k[?x�qTo^\"�_+;1�iUU?8mn �slˉmſkh KNIVƻWN'!݉7NǷjb+DWHo�Y5u*Nl�@Vաtbu �6ںJm;=n�A�0>PlmE149i9l�2V. Mc0CU =7~Pǜ0ua=\�1o& !*�Dh%Q&�&Xd& ]̃c0pf<@̈́J.`5<�b3amf.< �h€,qYr5@Gb=]̈́%l} -�זU>�-bjC7ՠ@�Q3a@HD=�I_`€O4ay�cA \�8J v{�B<kB]ƃ&W�WN"Ta@B^�lK0 I50  0 .W\ԦȠ#fϯy K?K k f rNk戛_WR^Jv ݹM=O�āP^AʕIy �dG'BBz1/.~#pz1�:i�K$0�8 ` ~L"E�ܿHp;Qa#>uoեU[d0#Db NBn{Y�0x϶7o-}&>&`a@n{�)`t&*m\ B�`JR') Mn JwfIrqե�0ITdx@B2{aߓɅ�0uXx 禼e~�0_ű=-"ø$�U zZH�/3S_�L�h_;aucc^.m|s�P?gps�~VP0p0! �*)<=t"Ȅ6 {5D^�Sj,@8m~Pj4ikk* 3GѲW�`a9!Ȟ d�Z5 GE �ޡ�t6@ī�0͸t]+2<LǽUuUf*&So3.�r<_8Md.γ�pP|1WH͐Tu =`pN�}(w؋E?�hp#ݭ(dWc޴/Z:E2?  }�$hjɃBd�^gZ (֡#*�Ȩ]<KNKb7`hsjǙH:3dc8wLC}3OXd�T _:ԫ]6 38QJ@@üJ?,@sLX(�Xs�K !^%t+?)A@�R8*W3sװӧgl)Bу  Id`W 3"Hn䃇C�Q3x~tbXOϞskd&r�dY9UdxЭknС:0};N珡wP/d'U#�(!�Aa?<_YrE�= =COAonQ"K,x@Ml$U"?3@")f�'T" Ȟ~er])km GMXd$w r:R$;*c髚Jӿ\sG)3@ a aOrfQ*s@�+ֵJ$0 Vڕp\r@�Ti*p+==+Z Һp_t`z�- t72 �4%shM �4(V'w/, d2C:u<�v`8hc.yüwND,wz�PJL !ӳ_GuIPV5a�ksń 4,�Hr繼,-,HSc@f'Sؿ>}F 'KNXas�S4�:,]ރxv53} @�-th)b%z_+ןr@@:JN0 ϋd$O2GIi�X@ҚL�k>=+zѱ@fザ*�T"+>zOJ~]JǭqVLb'ɛ+==+z[ ʜ^SԥFe&Ԡ#$kI}g~e J]aSTkhFB$/N 2&�P`?! @LL&$=�w8mG)_"NP*�0`F܍]"Eח cRh{(xp�XiQAJMC#I"-iS*E؟0�PL/.B;iFs]<c,^,'n${N$V?` >"[_v4x%,j+J.0,ݳd; C |5[,Kwa@Hы<An=_@9laeѯ0+4?&)1 [-�s1ܓ^7<*N:xl^B`e j;P!SrI`j@]tւX5p�%od8F$'Tb-d�J(< `R=quŨCv^Usg̱旨|CD~fq8z�K9[5t_707^E%L:k=Y3�=;qUI ˨|>,SS;TO!0<B-LսsgDJy&P+[m&U 0Z@%�@pp2>[N! Ziˏp6qop4οQazG  GsV#l8F ̡(t J3 J(@=UΡw yHD<ڢ9B|WZB*'%J[@#u@sbaŻPן? ]eʊvviL<ml7!!w_&헶HFάtBx:p�^/-팮C Ȋ3P&X5)`@c)�k; Z#7DIh"yY*}?.ף+"Ofv(p%$oHx{ZGI4OP2-qqf3t+$f 9".<Uh' `] L8DžzRpU7`m'�dKrh>@. -h%ii@J|J32�pv@QCj@NrENvk@u,]aYF�MA.9/ ^Kŀ:0b'`g�cW v�r`_L)X3qIIUXd  hog9k�8[(Po=" 1OF� d1DؘGggwמn!BH`@Gjז(vyvtTU355j�T81xm,4E@eeT$�gG^79'}u.u5KhQN#9�(BVvWGr �"ƿ)ȹ{4{"�s TƥQE�* q��@7kmrx86��� {]m <+� �%-.1.jBԥ�Y֊^�#aVJU)X�3rKHf<#\4X��L�HƟ'Il &>Y]<|f.)t0L}�Ȍ@ܠ"*w;�Ho6-8yi )�`7v�nqЁ�c�Kk{�\�6�HOc�pcF�8(%�V]lH YHKR@eKH"#�F}ܑ N7Ŕ�A+Ęёp ,"1. +|2Sܑku u9M1`=cw7n#$o� g2~1eįO$�pm1k91X`1�+qk[j$͘16pNhm눢5�kƈL[\Tù3+:\z3f~ vftgEYoPFeTھngb 1vt"y0a_9OdϤpAvo\ONh5Z]QTUf1U==X'#sS�<haY{Zuނ�7s3)E4)o![Dzٚ3XM/|l$ }3̬֬]x^Dig|9$}Ҽ[!&O XK öF� P8.JkBZ+�v5!g֨VZギqت3ߒOƬ9cQ_ܜbB[n+0n ۂ ɀ�5<:v`-\ɆІοZ�9;3Ųh 7ٓd'l Jrb>-@щ!-~d̚(A&xg˕>1cVKsz86NtI\;"Q[s gFICґ_ɹ;R$?sd@1/N�vM 99Խgǧ\[PϝqYL4d8-w)J$jπM;:pX`8�n q5ɶ3`FI(�:CX>yݷYeu5 5�1+IGPs�\v � B4Yd`9.'=W�`sKt@LJ �@lGwV*V<ۏr*D]$\5$��%B $6�a"C3BͶ$ U)Q΋LM(#b-W(��!XMWΉR^蕬RFSF�6%:V,RfS�(lRψB!v>U\hxP}�D%mRpF�hH:WN)+4Np151%9KG z s=+̇1TV"GqB,1RݕossN@rؤd9t 7CPi*ʶhN}ֲ (;�Y&Rʇ� %K :]DUƯ9 t>񂒵URxK`@Ev&R9~難@p-eQp$U �L:JfC|er%n ZHT! #G|ޣTOuwҺ @VYvȑ@�0OJA#7:%!� ~4a J9Ȧ�Aao&|ݬ3pՔ!(N&I"�8!gg s4ZF)vMg-*,pt=%�V(&+<A3n lBK@ДȜ g4 �i`֡1@؁!]ZN҂R@|�tv`{4pZp $?#zL(� 5ۏ#?OCl:-YJFa�`bal9`cݏL0!�5Ebt,SmvN7 JC~?@m JӠC(nx倖sgz4*mafr2�MtC[2 |WN7) :B{8O~�w=̚�G}�?̿GB <9��+sh3+O:ktQTVZ9 �0'z@IC98ݠ<ߝz;TT<3١Y’\}}zYJ �bY.bucH.Λ}58�[a.C�;,Y9+/'~u}3^ �P/:|_`�5:/s ʎr ,:�00))ϿRt/�2]ėA !i ӊ2 �`~QSM+񓀷 0@8AçVOeU"0��4^ 895ف lЧ>bifP�Pz X<ʎf`q C@LJXd��ctw쪠a+J"tE:C2ůԃ6:;փ!pځ3psHd`*p1յ"�nt ͐W FlB1mf@K2>:'N–dWST&�@ygLt_SX6hK!B9�ȉA[4cmS(;X4%]ʿ݅@D[B;;g-4sJxtQ@sT߼uu}$L&%,55;Iݾ~wqSzeSvѐ;�TUnOdo8 sH;p]g{C'UPt5<im6�pSTu|o5\iϚae �%nF}t/?};0#ƀz3@�e%pU7=4{b *QVtejvݠ_l~e3mUk��Ÿ �Y8(7X@]_oK�F<T�]c؎nXx+SZ~3п1pVcR�a'_"�ƀi*_�Xl7!8)vo _cv`"|+oe 9ԌeI{Z_ Ev=_:P Έ=OȮ{舽)?H�<x R/j dڳgL�ұÿ7>v/N -eL鳍s�wgf1p*^�] 6j��IDATQ%Vƀl3l�]7� &#MJ?�(oP#Dc?<�_<NO\|1uT~/�&US<n�t5WwsӪ7�-S -onТո͒,(n �;t4c ";,nY=dw؝,S@sc"k=@N> \/>ͪL/Wѥ�u1ХQ!PD8>Hx(NK_֥{ UN3@YLl_Զ\3po?8cu*n=@0aC`T/hS7Jcԕ/nic�~mN&PV&mD]N$t�R[mQ{qq&e�q܏  y_G �"J�(P�@o`0A,� ;0d_<;f>ΝE3�8M|�TE|E�ekbNd8-xAz{xg�PP;|�;"sM!�7~{TZ:\}W%_C" ,͑O ~wyA;OqY:?]$N{Hg-ɰJFg&"�[4CFޗ q =?m~V �uo"&8j J:wՎ~Hf }2�0*h"%�qu 2(X$�eo��\iZ�@mKÀyx/Hs.q]z��nq>.h}hV�PH{�hhԺ@}Չydq[Fݬ`w�i~ہ?$C@xl+�uZ�̈Ah@~ehSwD\cr+@>@�P1׃6ћTn�}C^[*wU/Z $q�dҊ4 ;1�@e*W �Xzmϱ]�)3V<v/EIu �jej {N�Pt,VhaM߭�ݨۂ*�u@w00Gw]i~oZW�p)j�ۧg{B+g�7<d9?=^]w�춉?n ڬN8XkSju%�zPiUx־NK�VߙkpVL[j4NZ>W�|t0=�0︿-�=LQW`؏Z˕;�A;�]=wB^i ����xӪ f �u%@�Mj�6U11U �^4����py ����[-R���HZԽ ���*u30u�֩M LMf`�䩛 S7`6�������� ����@Z.����j�Ik�Ik�)+l�@r����@������� ����@������� ����@������� ����@��� ee !eS B:M Bj!e5�@�1)�RcU^Ð.@v (D Ðv�!1� L8 |Du1�t%&`�$?D �K��ԃyqEmI)�M �ŀdK�?㒊6\0 +�`‐ �` E�c "8&(ZWD(x_Chg(u^j <!L@Bx\ɟK@xY5Ϛpx_.Tk\ -jwѨ F QA5vfQ_&7a���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/apple-touch-icon.png�����������������������������������������������������0000664�0000000�0000000�00000006000�14502137606�0021451�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��������� ����gAMA�� a���sRGB����PLTEAX<S9QxK`Qfl}ڼԝu~fx^qE\ʆŔXlؼ@˨)Tzg΢-�� IDATx\z%N v ߞil^dȈ#F1bĈ#F1bĈ#F7ɦ}tNͯ{h_Q͕ٝaqF~yEEu\эZdsoL?x"U޲@nN5Xos{j5'?sЩCWD?s$iةV+P~q0VՐB\;N9!0)lqWYë7ܤ]uGߌ_|߃޻S!^�ޅ)lb$t.c9E3wuÞE9凼媸9bsˆ<шwp;hls|f !cf?X;A,+d[x7z#GzH2lzol=NBaC 8�KoV璍X ܣ  yUpd[/o  # 8r_ Ό% Qj@a^N 0`[SV$uHǒiV0EZR)?K8 eH$Q|\RHIwKOLzyAl~VJYa0paSRp藟Ufö bk-\=7R?XSJR0CHMhbŊ,]0,_mĖf|DH8;+qc̊@vO21O]Y~H8ISmZgidNSd7-ن#eiQ/lm5וn7O2z7r̂ˆ9Z١UaҞ ؘ;�qV5c+#m!UR 9T<ߪJ9V�-|e5/f E U" v0[XQ™qF ep# H~ SO hs :eZt]IKߎ O(+l!yd`A>>O[oH g5rr ,cj-ZC-*jc&ox T 6918<py9>tkJJFtCS(@S蹦QYa qO.ٶ sbjgB3݁ԎWMD% I6`ߣP_+VT<h*ɄEH<UŝTy( "13'(QYN❥D`1kg4rʃ IRT+'1M9GШTMK'ٜP!9HètQlwdjpI!UQ :R[pv;X\i+l%>ɡ8t)Z.u koY*6cH#sN\G SDMtl0-W>h rȿl}Ck3̮�5ݨi1W+)u~9prI@ѸoL1F?J$Ȉb6RrHe*WN~-\|$zxLbs4 `3pK%T$lo4k. T;;55.m^ 7IǴ~'Jw(7dhk8`%>ZcIsE%p3B hn(0}{U2tH*> ֑Ŷ݌']Dk5XށZ6,P'HnaV.NGròlh[j|ubF SZ|PgIB_י1l%7ֻ5 @HG2ԩ-s NJg3\_yVS@od4R{ffwB)ˣ@ 97uYS=Zjȝ�Z۳ƽP<yf,[z `cST€<ҋxا|ҽ%NBQ]8V7;e:翫Im*N�m%u78 >bq;d}@YL4q4 ٢+p`>:|o<Pu\潍b'xb7/ і vϴwRsHsVw :Xz9U܋XMW!1P؊H5aշ9—hFWǒϚmxA^ڒRFhun2iU ԉE1p˯,GJaZqK"|9{ި4G@sCٲZf#YO7AR뇤S(*'D8Gb_҆c(eḒ{.HM_/G (*K7d?2ŗȴnxx_Jyo$ă/9G%B|ƻR!Oy׷={$y7M基Ib7=oBz y-yj;:72>uH2/p_#K#J˳._ :?p ;C HDU }z&y{B/yy×yo-rF[qܿ<zJEҏQ_xF1bĈ#F1bĈ#F1 t���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`mosquitto-2.0.18/www/files/blog/��������������������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0016523�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/������������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0020172�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2010/�������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0020554�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2010/06/����������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0021001�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2010/06/powermeter-example-150x150.png����������������������0000664�0000000�0000000�00000034604�14502137606�0026261�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������<q�� �IDATx}iՕ"6U QZ, $Anf1/999^~=>0^ 4m0m fY;-h$զj-2#/}/R`GT[}w{BJ)ahRJ!y�R赉ȍS)y5ѸV9l|>::]nK[LUJ<꣄E)S#zG бrDSTyyn8L6E)'9G[5e`)Y#۷mü1}t<�71]M E.Pgr!Ƀ[!‘%5цŔT\ֈ|_o`زe l2M&E�nz{O-*eϡaoUg<=PF+W7ky>vm$^bBY+/] �8r0&''#Ν;džbʓtt$ SҎ:fOMxQnOa/AQFF�CC馛G1<<馛�_~cڴi/70.t(+.sJ7X\H`9:N.b rxn/'qIAw�`pp<st+օ"su㤢VS4>[0 Jw&,.Jkj}4 {7ߌ|>O|с}ݕ"|GļyP)ObŊX~= fDP29& Hǩ1,h]s<LtlT֥4Y6M*j905hRF`3 jlbڻ, uT`X*=pMtmȶzTR/ݜ:.*lu*_UL,)5SVJǢBZȥ,3,tmJצLҬY KgScmS_KGJ=jrלVSF)ZXt#6Cۅ`F,}TD @-^Hӽ]F+5YlO/?A9S D-VQ6k Pt:~n#&(oS ;~di\ɑ%E]uQ5*URd1*5<MRI. W1gku& x1>٢NRZ}(ipS&v 7琜2QJl)T55t4qԖ%4fècPYS`3x7CJ)4n[zYfՌr5^i/ ,n-)5RqkF_jLB]kkJgKXk6gא 90X5&y/j}z6&lҗ%Pl)rl_sGĤfB (g@0hW?ql:J..5Ө(LZ2P4myM h#˽0WV7r<�>ETpRq}LilJ[ZjEZ7[nl̒u9uDQE8űyYo ٶBS25ߖlt՞ͤ"j^* ?urfǿt"Fgq4L8zf-Zy ez6Tmcrb)C^;z1:a ,GFk83m�6Pf)=Q}.Le-/TުQN7!\#K3 L)ݚk3ͦDI^SՌ\ &kY?Wy)Ay>3E} bm_ƚEoU_6#]S*V& KY@~QMuZnM( ,c)<~b1L^` aj%Б iiVQ0S[E[֘L4j⯮QIY/)# h L O'ч /jD`SԿ=jSt&#޲f#l)\Qe`Y"t=eMѶz3e�RzMy^( TS/}HThVs;͸-۰suHRոմB0li;j:hRڸэhS-P T9�WKm4M85?5c]ʸ%mrP(!ZT*5LUtX,߰b\~_-.)%Jp;;;r&US!w"߄v]<eh u|:FS!/D.e[K5ƥW3xp}l-H)8MxnӸ6ՠƩ7NH].̛8T;dKGV*J�8Sc:#(mc�0XpX:6CSuꐫt̜,YTg$5׋H Y'Lc&˷y@/'BmSG) ItNfetH&(c3EqL. яß5<`Vuè<S�|GQriʤfsFN1ExO ?#WJ9Sj/Vb&suM`jtNOϧJ.:^jz-@:-3d<G3l*klJ79 Ǜ36ҖZUyQ z3g BңyK6e6+TVr)'1QXmJ7LjCm>: @ \KA(<VqBGٰ/gY 'RQxTV}΀u}k)Eי 5kjs)SBU*ekRLokr}=JO,ʩbTK=@o1R9AMjqp8l5Y"qf-Iw�)%Ƽ1d H?7oMUdVhJsMƩk[ʤR)8,Tj!Fv}=l:o^_!82uJ6EH[Vj#2j3-WKKKk~~P+S=K)c\W JWダEm.W N=MT}͜T\}�r�|>o8q=5A:JN[zXyd&\6!jZ|@ʹ OժN@uNHRTBV?SP;S EOg; 6Z׉J/uUFEW#AVZ[r2tqt̳tRfn;P9^L4)jRJ8*9fqBEA&HR@㢈NC]H^LE+S3&,їs8=Ĵ$m<TC !SڸPStEZ U5Q)uBCW[)/Q48FBBݘT<<`p^o {TSP[ -ʼnhL@t\5tQNAK9 ]:mcQ!rx>P-s וeJAS|7| ȐAҶTspxHmz[#/]JoTmu(TD’Ǧ~h*Zɳl?`er`~*?w Q9 _}(q(Í)ǩCe\Hyi-gHRAׁ):S&j,j/LÄ)j3B9%銲<^T8YLWnidi&u`jLF7[4NG?uPפ?]BP,QDk5YC/8Ln6/tYhd-򩩐]RA�P j ?�RzϚrlWZ/÷мrU\Gބr7I@@ !qAw �_.WaZt{*2_o*)"̚f� Aw`Y28!EL pf3蚚2#qu·<qv}3 41 pcn*B@�q;?JlW3Mp~P |A K~< R'`I)Ǣ6D;Q63LѥNP<iZǍ%DWǝPH..ըSzy?^8Ӥqsq'-e<JqlL})ײNaha.Ȭ19Z39>?ؗ)R_f~:6өUoԆ¼`1_CY?]Y6ѥdd9,f&ӏ":΂>;$v</?}SIJB'f!?ԫHxycaC-HkJI<̮WRM!aPu-"qI7>3R4H(bIr(১~IkUvi\a$UC诳a@C d�H�"fUp]kSZ1U d-~YBbVH!񑎏dJTteR6\UU\e}7HjA*jP%RpQlokBFK?ԨWe/VDžB`JMcxuMYWpd: ջ�bGdyn'|UYeS,–6o�4~@&k ߌ*TRBs+9txqktG5,khMET(29$kE<pً>Ώ*,ָ\tI&KjPjA͚Tn£ӣgåӱd[lX")*AeLL=K@Qk2bHgW |JE?d$:Ny׸SP\Ji(jUU {�J?U~{I.=Ra 5֥$u�$O8ԅr):jTֶVt;p|8>t.cn܆MAGG)3% }.5emAF6a"H~#a `~/mmpkԛ @ W|To\ *Sql,�@8:y d|c:$ϋ1LT'0XDӊ 9kiZc3<VBX+b)[oaiD&勄Ǽ1TF Dµ3EOv$'U؛6 LT>NOTQKxl,F*#10Nۓoc1.GJףG)zNMaF>I/'} i)>:Q,wuMAصH&$~kE8B_a@'{Ƽ1-b Ⱳ_ƙ +uyq]gxϓAx&k('F5*C(yuv)P^H/' %Cq_. 7Nb4SS"zu)D2J4aMcRyAhXBL`yGSXpQrKհrsr"3>}-7CRH)U Ɛw/:1UB,W3J4o·jSu $:Ҝ#I {{W oO S%\I#LG,!jUpt(\5AD/\ZsV'&7}c�/ QnI ·"+"pL3Kqo'cY2k2(Z0ݝ2FQpǦfg=)exMB@DOKO/0΁#Xhsb]M&0Q4wzZzxFѴN�-P(OD rHkv~v|GrP /|P-.^?zyF-\ގ^�+\|ih͵TJ W6DW ѨmK&O`Bf:**IT  vl+i+U=Wa̅gNœL|6pP'0TBNE_ʮB`l G8X׳_xv XnF ,OZOqiRb6F^¡CX7cZp!xi%hym4D8~eЋr/9Pl,>=ӸJEhCF1[gcud�/M? qz>~@I)�iW-NZq+:.<fv.w*y� SƢD!KbhFG㏜{_B _F/b]h͵w&k?Ij<u)ܳLs2||6qSxBSSx~ykE8 ~=k  0捡TC[GbĞHx`�|eҩ!UH-!jSqn6 m@9K=AOK}+&kxvYLsaujj??Xꇡ; ޞx;6u*i?˺Z ok$Pqto=64zO0UErCbS|$2pdZD zZzp|8aʛB+x8~< A/gsP 6JLH@� ĸ7<{a 0N'v,EZ'UϡwǽqLT'H%u,b0ㅁ^B5Bp2)"6:~O-Si< IwN;|Mk\i'=Ev-׆�Aw^jWVZp"^PԷ  'rvѝF9(llanf' Y2\,w"5UDŽ?�0 cہL�D3[f"<WcJN5ʦXfn4S}֠=׎Y-ƫ㘔_M)dN $71@LSSURżr⣞J QC4G%$~wwff)a98'-=-nF :nIMZXsm{ݢc?皔KZ`r٤}}`}ߜa3ޫ_�Tx]G/2|Lul}Um&*,yYMF AH=Ͷ P(_dϊo6+;wSSSrD'xۚdU\~#H~l<"^�5ۯ aعs'21::jBar* xg0<<ܹ/j122pSՆ/sYl۶  3gb}q(]Nɜu�P,_ CCCk_|ryu@6e1W;Ca˖-VFClʉ):wvvbʕ�Yf/GKK 2Μ976DI/ZF&UJ& ї( Jn:'Js 뎎Z �0c \yhiiSBs92jK{}lڵ( R===T* 4(t5>xq-`ll SSSQױ˖.u}YR Ø $Eǯ&&&w^zB4zWrFyexRJ\r%8qϟqpazTR"JG5s碫 JA^CWW\ٳQTRu>ȮE6]hҥKׇya֬Yp]s̉SKAJ9s栻;۷oi˰`(ѕA(U} 1}t,u&,k}޲҈暰sLr^HɢW]굩xx84,6OgSm\da6)p43\k]R<1-)%aJ@@7X)QWHс��?IDATD@n ^ ' b #QgD^{˥[˔7bSj$'#lk-U_GUiY zs ײo_$G'V4/_Z+(koI !B()ey8G2BRpCɐ+MiDʒ΅AHP D}بB@Ďt7DObSŒyb:.F!#>Bh"zKC+! 5@)0$ʌg)oTwB$^, lOP=R6#EtQ6rUaZ+-Z(>6\D*J= %;xb^3:vxu\B2G'\WǕ0S6_*fWl< (!ԏICdP"H|3y;1vi u Ѻ¢r7_1>BNtYߐ(eHP9n0.׍PD)CuZIq ;VtquM%)I '$3 =+F*D}522(%zF"=&v*pB,J<#x~%DQ8dR^$>xIQBPKCܭ8G%|,凪i#Oš%/DD1MI%p E><#GB!T(cXH+.ORuSJ ӈ.N2Z%S#c heREeL6[J(}8c"'ݨ; ),ɒThAMŹ(F}H(0cT�J i.֕'A][J�I\+h!p!DhPNXRټ(*N$e$r(rtPPyEsTeMH18%R$S8 Ÿ:2ž(;3a!:%4@&S!y)YPF#(:JQے"[WY)u`qENN+*Yd(FGTS8&u6bJX ҴډHB mvW$~<Oz)FEJDiUIMRc;EWJȠbKG.J4"g}Ԛ/a'QuDƤ?bWjh*2u" OL&ʖQNi:~Ȅv"h>;%X_- D<1jQ9i]I%:UN>ԩFe}^M7jD"ƔHW dt[*G8FRtRJTtV!Vc&To&㔧'VYCjuqzTR|u3IP"b꫄HFrVl Eau?)Z*mTN'lwR"E@u$WWtQC$ 7I|])CH8JLGU`r;#I9F"%ZN?=(/E]E4D=^sO"Rr68z2V)Ӈ^BԵ*^b|Eއ[ r>uֹ@╶k>՟J?KI8mz5j}(WzlFg )>AQ8f㎰S6BpPc:N.48:,#�G,?]^iZN&Y<ljD}qaTuI"&:_|JJ:CJIqf N 6Ch&eu:eu&)g׃@t?6C "Q:R^J86\ġRJ֭[uζnڀo w۶mRb||;iС3]OEe+Yӈ͓o$uTyg3(q= ?q5Wvkk3}6_;{n|Mho[{n\r%xK+Vo*w cݺu8761 T*;D{{;~sxp 7`׮]Xn֭A.MiJ:D hmJwc*=Fvu[!^ƿ[x�?�x'p%`jr $o?j~q#^ o^[oՍbttΝ;!'o Wݳ>`f8Zwzn-exZqum&=r^HS70ɩIQx/7r >ymp'PVomEZűpqo/U X}}bùspa�VaѢkKJŔ^R;=1m!Df)TyQ@8gHxt%UAJUMf̛7/<NA\Xpغu z,_;vlǵ^|>V|b駟Bgg'/ZٳgAӦus׬Y)%Z[[o?]u*Q:S@A߲>D&8\ͣ* /YdѼj'~3|+_1:Ym8lt9&Yi\H"DRu!8D*[˲ќq2Ȥe3~q|1S0x)B(?jkfãOMaP[jo~p�‚?02=�x?GT³= *~RJ˕?PJGu3}.1Wԯq49s@a8c N83hkk/ ^W_}5z{{u̘{?lقB'OgyᇂO>{ ǯ�#+_*@TnF[e\}ƦMcӠPAZcOJhP^צ@=qѲh:8,X;wwmx%\ pN>~\qصs'>#qFc=q S1ӧOCJSOP(`YhooO?lƍ+_*/pZ3[<}%�P" )P=SԳ:p((,*j5.H_~1{xY!L۱jeXb^}U\yUup3g=;66g&0o<H`ǎX|9>ǟG?z%fΜm܈ӧOFQf)~YX31®>/Zj>yx |,[ -XbE<:u ^z)8t/_\.#G`Ŋ8z(/_!| L ´i8x-]<~�]8|PXkגףp9^DԷjXYr)_Lu^Yu12JNS׌?@n!soM R\ӸNC<q,)6Ye)u̽)1Q�$�f,5.C4*9&\.rE5 q6WCPdj~[ObR8}188{OI<Sx?$ <|̙38p�T7Uj-@p St0V([qlWo|CL@ͣ%& T'+,xpi\s͵x'i&\b ؾm`Æ >ߏ w^,^8mV}1c&.Z+‰҂^6޽O>n+8rV\M~YfCLNNbzO~a>tb;oE]v=t1Vڂhv7&e̘ [n_T}}B R qSnCu:Y"Am3XST 6jg>aTUU~~M6[n/ 8^{-~}կۋZZZґ@W WƜ9s1m49{K/cǏwrJxc__ހ7x?yq8q?=N ضm+.m?fg.kVUocMZgYc4DjuNZ}{q)߿BSSp]r ps.:;:/jp .<ۋŋcѢ?>);v sET&&&gnٽ;{p1xwp護лp!f kqܹ-.jG̛щT<-MY=(cRƩ0w"YtԦrs8z|[̞5_nzljP+v| 㮻Xd .\|泟y_;Ο?c׮](n'{.89Þ=qM7رyN|ǂ/5kݍ .ƞ={/}k^_X`zl;2\2,\B s0^,AhC٫y3>NO9p#GY_Dbbemh6cS+7t3xoi1axrx lٌO}j}RbՐoaz{+rDa&n zm.5ǦtN8lƈ"Y:M#y4~ij _T$j".ͦP=%G\+1Gu`tt e:6nx_Gۇo?[&��߾^�~(x{.* ~Rst}P8i 6#Ѹ1jNL?/}{ȮWv :رc. >|^7|##֮]\.Ə߈cǏ;֭[i&虁oz+N>~+.7o�OcXj* |?dW>y?\(C>'P* S]s }}}ַ7�C8|Wo>Q;bXmQRe !SҬI&z=Y '14ާ<۶mER ?߯aѢ8zN<ёQڵ kE@aT/~Xd qтRb߾}O.ÑG7݄uC7ޘގKgFOWo}r ^ [nիqպuxWК ߚWU[czSS}Xh1qqo/yT*N5kTGPPe`S:N"&\hewyuڻvw܁c 000v[ށ%Y҂'N ?›osBSصkXh!6oڄÇa֬?4113gbLj;rnق9L|RJ,YWEOO݇+/˱g\s5Xr8A{{;\׍e#K%j5T oߎ+,ysqE;OoJ6JiYҏiiT6F'ϿlT ttt cbbJjjn?y8s _ٳgqY,Y}Nu[LNNӧOAܹ={6Ӄ'N`FO̝ =tvvbll � bypCCC;w.1o<H) j~ mp]CCCXp!�`` \\.gϢ VsE_x-ܦhPc] }ɘL9>c7003f,l6{ +jYrS8kS$iؔU^SpR-j0StSh&[;Tў~QPQDoTnb*qW&U.J: Oֱ,2szsX_6tb^fgMYi:?c@3-kV[X*bFE<k(y(,ѵɮ1E%nT(ٸHcfLI>K4N8uӡSMQ_nKi^ʐչ&R% EKlTܮ{G?l S:g*&M¢ˊOovmtl)i+ҳ&d[_ l+=ipnI(N3ؚ1,̼fi vꬸK=ԡ3*ĨWWSmBqBtS꺢!>lQtqfq ]խ|����IENDB`����������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2010/06/powermeter-example-300x138.png����������������������0000664�0000000�0000000�00000063370�14502137606�0026266�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��,������Ow2�� �IDATx{u':>nKz'BF1`ll08g$NL37!_'3ē7& c $Ы[-jYT]묽Z@Dc~^kUs Qk0 bJsRcB�F0cm~ۀjA}#\O2C  !mn|'c0r}uFyplP<:Ae|:U|lՍ/ۤ㝱 W~k 7�_y䲎?S?f<977n0}/rbTʑ~###R?رc�C/չs$�ҩ_E&}MR\@TU $>*r:5y~Evlʕj{1UȕN' LO>gOlp�1|_R={8~8J�`xh'�~s91"##G055v!x08xJǏGZ�azzgΜѣ( kG JAQ:u {m"G?* ;|>~000'N ͺCDž P.8tPBa޽XXX3g0==Qٳ8q;//o J"ᇰi&|ӿ9եE@XN'׸ʸ~U|ǝӍík:ݲޫ+GKd thѤ))[B"��[[lŮw7 l޴k׭ȹ={_߾Mܰ<HSشi3FΎ??W_# oi&?78v|3nv˿ na[}cΝJ/|Ɵw=~ذq~�я} ��9r]]]( _#?!n}9{x 0;;[|v�o~�ذa#ҙ4O7߰ Dd]94c*V&1>cL+9Njȕ T1Y)u9<W-9:u HR #(Z-X|9:ubD؏_O�Ӎ[n--Q?ab~�m===n6fxKȤӈ ŋ]wyHyQ,j ,+pÎ0~= n~*FGG/__}ض:._9{{{ӟ4^~qίˆECs:pMh8|l>*"՝Sa~pE.q9GrsB1邕7̝=$Iu/��z/Ǯ]P,Յ]vaϞ=9`||?7P.c֯pğO͛Gժdfffp=R H"LGc?} --Qtw'2}yg_={0?ݻw[o�d2oV^_nXчٙY9*z{jMibOHG܉-d)*'cUxEpp0sʐ I\ޮ\Ѧh 9TOwvmYaB>CTB{{;04,²eːdDP*l2,,, L¶mi�j78?_cAOOmW*dYx<RJضL&6b1r9X!:::`",$(4LӄeUT*!,$ t:T*T*!LzHr(J{qfT PiNdY"b,:;;Q,ގh4t`U-UVeOwY9U]ߠ.;9w%p4qq}*[uU^1ڀ%PkAǏ~Cvk.n Tո UxUx8Tl1UbɿNH8Tqo'tx5K`AR/l&~Ĩ6 :(WmOk1Uz/g\6]I\-6ͥ �&HSEŔ*~%!|d5hl7_td:yNU_ήT}Uw^[O/* J[J~Πq::bRDϥTo$\d^ՠ^%@ϫ7s.Vo*^=333 Ɋ96Ջ<Nn{`G<xضWz |2Jcnˮ~W]J4 N!c2*;ɚLq:geɠ8UD&<jJg?qd*-϶*_ph�C0 =zعs' @6�xSSUضC9T $.\(c`{mǩSv!06VƋ/vm?yD~y\w] UX@hc߾,E[?[4b]p]z]+vrT ȯ*xACUU~Y_!hŠ?j*y*1|RWzaɀtOK/0Ď;~a<x ݘ`~{3, sm p`!5kػ7njc𻿻ERU>Bvض]~U,MQG2J 2  T [W|^:'2TΆڸ[ UDYUI -$~ӝtI˸3Ϡ[nE4mؾ};phWGzu wDOO;wDOOvGΝqR::ر#o~s--&amm&B!`j d(tuU<Q:gW#UQ<9]rk-j ͜<+K&\SU[x;p;ίqip&9pMî*l\F.C8F2D6eYxꩧK_> ?O#3aY&H$LXVU2Bl0 |زE<, F q-(mWf,fTq=x{x�1R\�ztN|A9t67b*8ѥ㊞|`y;pT8]௚&} L݄;smV&`l[,z<ޯѝ҂,n[v)8Yl@-(^be8޷ WAt:Mh-ri"@b۶ǽMo4xcd것|P7^U*c(Xt+HM4.vNU60GŽYە+YX*R:Qd2xQ,Bٳ/9�PVae ARg7Ү$,�`۶Goe{ npx7pN:0PTގbB=nXUeY%46\?2VB 4a2~r˲PTqtRU ZZZE̛3 |ރE%KO~2ϻ_ P( ^sUU9,H8tnFeyZ lBSZQg8etb9mqv-#jj\y<?n87O:apQgo.W*{,t1JP(4q`۶{<ɸT*XXX@PmsssBLNN�Q(pyJ%߸bؾ}[X,y 9cIC9JO&OjrN.ƨ*(qqhY\`#cW璉〛S/urJD+jSN!L\.##bŊ0 H&+GE̠ ͛l2X6,,,`vvR [lAKK `Yr1<<۷6mr”R5PLfrWqY:.,V*XdJJS%W�ZpVa] .A&++z5쏌`rr'Om8q4N8~\s50 HΝ�=z˗/GP@.C,0N8h4 ˲0>>/²,i�<^}UZ cccXbΜ9^eB90U#�k*jx U sd.vqp%@gfXGعʄR8Ť 2~\x[JdyrD"۶4մ 0pYJ%yqDQDQ;w(XnʎMr!۱b R)r9lܸ\s_JuDqx5>J7&PSrەJB4tѥVPy�Mw:[e9]WD1Xt29[u٥W(ڪ </W/ d2hiiq?Ts>d3 }Ro>wٶ\.V|>!cÆ �ޓM'ЇB!XҫZ܅Qt<~(~AЬVb~cT-Ș\quՃ_PFvU.bU:Ylh*S>"]*trrӑL&=>c0 D۷o<D(j< i $esL<NOU /HEGrptM&0[Ur/dRGRquc�Om߷W:9-.Yڈ̑j,!]eF% ^lPT}+!KͲN_Jq|yj&nԂw_ u8uDx)sq9rҿoW:<Xyl\u[ WrrȲMYʰ3(m-t_n~HtrU L[4o^U9ιS $Lޮ\p)TuU+MS&qNsa"?cwobG/d7›߾#߄e.ᨪ>+_町DAʣt NRs%cx3٪Q₞@vV,qAa|mxWQ(p7B-!5\.w|Jb6*[ mb0|5P;JˈJ;XV*,m#,ܹ~ !ׇy5!(JV5!V4OW�D"0C& V!3D"W/`inJp \V`&;o]$WqaUr2Jn*|4*DrQƠAe Et_%W5^P(۰Q(h,8TB!B!͋�u)˕NRsE8XtԇuTg2TU<y;vpJ~z~K(to[T?%"5jH ~d*N_|߂fC]Rp@{<\?-$]t\ W 9 bT8ٺ$A,_wu{.L3LwMN9G:O8N.dU�7UrhU�-X&hS-hU[T&Ū ~um1#e͝\qz9ҍW8 kܾ#GttehIDeb!Cq _U ]Vj%ypKq_1*dgBo1*\gsgQWqm۵MUqEu6Ie?m\a򹀮]_4ӯ&[\:aʆU5nAru <B5]`uUU @*~9%*Oח j�*Ke"Xh7WWz bgC|^4us*øjT :Q.?*^_vSU&qsF*Oƥî˞wSUt|ni6_Y B([,W~Aor8t8}~ д1hTV-r7otaTe٠ǨN NG>WŤAT[DRc֒rDu|)Uܼ6b֫dcrӉQ9U9tAtAf\Lh8 qDьAg.r6puU ǏJtA.Xڏ'=FSqA1r2\2T2/W4q2)gx?q9ռteUA;' *A*JsD]~sJM9qK:v/pOye|ٹöT\v|R5ڡ \Т9BHOU QM.R.25Jd98tND/ 5U?G5__uiMG,XxuU͝-g*RΟ?ng2<3hJB?mUo /E_ bkP,q� I,Ax *'qԏ˶0Uti|AbG^z%]@ ;�T(5P/JWG$_ XG$_)X,=JΏRS/*JEWͲ,q=ގ믿Gwgz9ܧ ^+!Pb *g1xTۖe!E{Oi)-)p6H}<]>0qd~rM\_o,S^8CE#Q%J~byTנ~l;BKK Ο?yl3un!ss:DƩ䨪K;7jׂ8SA'~MwAY=Qu5qUe*<3h�ྟ},鷄5n(]:a@)5J Vܪ 䲣y)Aй1\?Y̓+[Dny`ĕʧsA<O|P oʃt:TO9qs~)ԌsAëb4n.$ .[?*tsz=fr]ॉ@'"*GtkIWŝWeբWe/UGAȸs0n 1\y ܺg*?qr_]8bRpqfpkP̴"r%?N'ˤTinqq5nuNqnJ^ЌM9lac4|5H3U jr8;ǏՂ R5*N.g.|TǕj۪H'3mw nP2�<G_TeYYJӭr:DsAԏ vc1ٗTe(WdªqJ&?بW-yrrEu_L(WKMmS`N.SB]b GqX]UQa-djJjy?~uv;lQKnWa8[uɍäEY\a-WTƛŕ_B~ ]ゑ|.:eL �TJO%_ب<6RǽlC]0PJ(YBq~]Amq]ׇ/Nsrڹҭ!n]m_/s\Q]re'3uTM6�/H+u0ɖ9:OQ'*rQJ{�� �IDAT6] X6lcъӵ`ȡ7q\cU<s [WCh\Q엓+r.Xpv9?\r+ 8r䈻J̙3M~y 5R&[ٜ\ X8oAy[RdIMh J}қJzե`5ުR\1?纒^eY(2q7�FGGecÆ `YnVw477A�viXo f.%,ԾްT\I8J`@2TSo/ /pqZw-LQ>e~tFU_NVppz8Ue*̊ Q Z3;Ng�FʪF+W%G]Vݟv>2f *4ۥrUV֑߼.WT&^8(jb�Xz5?+W6 #!(~TnBl(v\QvOR7fl]�`ZGGg37!smA'W.սս*vܥrΖ+*s1EXum۶�6n�܀&cu-HP_:*qɓdȶ&U拓$�j7P c lg:۩phˍ;O ONb$7$\o&W~1&eN%fuoOsApj5JT Hu~I.3=&OV9S�IvP[t|0`0q9*L~-a6n<rE,VW:4uUc8hgΜ ::Ī2R(D9\PBm఩0F|9?+APU-9Wyt}Xt_Ơɕ-$++N_TtQNyʺLڙGRкE9U`ₒJ/$T나.`p-4oOģJj1-JT۲=0$-ljYeg%J|.W~8txd]x rTv'әQf}Z*<m,'Sq%mٸ7Hqv踢y ~҅è~II N=t5X[Ttl)RoϿUMk Ci1tQ,qU UTA(oB9|Cǀ@EE{UEUA]8~<dՔjޜ`9m4UdmY WVmKɕǏ%X8ʣ@5_r$Ird\ Z* #K$.U}.RnrWހ圷ac8;>U2VK9,sύU%'OM=ŋMrd8&U0'b\p֑Tsrv=sRWOL8=3tq[lܢ6r .U eKBwDR?ӏ�ӥEss~ ZhZ�Gcll=>00^{ R3馻.k*j,׏K9U\_N_,hVᜓh*MaЄd<W5gtBWy*Y0|6vC!d&]W*Wt=֪8UInP(maDQMw G9?~8?~ޑ�||UsX/_(UK(ؚ؊~Н;IR9ʨ'琫EVR`\-NYVd2 NAm֭$Vj| XԈx< _-,'`ɿZj҃sV,[ Z ₶H%R[hDDK}ߛîذ2bbdu(7v0>x}1ߓ/+;{J 2(؅ڏÖA*OrҫW]ui0+Wā`rfgg1>>˲~zOey x~SKc[2*.~t[]r/WX(/�,Nh1 13MMnë *{u<]j؎.qOZɝO\ϣ``SOcel%[TVpX8]ZXi𴵵 0B$X~o;9�/'#F+n/Fնʑ8~z1\afffjc!jTWG珺4OUR8?(./‚YylQ-@UPc\B`$?_F*7@\VC#oW~|e2NL'nm}(LQ݇B!iMH"tÑnUz 9NtNH, N^3IX~ YTE}yϛS]@j�V!I`Kr è:QU!Pu.Ytr7 i72*⹧ůY\qHet8縿:; 8e�g//備a>n[˯AL9R\ %l�U@wǖ2LmL] G掠*T2,XX@Fm嚊; '&B䬜l|-\j :u.Ъ{]yuJVeA6m~$S8,-]㜬*H @@`sr3ZJWrX*D~jk ,TPKӮ,ZNotq\r!O2WR>djdj jfگ ,Ti ON>eJ>3t|n60XUTq:}[ڷ 3<NO#b[6T9 6&7zdNP+X_ݤw83eeMMU-U™liQ}Da=]/#]Ic}b883n,.s1�@z� ̖g6X׶!#S <?Ql_azRT:t)X5rrn3&3P(UBksvlnbᕩW- t8<~=޾=Գ8?0q;* a b<0K m6�@cCĈ-!f˳x|q,.'W~ҕpN=mq{.^kװ6y+難 Ѹt9[ qO=XY0cϮ,ms;,Nbcb#6'69V/PPr/:K_W%05[xe̗Q h bsr3"fUŋ0a⺎\YIϟGBW 0 d+Yϝtq+b+u%8;HHFXXhg%Px~ja�fJ3ɍ S͠#ҁ 0}0k DaI�5נ+vh<?ŋh acr# pU/ò-wCFe 0DZ9!d0v32\�0]X~ �2Ԃsi'aѨ TSAq9(:WV} adpd{g4Mb;'s9]88BFvjWqdNOb"Bakr+�`$? G6ø&Aa&>H(ZE<8 NeNaYdX~ZE+2 s;/cyt9>Sy+N1W=C@x87=\27SBa4JWx8: & V^i1Z/_Bܚ�b']Mh(,p�r2B&0g_ĂVY~8~WqjAaN PY%,L'nņEҲ-F`&6'7#]Ic$7]Daabֹ:rrҕ4 @_Jf#e[ؘ(L��m^\Pf%kqmm P-`:< X_L58� [bkb+Fggf @Hk'X,S)ʩsJ-aff..4B Ru~%t{iC *)ɞ̗qR|yq$n+ t4�`0ҴtWVehݹ+v @ƓOS+?UTq0+sxcʏ!Wa4 �̀m33Znw=X,@ Πh6xny"}�d𓉟4UHSW͕jI+Ȣtw3&mM= r?Ξ.O*0`lAg<Eӥi7Z@.a2ډpp@FLDGVidYԪH'F;WDO[([ZR3 ~<c{713H'򌋩-܆p;;f˳.p;ZCY*pFAVdk@+҅Y{\eqHVPҌEŮ d0_ᄉD8L5l!m6$Isc4LDoӕij6d6µp4 fZ֡lpaY;[W|"F6l Qj2t`]:ذq*w`gkk.qrzrkm6ih>J`Cl  ."] wLSZUU��}9 ~]fHAP(4}qۖW2*Lӄi2eضX,-jIQ*Gpmss Z~/hMKu/V醵!qgHM8S9*׵GlLW1:^#0ǘ'4ʕ_>a9/gy4GL?W6մx5:@p}>A.Tˑ/듏Gj'Dz,\.^xB}ݨVؿ?صk:;/Sq%+2 h7.QC7%_^Z*xw_&j{ފfW--xߊ0�\YQ,u_%R.{i$mP(r7Bz/!x^ǣܧO;Mu/JA_Pa /mJ|)]u_ 6{/]_{Ao^ʥcT={rJR |mcڵ,,<NT*!apptZ $=.84۶qY4(\Z(d2, CCChMOA'… 8|QX8̥Q5kYj"q9*,Qt9Μ9>#ڥr533!fgg188R꺜\2 qf(r8}4Rzzzׇ;w0 qlݺ+V@OO[З/@TO?>LNNBB[[b;AAG&cccpK'O" MouXiUݻwd044X,ATU,[OedL&'NZ"ԩSH{j+;cxx}}}ߏz 42N˲/# ###@WW{;A绲7 ۇ뮻NB<177 tvvzm)zaYlƅ 0::6DQ+_~b~˗/(D0==˗ HDMNdbذa�`՘^L&|>gϲ< FU(iTULNN"ˡX,JTD!:Q۶miDTr HshEU_t<kR 155X,iEl*WlNǯ[mmmH$ÈD"( 8y7>`TPy~Z <022B$C^|*?Tz{{W]<[v^.c/^mۘf h M~zd\Tc7;Z X~=2 {+iz;�:t(E(B6F`ݶ*;P@`Y,… r9m%˦:d3gΠrrٽpnrvLI_'C; ;w2 p>YlMsN:??C\.O,JT*8Ώ80QTf177jSZJ~i d*-W+Vp<N1<<^1qCu>1i"ͲcsZOtZ `ÍgPp?p@KKG Ҹl<ymzkP~T(J0 0 WV~C͝\*rD"D"XQ, rxBiY jcኮ%Ͷm۔_+Xrt>ڊb׮]AS9rU,R!XAbbp,FKo~iFq}-ՍP(AT~A-&9(W:\Al .7WN0U%U*ՊKYOűm[pRQ,_-io 4_caz4XXH{q-o#UT�0?�gk]`xN WMmNg@} 8o0{l3 6 !h ώ}^o /ce B$0 i}# FS R}072xp!Ґ٘?tCp-j P\G躆ըo4jjFS֒!2JM3�ȦB VD T^ -kAH@ w4&O9 Co4n;PLV0_ `5$Ylюq8)! dz6uCMrLg&Gn;c6 )ԑdJ Fs~&@4aǡIW+WZm+Ċm�ʵۻjɺa<>p 7%*$Ce4TH&`\3~a'ό#a^D^u~\:6^4U,WQTl{ƻIpyRM,NjOqU}j q] NVuI.psNA<F&sj(AV-<%d:=<{IP,ۨ#bT5c axqyxcNq@-CeiѵGiy :E+ Mk$ :|wz ƹ]у)ct'pafz!_,  "&>̤2FˆB8{akW#G&WĩHRB=IƢHeXѽLtm281 d0;Ts򭖉94dǗ RvE4 9 o':&OמP !21Tl$ ~/OhpcHs&E,!|BuÿQ 2 '+BGb ^ɔ&R?rDs%rT*jo۸曑L&%:NͥёhŎkaTWx^xKwu`|jshG165t&[ p8]]ݱQ>/x9]1raB}]5'ͤr r2l-rAȁs1�!ei@6Jj0Rl&U@i͟F Q9$Zm\2X:up7JN(~yI[ Ó9ux/fH+o:7W1W\46?4<+HtDPhppbGnz\Xӗd|llGA?Ţ{,͢8ژ l:Ry�� �IDAT㶝[h#-Dwa^FOG0 ƢCk, ˲1p"-0 0 o3 m$ZcKa݊O.3{~U۫u=F} V�gdxK$_y'IgvOC4pN.)H9jٽX9 |qJu7d:ר Nvckf8,ݳ`[ۂ+![/ G ٍT:޲q9塱<\]y*0QkM'.^bƉ' m6Wo`CF\o643ɒ%>I|6Cp.u TX}qBWgHB._42,$'?! DW|9չ:o{rMD猍O<Ҿ#$2 iHeUU _YIST%K"̂r,rX@W{_Mq%!wʵw]J44{CTcjLc&p3izy'Bl)0 a ٳE#(z mdX2IIjɆp/ ^=yT3^p?%ɔT/o46.YIOW\'x/ޏr/)F1FB\ ;yqM*? .Vx\ꝝ&i+{+;8~W|b.�?d1yzZi&  rO 4j\ RWq9!9MYɝ!M2\tT=K2 /*M/)9 Ú]p* Fo/y䛱nvxqH7LqT'X8!l@QF~bWD a $y;p]@n�q.OoW V]܇&4v%B \ ݟ~ZUzoCPC%w95ԙH!S$Ԏ$ W -9+!9{I%$Wo8јLH ~WIV5+ttMu}UNBwA hj ѩf˯Yt\µ %DQ'{̙4<tvuɎp԰/\yIs\ZDW p7>)s%́.aaX[E%1 ˏ!D{81 0u>Q:幀[7lr- r̅-[rpt\@/ɛ㰵wf|̡ ]uC#;}M.,āIl/amؽjք/kb&ٽ)PIz]IhB%p׹J6a6>%,P`^N{iKB!鶆ќ>xTFI~d9<^UyMeP{UrtܨqU|T7Npѱ\Be+ 'OAy'ds0դP 2^8$9d9}e,K,H"W0d9PO ɴ>6ۑ+3^g b7Z0/ܺFF]`hN>]V* tT"N*l\ HF/8<^-N]RPmwn[N_+vj7 ȹF'[{m?~, ?~Qٶ7ʟ۷O,“?k8}zcU% u/dm?JEZTܗ rW_pK|s)e:-NѣM2eq1T*<_}{,KmAR t{j :.˓pajl+\6{?÷;wž=} NB6Mw#Ncfz4nVjk֬qꫯT.㮻O?9dر7ލc^ǃ^d2<?[Ew<]?UuM97Vux8ܜjqp8ۿ񛿉?na?<^wFFF0z<v?qޏG}#gGӃ}{bǎ]ލcǎ}{ @6?|ppsssm!=V\+%Q�92-J~d;9nTT*t*>’uvu[ӟ>3`xhg~C/7ߌj)L\ ~ va=== �Ǐ/"=a`]xo�<S }>_*(n|~s> Sax~U {SO=X_@\>~c0Mcch?1fg# H�pR)bb^{6n܈t*|>H$�$6mڌG&&&Pܪwm.n8L*1EUQ'Ed}FG#/ڐH$j{3C0C&݋Js!Ck=Hb1՞}߿-i%#`xxB8"oCWgofqs2t9eUPq:T}9Y8n '6mN add{~mmx 7[nEww7֮[g}+Ws=B4 `8alܴ aO?zVƱ�ۋzPD[_wrsW8~A82yrgjj ]]]XGRAg2>=u!cժUp.p(Lu`=inl޼�7wwuٳhooGKK V\ \Q{ʕ+X[.l2vƒNjrlƩrc*SA2 r n,,,@k�Zb1_}Iok<H&mԐalڴ ΝC(Bk<itww#\.#J C#X~ 7+6-*t>Z':]*쪄W$1M#MEitww{ۧKWQp,V\ $~sJQ~Ao{ۙLa !UOAy]BɾZz8{7?RU-(?hF=i�j۷o/~(0 ?ya6۷\C*re˄s$?VKIqUUc}] ImdpNy'XJO%K-y.^u<C o~�`xx\(˘^3gH$X?/chhH)ռY\qA*RC+GssX侪Ta5p~`vn;v@>G8\n'ǻo< sax|, R ۶mɓ'0eG? ˲p _1H&wkVc]я~!ع5JѾrr㗪S +=GVuՂ.NMMa׮]/= DSN9m۶אp0~n؀ǏaxGp1|;zj˸in|0$݋{Jg> 3T*-lJ򂹜\險q :A*h PU(Z 2SI '˲`q- au�QLLL-C㘙ŋOhalr/fTɏr&l1$S51^LpY+(v932,s0!>cvv�hGn={pѣm|۱_Keqx��WkOP(C=֬Y,TvOǍXT}hPUk,SU\SgD�4?"Ynibzj 'm[?? !LBVr9\6<cرc' @&- �Xnu.s /D[[n{pT*~{kH&,nTsVuLWۺ Bumٞ ӧUOW ƫ0=c!bUrBkk+7LX,ĉغe+z<ԓ[zj`ժU0 BD9* cgv~_ @7W7+US6aTM+- gJ.Ax^UO'0 <y6l aY]'O4 [g cwanntׯ199# �f FGGQ*nÁӃm۶atk,*չq4Upg ٪:/F I{L7�Xf vZ�s##�fgfpB8~8mۆp8'Ob۶m8uz[k q{n Gŝwމ1y,[ ^{my Gc. b �t�c�!&LDHTeܠgCPq:<~ OOe#KLJ*-bݢS ~V]rUm 0AjJnTUSW0,f4HUJ\sd lr)˗sTͲl %[�V (H J.q˲sd dQᥲtUM[op 2_X2 @PBW"p(tUsEu[UA*궹9  =yyɟiQ~Q#+_ /Ǧ͛qCزe+laxnsfO~T ly{.14B$pMo;J*bo54PMJKl>.:wVs~0o^>|wq'^|e|sC6oo`ph'N/gȏ~/7aURl6,[d0BXחle_.8ե d\P}6ϯB局Ta=޹>Gq] ףfi^XBa6�0 &&YH*CljMU*oU&& ?l0 xJH`� z4F3L˽[v6|sszp8 {,btuvRRG8|;v젹X,ߠ 4ٴiHZ[ZUVf~oM{x|O3'Odk==1-.pا$&t_麳Q$#[6d N7%KNC$0H#TVV@ @MM E&sﺋ ?x,\XMֳ}?DkxںZ,Y’%K8G|@f*òa'H$q^ʻA ӧOa飏0<>{/橧"R1QB(w(&65_VE'~`6w$؏::"GOXE2##<xD"w,Wxhkm%jCR)vJFGzV.G$᮹s B?[CYv- )Y~=oy ߭'222Beqi,]…3_L 3m )! ( <yAvI ث}̙2JrL H3#?ײ,:477399 ˏ~gβz***v*۶mhS?}`Mme|~"eO$7|}}9}7nb z8{,cc7z*R)JJikk#Je�<L[" +S◿tOOS&(hn*9tEzE/KmGe˖-<};mE.gβ(ׯu|:8Hj::Ynz{{ysq.vuq0~rEJhkm#NO244ƍ8x�vb||7otR>>1fQy Hgd34,)*s$[ "Y{ԍ`Uۮi/>|r*N<ICC,>]Y0_jx7rDo4M~?Lg�W^~ÀBGD~9 z_'Jaã$qSik|\v!YX͆ 0 "A,02O% L%lU~cKW"M_E_].:U-[*(/~s~q>c ^)H<f,1t֢4 [lK?8tC9.)[#FU9~;w˚5kRt?Ûov6ROf:p,Ib*ǀS[9g,>˜Sl頋Qqqlq܎-F4ttTw-6!ȿ/mJqN ٍ*eu>pVNvp]rdtUn{ݎ:믿N,رcLLLh;&'gdo?LNN3µF0e{ٳb1ߒfd Fu*]c"n].oQqƝňմPlrTrǎ9Rxr};yˎ0󢿽{yx?F9y$}}}t348/ߤi=,*gU}e+]<VUEڲNS*[yȒnoo{A't>Cw? ŵW`ռ?ۿ ڨ7jjVdܹsjko~﹇?͛ilh0 cXk֮0 xINjx?+Wկ} 0uoc�Qb`t:8}vfxmgx{~zzz(..f֭:E"g-{t_̒% Bw8]Tq\::]쁠ںKJy uiV\Ecc#7mbhhO.}B2d5syAz!yfx?x<E̟?I=B]]eqiZ[ZعsgA6o`׮$ .\`ddd2IߍFGjap9F~vEkk+UUUtvvD8|+V`U|],'mm%9e MkbZ% ύNCW$|9NL,G6QQQ6r,^#;I&8{a…&c+3 'N=Ѧ&&''yߧB*--%-h",bǎEyDal| .rJ>lld rb�Ջydz:;/�C.X,GFx0===464|sB^1TG-YѪieRVqzi pʟ�|(\zacDEE[U8~WfX9 h+WrG(xudxǙ7o^AJ"_lҥKb g@ @KK EfOoΕn�'~x\.i۷7ikmm۾^x{T lT~qɏ;ur2T|uΉHW\c3Ɲ9Obã Fř3gxfN:/\[jB]]]`n$}Rr/_Ʋ,(2#äI/@��IDATi.]tqene˖ut_fÆ>=W%dP(ĥF,_�-b}hkkSRCc:#)[ĕ9YH2[H`[lݺmOm+Mmm-:شqxяaFG!8A${)  |z{{9qMM`݅ﻟ{2gފba,ܹ:t֫ B4}SYUIjB{!HI8[b *ixnd뜊n^W (3-4�nbId lO"`jj@ ` BxI^/tv^`o166$4MR$(R)Bcccx< L,ˢ3>>N(²,&&&0Mbe16>N8&ˑL&)))! Jzʪbd2 0MS2TʫݝJ.Z6oq1>/|?( �bXE `||2$  xG"Wy)--e``R·@z|(;LFFFA x(**`Ni)i2>>~[<OOO388HqI8ahh9sfЭ SdtRJw?TH~M"!Re6Z;eJ6N<i<i1?$T :]J?7Tʙ:ٝh@GCl$ ̙q+L6Z17ֶ8BEKfnh9vbb4"զ骄P۠*6) xdUH):m*}m;UvSrbg'Feutq 5X^ȥ1˟; ?_|UJk " xl*UPNG'#eilۘ*_ESů R#DLV2ɦ+ړ2[ڏ^Ă TdFmT'C*urv>*Zn)Q;FWq^Wt駒AgkٵUUU=g+Nv]Oxva+]hdwW;WBўI*IɄVu9Ia62gUU.SU:)v@]nKtv9JFXudr.̮"8'%J3=&vDN]\VlT8* ,N.18% nCV?|NqrnC9Uc:':uT n,V*<7D9l B[Fue:&P|_III  Ovn_s!ɺ:Ug#Aw;dx2N0mRGcPu2|čdt*: rӍteuuwHxsc'E2CnTʊQđSdѐ(CD:R9%7=̲Yn[tfk$~*G(HGe6{c2~T.,!2T-cnɟ,�펓9PT_&Dl*b@Uc"ʡm"n+̪,A%jVkD?'\W./����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2010/06/powermeter-example.png������������������������������0000664�0000000�0000000�00000047561�14502137606�0025346�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��x��$���3L+���sRGB����bKGD������ pHYs�� a�� :ek���tIME 3^���tEXtComment�Created with GIMPW�� �IDATxy|T?ϬdHQ싀٩b- jZԪTjl]Z[׶<bEADE"[!HBNL2}aLf~xm|əs=2!�ur 1#""""&xDDDD ^B|M׷^{+?Ӊ ~uL&fej<`&"" KU_[ط;z-m>~Ws16>k{t9r�0w6DDDmEY :[J4[t͙7aa>v۝':vս+SO= ^o?<k@6V?_5/^3{,kxMmzq׏EB|�yyy>rfΘJO? EEEAţsñqGW]7>l(2xӧǛ6\yf5UN{?;!{ǐw_J=,>ݲ/g<ŸwK/ݽ/w~<Gl9;}mM{~~xzxM5=3��;v|[ ػw/oiPxf*:x˗?#YYR!=7z UUUX ۭz-BW_?pU*_V#!!7oƟ'|n<ܳl)%x^}{Cʜs1o<�?vwR:>Fj-v'O.x@C�ܱkyKz~y/Qp� 5=_�rxwk q]wa̘kNc̘1WB_i?1rNbȐقaڭ[ "KmHf uCĽ�1#"" C"??BXXnfm:۟DFFԩoňðyQ׻cLLlO\TJ�H|Wp޽wV]z}Fѣ�@<1 �8WpW~½>11! 90L7m48N<t:1uTep8^`BŻÂ A5X[[l+= qX,l7WtAܷtq!!!^=u7��l) ?7ߜ �8|00mT]sq֭BB|fl]xM5=3@ߥ7e9^{c݌7#='bcc=q7/|&M�|Wطo�`޼Uk>;J͛ط;lt � y ""4z:#)gϺ[,_>gjmy!Qo}- t6=~eeHsoWZZ7۷o]+;F|.**-i=EjJ={8qxP񨬬gׯ]ĢE Eѧw/Ѓ&)2G `u<qB̛;W$?)a6Qg".h&0?#Ǝ_`@SP2L&waw1(DDD3f�8N .3(DDDip3DDDDL 1#""""&xDDDD uO&jms%%6~Lv6JJleHPۍwީX;1"Ӽyy-.Q%x.N+WĚ5k3ϴZMāF ^ 3}t);pi",8ǎU��/Tc̗u?e#""r?�|GȀRDJJ }72 'N48q2 cǎm6p kwh<bL9l\}.~�~l٢ǯ]v[4v�mQu0iR'N<†GrZO>ѻ{ܭ[ŠAu���{orssq7e��ׯ��xh-Dc۶,ƒ&v|2U!�]4}-Zy۾G=@!OBmL(l?x>-:!F'�[˞V&]LXN'J%j5V+o`8~8�jW^h48wZ!,,k | Ǐg`sxÇx ش ANuݫ�* 6(e#X,z|yRxBƼyyغu�? R !F#<TT GDza2LvfHh4r~L# ѕRAZ-�`�ٳg#''7nĂ ��j?}YEQQ~a�߮Fi #G6l7vl8lx*\}8�@HH|;@\ՎxjB^85V(@\Qkj c˖-� � *={R7cu6-?\;X~qxb &E)Ur.gI,SoѢX<d n5+{ X,Vĉ#::v'SOGŊ+��/kylovӦMu]lRp睱˗,Z-s'xo |S˕xu;>}>< 3gFy,^Zq_moxnDJ1:"""jS} ^KvTWWcbĉ<DDDDL^/ݎ~_WP]{> WaļтTQP2DDDDWbRR;53 p) <{ݎ7xv'OFff6$GRRó{ҰdɒeU,^v+WĤIFӡ_~Y*IKKC>}�4<zN.C.|]ee%֭[Fӧ#&&m<IxfRRܿ�PVV>�3fX� DAA֬Y~mgbС<SͨBBB81N81NmݺXp!ӽ:t;JII={`0 ]lhZ<#w/`ۡVw^bȐ!p:X,h4*ɓ'ӽU`4/bոkرc(--EffW/`S'Np6f'Ɖqb'f+aZ}+к9^SZ2 eΝCBB{4//III󑜜pfanGii)rVI"""jkΝÁZ}^/\<7>8~8N<8x �tW_ž}�4 YNRЧO;&xDDDL&pi(BAA��NVh^ӧ'==ط�`PVVQ>& :8|0,KP]]A|Nr'QQQjsZM]]8x WTTW_d–-[~TVVh4gϞkÇ#''ϙ3go>}{;v �padeeyɵO^^֬Y<⭷>KJJ;@Tkq'9992e aXp9\{~Qٿ?&MowuΞ= ={6M{* ÇGBB�HJJ]w݅=z�}\;f9r�p!?oSSS1sL̟?:6x嗱tRDGG#<<Fnn. A̙3Ͽ* %""VPRRgh5<h^G||<�G3f .\q츸8}퓝U}UUUp8HLL[C[Xaa! Çcƌ�kGL&CXX:;v ΝOv?#""VÇcҥ{qרk.\?j>C ヒq]{4i-ZZ�0h ܹ r'|NB@DD<"""&L,{_~ԩصk}DFFBBBPRRa)_4w(((1cZ}j4̟?6l�� 82T*$''_Yks|~81N$IJJJmw=->>uh4K} k.W5N5xDDDԪr9zg5|tǏcӦM뮻9`GDDD]5\{}-<"""NN@t̛,:9]C@0DD+[w^`<@@DDL<.=xDDa1#B9 u m$)Q>39L#"_v0 <y2233[v-Z-F#,Y<.{:1 ubHHH?+V`ݰZfaXt)6mqكGԉ&KKKsϥ+ !{###� N!d2Y$xUUU<3tuht, 81N$Iw޳gO�@YY>̘1*˼Ʉd!!!Z_/!!gqbb l R�>1NԁޗlݺXp!ӽk4a[Zux Q'frr3ΆV#< !`�4 �h muRN!Y",'''OĊ+VZш_|WưaW_aÆ (((-quRF%",Y%Kx-h4Xz5�@PCqq1̙(&xD]UA "&r9z QW O#"vk)f@{ "&xDDDD 1#""""&xDDDA bGDD]S8"&xDDԕ8A bGDD]Sb<""Z hQ!Z"&xDDQGNHBb Qg.: uv.DN]B$XV PWW1 +YaݕU uk^ǬY|&VBRR� -- K,aGDD]3D8{,̙sNC~lٲ Ul2ݻ&J8[ӧOGLLL&xUUU<3ttvN >u8iZ%IqjM$!<<<YO?t&x <St$+y$y111TE2@O:NHͳrS+'Ʃ{СC1tP�@JJ =%"vk'f�ݻ'O�8NX,h4fMǤo555Xb�kƍ㥗^Bff&T*UMDD~ g 2ezj�@RR~i"::A ?8M,(8* }i>%" uz Sp=#& "&xD!""&xD]Sr2$1#`<IBzK6x-QDC^fiaiPF00ԱpL 5"I I)6^Xu,<bGMqp}P.E_D<CDL(�WψCq)qم"bGD>l:Fԑ1#bGD8SQ1#�$!0G:4Ǥ1#�BQ@PQ&xDLN@P1#"؃GcR؃Gp2CD>oIj bq?>ɂp 'vGG1#:^Yf\vZhZF,Yo#"NGM1(؜Qwꫯb~faXt)6mqكGE9<~: uxnٲeػw/L&��BALv ^UU c8]8y}06X*ku8Y/'Ʃ5I7LHNNv Fv ^BB8]8d{?!r$&>QZŤG\jƉ[0-^x܀a٠V7k(^G-N C{4��t:BCC!71#ȮFG'1#j+V�� 6 ذa|Mr-A7YuQvaGɚd@xPYQw7ezj�Bc=b̙3QQQLAJԱt430Drݻe0lD]4'-Q$ {-;U{ j18DL3uaa|R C:S<""  u)$�1#"^VGD-U׸4�BA]QJր +[qrV\Ǐc]l. <"jCSQa׿r^GJ8DK?TKscrGM<("&xDZ {𨃰I6<]fަǙ3TE"GIWͺu0:Xq8 ]P6Ɇjk5ըX/.f=DLrh{Q{q '�M 7!>$bZ\9~*B@xx8<n]-ݣfDCwpNĘ1LKYv-Z-F#,Y{$ VBRR� -- K,aGԝYVI@**o9F7Q}Ofa(++wFӡ_~XlYeGل uPʕL%xz.-?? 7BA<̕p8Xn4 OK𪪪xf#*)k\2:{�n<?P-U�Ok>Y׷hjJ׷.>1N$IϙL&$'' jFq/s8HHH@ff& f<m%$$L1NUL֢!"""|VlJynL*��%_Ŀf0.W<::EuqHFVfZfС:t(� %%{`@dd჎'ۧPaCeJT[y-y18 #Jͥ-JVTX*[$^>@]GZZ �4\k \!l݋'O�N',G/|r}6}U"K0�`fL(lV#5GiȈ@&eɡ݀ wWn.GF$M]ðaW_aÆ (((-�/իWk믿cǎPTLɝNN�%w2Zֆe�?UrTrf&Ȅ 5^z\\Z~xZHj:5B{ Ř3gnի�IIIxQZZhDGG7{\&xD4keHl:j$砨Aٟ~m@c]KgE)54{-G<y,QɣO.wQTӧOdX7IHKv9R״N9�d{-pq'SOu=s4pMwpr> 5J|9WwtiD 0ۯݏ]|L_g�� �IDAT^L_cqۮqocfų 1#@%WfGX<z%_ZNk]y_&~|`1Q5xD<38 � QP>]Czdd]kWmƎS"Y7/FI&X%+D#7\+sN\w )TӆPE(�VZ[m]u%q( C隽.\P@\  w#z$!GÏ <"j]0_hv?WrVmܳ`#׃xߑMDLZ V! q8mavq B!:P+Ԩ0W$[VcAA}U|^^f:>ޡG" �0;y6CY Q[;g8M].oN./3>fL:p髶U#KIHuOOw#A*,U(3A-W!VG#=,=�A< v=mzD,\IrjsN TQe2hmZ$kIh[ dȁ(4A%! CPinX:0È<(S85LdLm3ug\ ֪ř3D]g"U/16l$$(dy_kTHE$̒z àA~/2avB09MUј43`CBCr 3f:;3gi[Wn)7U@#4[&M`WT׵{6mѺ~˽U{Qb)A2F;aI2wXoϬ:~N.H$Pb*q<!E-J娱 NעT"c4 &39L]B`@�|WmFa6O  T=OSM 29M(1UQM{ TB^XpTXul AAߧ'NG{:.`WmơC^S4)~eRUKvL7]Pit/ SL\ue T&>>!_p.L;-s]QqJIn6;?xqX%+< ^ w畘J`+H$ Jq6- yW#%4%`ϟק6* 2 ;72o0סlq!:{ (354V=+Nꠒ6kR&\I+;zs5}z T&�w-Z8gRX3ejkZcc L;q,"TH M9?g g~ũZ:[2%᎓g: ! >{JMѷ4LGG˹+wOD1.v"^_H+#= �#ΰɺ]#i TA.+!g{v|_-hn3P6UI4B~ɺI;/D*&zm_f.ka<|Y%4 c8 ^~}yN \TguXq 5s<^hZB 5,JYiDҝ3t�Ž33"UW=kZ&W B><åןo ަަwV>zG=rr.=OӐID&uձ_(6sggrd?\٭<IHp_S5`A%;22ú^w7yyd&f^`A}?\5}rs+3k}F:{]2Pf.yN3N՝X؁uoWŮ6{/_QL&x\45u:ˡ=p"ߐ*GZ0e"A +w{(>^kբ\0eaeJVJ+IضTsIźVSs]nl+,:U"DxC5yfz!ĪcV\;%%#.ӊS B!,P'յd{NpTI$hR4�TQȈ5V/V j8>s^*kYπ#Yg-Uűc ^kHdd#V m g g[*Vd R 'uSJkȮF#PʕHNLNO霕Z(/9qc"Q;tV~DM"bb~9:kpvK"!1K_vMvb{Bowy)baɇQ`UԪ^0y$xf *0hRuPqBȄ B& H! 䍆@*FRťa?+$t:!W!2I2T*Vv (`wڽ.WPP)T^Cd2II<s7\_fkxe3̀�L2 BSA%T p2$F-B e2 ?CUPl�2a5n{ :Ά hKq^Lp:eQj* E:Ar,={.4L:sR\9%4N2d28dNe 6NFa0dv!r؝v2(B rߝ85UǛewNt:P;!UBxU<+Qvmak8_k$N;T ;"`7D w\U٫ d,P*p8G3'Nk,2^KIp:eR)LJNɉؐXp(;q NU&٠+`uZT(}U&?/np8ʤ+.o*k_ױ$xVXk XSrX2ExSPaP f-4̮'N`gN�p. w]kBqB JrROv(zB ̵WCZ澸]GR+հ9l^Ϧe Qzޚ)DeS m;vi|~s2b.s4~CT!ڭ~OM,P\Ʌ}4-SlΝOrWR&wAl8U5O|JC.BafDxH8V#T*v=PkSZDj"a)J8nc||5W[ U&Z t\q LXq_\er]25_mQ >5'׹wU7zߡ F#ýG,jr#PAlӪ y ]J&x];[v-Z-F#,Yo f 5Ց ͆˗cҥشiemGFFt^+MS}Ebb"f<3?~fmZ3:0 eaZ-N3~k>Ny%''{ њL&$'_zDHHV+4Mi/)) III<SDDDDIjT M8-ݦ)]%iii(((�p<Bf܆ Q4l0cÆ x7q-��jjjbŊ1)$n#$$cpHR30]Jb0XXXXXXX'IP\\DEE=fs8p�zfr7xv'OFff&d#~)rssaX0sL?Aa}b}b}b}b}b}b}j+ɫgbΜ9eHHHŋa۱rJL4 !!!F^^"""0x`(nwBPTTX,xǧ%l6CP72dHPO.Iw}bۧkŸqݎ�h| n&t:kHOOGnn.~G 2U"%%555j5kr9}|ZRۇ?QQQGVVFt>ݻLJ'Ol>֢gϞHJJBYY^~e̘1* * ?L믿gt:9so6�0>-O@swߍ*z֧a'OiU[nE~~>.\t� c1T*zmORԩSq7�?h42>-O@Ã]d42>l>}bulhZ<#d{رc1~x`䥥@.6 Jia}bwt>1>l>}bU<y\�Xj;v:(//GJJJ:#F_w}UUU<y2BBB'�5551bDPw}bۧ|LJKhZXVQQQrCIPZZpo-Ovڅ�x'OljOns;P\qs'a'OZQk1 g0"1> QrUmQ G5؃GDDD0#"""b1)GZAF³ADDDt5Y{1uV+Ȗ-[p[yFhQGÚ��2�aa!=a8F sC ۼmDDDD2N/h]qċv}ᇯB؝dJ5aֆ;yW/k?_~*�kFY�PVkvr^)3-q?uO|{36UƓ%x_[qAϗۊ?�%J+k��:ٸZEFrJ5N�m6Ϛ�LN&Hnd:o7]!f%scׁ㸦W MkDZhx8~н$<pM$W?ڍa8p2906k#]>'|2m 4��r �0;0cP7x3Zd^5g2[yVsdNIVo0oD <XT9j�_ ބ3q8k#]/-r9DGa#2,="��!*Joзg"BC~ G~I� &2ܽ\!ԹR(Ƅ#؝{=#g/@!hA,~=d5^`ԧ'*%k#]h4]rJ<sAmMw|z7�76}_Ϳ~F= ?˼޽a@OΜ)᎙5glNX~]ذ� 7?L͚HDDDF&A_~A5+[ref(;f&u66Į; sLADDD].b)@DDDS1#""""&xDDDD.e?&Zo׻?mtGQI:^!Z""". QsCDDDD1 1#""""&xDDDD1#""""&xDDDDԩiSu<nM*Lk7Z+++u}۹sG.鿶~^kl%xNڵka4�x58ud _ߊJuV?kQPUEU݉j߾}Xh;.16DL1w\|7�o7-q9HMM9Q\\foaC?ܽٳзOo|9r7gބ .|�`ʔ�h>l(G~W^_߻>Oz;kQ￴?7)1f�>s$#%9 ӦNŁ;}{0e4V"&xͿ6޽ �嗻1gǴPPp'N_zq;^�=n)rNT)Y0y(,,?ʧW��nv|y3�`ӦM@T^YP|oBFFm6<CYDx{ 7<Gy� %exe:}{o>Eta JB;$IbIpB!zHbxWz0B!˫MwѣF7LBqӍѣG*맯X.?_X|GD߻'>./%9I!;o-/~ԩSBN^8Nqi1edV.?a<{o  B8t_Px :L&$I^.$''{ņ?t/OOOGtL >d3, ǔ`Z!I }+vĢE =Ock/IrϜq_VV~�K;Djj*~y]XN^5kpYV.os>Yx,0a^_V*7.qN_Μ9̛nr/a0x {sO= <fLL vލ#gO�JN{/[%M/VED7߄̞�4x=܋^xӷWFıcӧqرD]Ik}zOEJJ;w(,, 8PVV&͝+;Fdee[b@~⚁UUHEEE>˸cbbkg}&b۶m"%9I O_lv(%C%Cٳgd1y$qi!7L/EJrضm[o^wHINuD5DqNxUرs3v"b{EDݞ+E5l+""�].\DDDDL[$x;wܷIIo@VV6M5rܿo$a֭eeee5rD'>c i/9)1�Պ{zO\Eҥx_7{̉'Hx˖O-nq/;',e=_^U><�x?0s,;۷o˗ㆉ߶F#~o`2��YYYa$c|ᇘ0~ɷMoBqh:=gϞŦ7׿~1+<۶mÃ=s]}}=~`ĉ:Ѐ̝(̚5vl}7 7;?&7Ɖ �3VaO@.}"JŋW_~ :NbɝS֟|^u/0~lKǏB-sa {n|\0&5Ɖc !DNN:eJY >Qgr=xvĢE .-++SO>DEE&6&5GƺuP^^C�FR1j(�sj4xnDDq|q#nm:+N_1}-s?tzwbƱc��7L';g <;?<`߄& 78u =~'O x,ODԩޮ߇\1hX7O=BB$<VWdc3M(orQi޽E]]]m'",8UQÙ,QGd:?тZ;lv;*"4* AL""OD ĜN'F8$pduF@\p( >1D[Z��IDATK!MA-:côIiqQ }"&Z-|8lf祗p51xеW�IIop/oyNNM_}"9]ދhC3Ց$BZ NVWk, HOQ#G`߾<sγ}"fZy+-Xha}&LbB(,,}=jB#G;E]oq1t4pV<ߺ~zC{) )0Xoʼn*5IbrѷBk$J(@mu/xgd۷ocnjvbIoz'U 1p�Q[[+ ***<wߊ~}7g.Ŕ)3gHIIgEEEB!t!n"[4`{вBf3Da u=ЃbbIٳ:DvQRR"͝+RR3E^^B,yӍ"%%Y\1X_g,;QT%p8RkDQū=_]/NUSۧ7L'N_^:'Uh7nsETTfϞ?XqT2oO=TSSScN�ci((8'b�WW9sˋ/,c|M�pq!l6{voo>L8~;>ټ�i&OT*'=ރ#G�fxߢ�+^3v5-. Nք}/˕퉺>?۷oG}08z(�yl۶ >l8Db&M~B{I&z|3jB!Ef[,i=E}}B^/z}eHO)nl/)jkk.cnj}z"5%M~B!&t=*WzGRBTWW/Gri,|}Cno=2Ql[o),8<yB/^,K1]>u?WǏ!i6VÆ ���$IP r WދׯZWVV}// |8tB3gθ @zz:cb'aX0|p=sqq;p]wcHII|Ƣ FZ.`%7VpaQ u;]}JLL/ !!!5z4��vĮ]zPUeD ] r*BUO\ 6PZZW^ynM0YՊ^{׏kh\޽Eua�P ?V@v0iDQ0nB7Q5mMlo4L2U4^bB*EA "(( ? ,ppqb y8gp_s><f8y 6%׹-) 31%5*4X|ayI'ɓhjj‰>{6�l yzzbhkk7_@�@qq1\0jdfVoA ة2/3*M[`+k'_ӔS0 Ɇo%_'".^K}}x]]^/6C2Q"f˭[jʌDHHHHL"IOOȢE ;:[j 5o/OYz3gd srw}GEDksJlm_VV&AAjdd_)**._#Fȶ$%TTIEuܾefe飢תT[[+s犃V#~~R\\o;'"otܾD`xc1ߨo3b 0C+z[NDOODfYܹsekc=hXdhoHDODfQeDDDD�N<""""bgL-s8v_ޙNhQ<'_N26gP'9J9'SjOqq1N V?8wKه(Ko'v{V~Kbc7lY3QUUe~}||ߡY<x�"#zPSnjJDOR{\ K-CٵrlذKbY̭[|GgUUU(((lj(((x� ;[?�/?8tbhݻ�7Y{mM ƺpjUde˱_`88h ,oSb"ƺAAzzzg0cF =]Iں{ʤ9>r֬^ o_j`CC.\F�@^^MMۥsLڼݼyappbz2A Pj*eD {_0dJ<}QQs`aaƆF jDdn ^o<,�f Ǒ#pttD �TDDFR5[-L{8DDFMVZw~˱ÁC9عk7b,�1$h.~q!ー( ? ( 3kʐy7ڼǯ[)(?]F}ԧԞQ/XƲ>%ݒmHII̘>MDD<c2qxyc`iܱCF8+VHaa .O-<s4ӟǯ-[yyN^/xc\tQDD 󥥥E<f455xcM-|444Ƚ{L+i듩}dh'>W1g.]4z={֘6m:qQpyh4Y}))i/Ŏ#UF!!>($Q *=<<J�qM 7۳wkko*Z__|6oٗ[��  .I43aaK3%̙34.aa�gheB FO?aʕY?q{K-$QYMmŋE2.::'t-Dzz:޽E :ojOqeL ໌sO\ YLR9/њ1f\rEDDJKP;TQ V o/Ojg+Zu-]W i_=-l߶MED\{cmMuhP3--M{q޽KFmu: %//K /њ[}2e)4;+K<EP`}"C~>-|"MDO>-X_Y/<? [&!44?e"b}""6xDDDDw1 #""""6xDDDD#""""6xDDDD<""""6xDDDDyۜ""">޾˘-<""""bGDDDDl #"""]ԏ����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2011/�������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0020555�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2011/08/����������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0021004�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2011/08/image-150x150.png�����������������������������������0000664�0000000�0000000�00000035676�14502137606�0023536�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������<q�� �IDATxYp\}} ;@b%HQ "Emo;%urV,/MJON݇T!.lNYe(7�  ``} 3XHdl|Napί}N_N'R!'L&�~Lhh4xBOt.O�2f3*F .O}}=\t]g{ kx^H$$IVWW1DQʽ[d {ȟv𻉽]^î`aaWװ+kX{5= v{ k]^î`aaWװ+kX{5= v]=BiyQ(UZ+~'\}5" ci`96Zb;3es'x\c7.q(7�R$Nl8hvNXLBy%݁ n*JHTOH /qE�,&qS.GKI&ܛи? /+kߊ@ qNÒ+T O0$W8le|f/Tip *?jtج>R{|F,&-wFTOO ʣu4.PL˞ T Q!e|%}XFI$Rx�%Krh+g?䗎R:ȁ}#1dj ZxsW_ڏ;f+.SZ\X{8sanP\hfvkttC. ߬fn~6NSC-O2R: EI$bx\ ht?ȔuZ.]bvϡϹ_ H$1AcM>?&VI%c 3=J 't]]ݧiJ*4-7qwpR H'nI#b$eoܾ#Nׂݝo /`c%$)Cqr55*8weF1 7zng=N8o;l?JD=YDOpO2kU1R)P*2B!GQB 4j%dD<R)GkP!\^SdNEc. {Sh"whL ݟ.eK{eiM]΋)  ~ߤ=J+ `Q?wd26f]mh3ϔϖg4x!ų<7+v?)=KrM8]n;ɕ_<v0j;e~V8rar<K&=3lgKu>v~oc< G&̓Pu?LI/@z~ $ ،g f9a\49=lO֚A*la7fjk;ܬNG.^s!Ir33ɄԢ7k3 KߪWʬP&fvm5>Y9кS}`tGz6+p<c2ovYة49f4tl5ج1nvzYxivtw`(e}8,pȒdVzߔ|yxy=Mgkx*V>Nll"<aq;u{!.LِJiT?#])O#%3MSӱ~7ipȳY0|[eLR,--144Yteu,,,d-n'?{D"/--t޿0 aI&LLL<P^$@@daz L&r>eLdrP|xp X__p8FLNNr= hoo' bZq\aXqe&&&蠪Y8^;w`4%ӃBl6t:}6ݎ`֭[ Dp8?NB|Z- |ggg T*1L8|2Zq~7oHss38NZT*T*}p8۷zav;KKK`6y&7nܠJO~O"@ӉUϰ<vC9<<B rtX,J%˔0??Oss3~)d``�N$!300b~_3==MSS`nn ^/\.;SQQA8f~~E�j|;$I, ,//STT*gݻwF<x?JӧD"ܼy˗bX,]Rz= LI رctwws1l6.\ZFՒJxq\LNNT*z<sLMMa6q P>3gp5jjj0 -=\>ܝNvCrXYYԩSd2�?< h4 HRjvFQT<yh4pu% QUUL&CQ]];}(((Z8uL&J90AIRh4f3eeeX,8===h4x"Dh4 DQ***0L RmXh4`ِ71zh4h4t:`c8^^^j|>333ݍ\.G&qV+SSSLMMVYXX`mmِkl6T&,olW(BdLOOсh7 0LAJ%jL&d2$2p8B@R!| J$XL<FI&B!#//B!-I&""X"jx<N2Djd2"bݙ#IR( b/H Ɉb$  siJ% P(D2DtR)p8Z=\.GPdE)*x`VMO&x^s<JwR)B:.k7+\׶&/a+D"A"@RGOCo`X#d{144fR)FGG)**z@>  qUd2ygK&"=t%r0bJG t222ByygggfՅ //IvV+L&s~{f{ʕ+NQQF|v;/weff^c~~jkkD"\p`0̌񴷷s XZZ"DWWyfggn ?g~~^(6 166*DE:;;abbZn߾͛7_aa!.]"HPRRٳg匌rrRhkk#Ht: H*w% 133Cww7Z˗gdd)bׯ_l6s-/(TWW aRQš�Ǐf�J%TVVby饗hoogqq׋s 0`۩EV줷R^^ΥKz444t:ׯs ~ߠRD"(Jʘ3iN:q~:D4%H 1  ,--0t kqv;]]]ΒH$\t`0H^^xbzzzG~3L&c@ +W㔖RPPe/!zrf3/^;w"fEEESPP rI\;w҂bA.S__O]]F|^/:"jkk),,b`2())pp] 8<GV+vۍFh4L&# 1L|>())D"jjj�0TUU188@MM r ׿bfQRRBQQ$ 1ЀC ՊB@TJBd:3g_;&,Hx!ٓ4bBO$w69Hwb2R)}yAzcq ŖreOY]]7 ??k=f#K&bȦ>)mL`;w*24<�i6L&5S~XGyOC>::J0?~Hӕ`0͛7>saϹv;l2{lJf&fu.CR|fJv6yʹ9�0>>R6x .ѣGy&LMMh<FGGl6S]]h|*^ [ٗ]eCI<i˴T?6!w\\t_~�***())wa}}abCCC>}A}FXs,@mnbeنlezV8r搛fΜ9Ç~l/(IOᣏ>_:055E0ڭfUrtن΍?[oMYȅz'!)TJ|kUIgfy`x+ٝpln<I pCv G;-xVs_-ls>kM-vz9NfBk܊s3"DqOvaWL7xe(,M#[l6ld9!T:W=sYvO6qx\,V0HF FvHD6!% !L&i#w$Ǜ~ -dp8L* TJO#3<pIny<ir(5==fŋTTTVikkҽ^/]]]԰OS\pV˧~Jww7X_$ jjjhooO>rE[[;XV:;;x"6 łL&p"+F  \Z.YYYƍ455111dk?c뙜dqqWj*GȖqqr(Ν;\.ZZZ޽{b^ss3�k|gGGl64 ]]]⋴qi9s ׯ_GHuFGGy7蠺LF<G&h|b1뙙aeer bQ_*bllqn7pq^s=mnG*N9Y7;G"/(--%??d2:˜8q<8-s ob4x<"M8cuuB<kkk oP(X__:r9ZǃNo> Dz޽ˁ�fc{Xcc#VFP(v366ɓ' pt:6-0~$#u7Gsqe~~^쇓d1;;z,//8~8|  bXhjjbyyW_}.z=RIqq1hcǎZ3<<ɓ'룶RIii)b_2|>ﲼ̩S())!??B$477cĶSNQVVFEEx:2m~;w‘9,e;Fl ռ^/ ED"4nj3 IUU2EFFF8z˾Glpp0eZZb8iӦb"`x<d2+Jx"/i[mL_K?+M<g}}ټi‘pOCo`X$mR*DQΞ=KEEZV�d2h4 p˅FL&x<Jn Oė%>c&&& d9{,ܽ{54 xZM,<>N'<qn7* +fpg#u"`jj޸̛@ k!pdkٔ\ Ir( >ܻwjr92==ͧ~dU,poFld2BPp*++~:ܼySl_ٳX,z{{XVz`28<wΝ;nr9OC1::J$ݎlIYPT!^iLLn^;؊/W4\R ^h4`@P1LK/177bAPx(++#133C fhjj"QRR",..p8bv[`C:rdMQQhCo> *I6AJKKQTUU166rZ[[9p�.]@AAp&6nN4x]KY,GPPXXH<gmmBfKe BџD"BVs)*++1suuR怍%%%L8q^|EjjjP(rn7Nbjkk|rX[[СCQZZ l( J 16nQ(SUUplq]AlJM`eemǡox"+i  s(t٪n&Ve;< )Ǔx,!Ovrm4;Us! 6ڊc;Oc;تOそЙ6}MQ7ZJmzl˱Y]5̼ӱC{V3M5V93Oc/j7.W]IpEX݆̳Ƒ OC>66F8 zJeK\uǙ*^L\Z*& Ÿw�kkkkqf>8rJ꾤#1==f||>Cqz1NP=z, D":::s,//377\.P(Daa!h[nH$X^^�/^jD"beFaffiJKKIRp8\0v^O{{;FQr |CCCX,h4L&8B!d2122ŋihh@T_BxQzt:Q(tvvb4aaaBfgg1Lb0wˌp8h4rm� qݬ2;;˯~+N'xv\ir@ @ss3}T իf줮˗/S__\~cx0L"H$µk(..&3==什|)..Q122/~!꭬oVooq>#??V>*[z51ZV4^n:Š 9vկkd2�嬭HaaX299۷Q*Űly7s>5lCR.;;;fe~~^,-,,L4l6SZZhpvdjF^lF&X\VVf,F#( nݺEuu5Nnj5t::::ZSYY\.gyyEp%i0p:8N"022BII jjZɓ'imm //|nݺEii)XVjjjKie oP(D<z"R.vzh4 Bz[FL&n S@�R)t'R\P(L&CR1"OS=X__@0bG ńhe[__>ar([$!녃}QCR1;;K?ַ+ J (K `x\drF>imX��yIDATh{DXTjc~t[:Dž+W0>>@X,YƱLda罾@Vle%P(x}}}"b樂Jn377i=6V`rɡ">W@ss3399tttPTTDGGW^`0011  prmmma~_RYY磽7N(tttju`P3LtwwӃVeppP޽{3>>.V?ebbFCoo/^˗!:;;Ōs-B\Zk׮qmqܸq+Wp18ozd?vn<?^1_v *f[afq;ţr(zb oyy9?YYYajj{+WtbcXujfݻ>"<xWeGpmHc( VWW9p�o8s կ"277Gee%b6tbXt255ngff_~y>̧~ /Kn޼*cccB!ijjbjj.4 CCCz0d2199B<]]]TWWSSSCqq1333`XƍL&\.ouQG=zɽ{ZJ())ZJJJ0LbIJ2GrdiiIL#v $IX^[[ˡCX__|yDш}Ĭ40�PUUEEEW\;v/^:EqqRֆRB!^x|b\.X\]]EףVETWՊ륺ZZZԈ}+U$]N: ʙ2ٰd2Jw/p2Sp_BR~XGhĺwI6$'+I$RMHnpe9r$)bP AG}N'.L"n6o&<vC,^i)s L:e$<[$G|D=#[ߌp<+W`6jhZ, <N+ }{x!/R333 466ghjjW|ވD"<y&&&8q7oĉCuu5^ѣG3gDOoih46dw}H$Vb6<-*V=S<- ¤077/̭[W7pTWWc0X,˗immehh);F__%$~יPw\DQ6_366f˺>[}؛,pd{JPqu9hbpZZZximm%SRR‹/ȍ7I_|E._Lmm-*Y__/唕QSSC}}=JRlojj6fN RS /<P\uraԳ4]z5믿NQQ}™ sYI&} Ko)wCL&SRfnl؎"2巓~)xs)ߣplOQd\+4IIe,tqpvF>V1۴2[/y==v f\g&[ݶc4|ϔipd՚s=6˧˦rz2sd{`S[![Cj82e2OC)d3e/ۚg+g.l?2 U8鉤PH8IG8fZ8Ww7m3J-lRٛnXүD":ȬGzl/q2f#fu۬,OCF |'q�\z>o{DsΉ=ҾA)E2$[U2$y*X,F<TJL$b1qHy7dCI6ַ;wNKK$b1]""$X,,kH ?LLL'|իWeF1::*dzHN/V !vGwC466rMfffl~FGGE {%z{{ 1??l<Hoo/ǏFGG1Lvr9PTVV"mlFh4DX[[CPzCa9x :wƐd155 ?яx<)?$ T*ikkc߾}ܺuӧOVwp\۷.V+wޥp8h4TWWDr׾FKK .\إդU^OAAnu8s G7(\Juzc搗*>?~G}}=%%%,--'255ũSX]]bڊj2;; ,륮N)--pp88q bj(JÇp8LTZg0Lq)r9vR[TrI=J `yy(PSS` LR^^N T*EMM F&&&p/t:dRPP7M^/cccTVV FϓL&q\"'N@.%VWWI4tm ofb78dS---x<Z-LMM177B瞣J=v/D(((ijjbqqk׮q Bb1.\@aa!yyy |4 TWWs]UUUtttpiX[[ڵk200F\~zG_G2::Jaar>|NjjjH$444h4_/J*bii HQQ2Bd2p ijjbffFX}0 |D" .2F#}}}k,..R\\dpp^{>z̆]l w'Sgcri4ZZˉ>L8mYRLB!^o7&:0iRfu>}f,]֕ %zH3vg^{lפ9_W*((>\M BT*֐dh4b4zj|>xF#f?O"//ZZ`0V޽{b6Ŭwȑ#d2ܹs V+p׋FrT* B|>ܹsT*ܹ#v1Kb1^/*@ @4y^axD"nݺE,jLFD"EӑJ~Cq}fff4 ,,,hh4(A;9p(% {ӏcT*VOD %jrM}~WAN%Z[[$ /^$8vD~"j5GD}099I4=u+DžiϛoI*⣏>bzzZaX__믓J[nqu h}k477OQ;mr`0NCTrY󩫫cee_^"п]`+䐟>}VKqqKRWWGYY+++TUUmMxOEEPRjeeep8JfWU~?p*煁Q2Jދx^V+ BDz2<UPP@kk+ EII JӉjE.RxWx<ԈfiiI:yal6qz^{5a)--ETV)//' QUUu_Yl#ھ}aii2pb]&-wwCzSohJ%:tH v?022lf}}:f3(J•d2=%ɘCCCT۷oƙ/r@ww7MMMR)BLLL`4EO*uj*L&H&ܽ{J%<欭zz:t(bee%b1immE0??Jbhh#GP(n�GannFC^^~�^˿u;Ⱥ ҝ + Ҿ\پ3eF6KνYYl|;ǣrDQ(//)ff'xYW7+lck>tttm䙑#$T,)caaǍ p8bFDW.RD"!lPn@0X\\O6]&D&G2|)H V9r嗍#)z<.ş%f](?19t2نò444rav;yyyqyt:FQlC?{,eee8NFFFbGb4zE\.:{a0PT ɘ{=t YZZ"//Id2~%z=^V˻ヒZ& L& \.tee<iH&ܻw|#Gr|3g_|.'͡π L&vWW5ܽ{W$*t: B!J< 466O~Bkk+h4Ν;G"7ҥK$In߾͝;w30N'477ىg߾}LLLo~ǃYV+@>n7DBr|_defggY^^f}}Gr0MMM8N>s LOOK/199Iqq1466fsum3ߌIpȥ8j&A!ɸtSSSTTT0::*ZdCCwRHaaRFF3rQd2R( 4/yFJbrrr8x𠘉)JJC|> xWf3FQ|ϧOcc߿:d 133h1%_[ك$mu};S)///'D0LjZ7T*q8ֲRCᠢߏVE QZXXH0FBAoUUUu_{ɕht:)++qH2deeET*y j_Vc0RZM"@6abؖ|<0e*LL03}6;ۤIsVVE4#SH|乺Tj#pPp8"hxn; `= ϙyaakZ|d޽{d2cqll)Qg/i ̣ +[ݯqs(on$Nqau//HD8q&''H _x5^^^ׇfCPzF#cccńQnr0 •K|>>#;,}}}b8ehhA XXXܹCQQVF[[d2G q555,..C"w̙39wv?*2gCf3pfuu""//ʵk׸q~RIYY7n$ f||r۹w\rE ?Ŝ;wN#ܹsSRRBWWpj5cccqE(Ba>t|笮FY^^d21<<%%%a#XyN't:˹{.LLLbΞ=KMM mmm /dU A#NNT[VFGGFV w:X,Fccp/˱ZD"vJ%*>>2x<.' .͆bpp/}K|R[[dB022B0Rׅ*Z-~YߏDlttt� $ByW*F\ZP(ꢦFfb;iU|x<HҦ1$^I=:E|bM$bxh4|G!p .~W4ܺu Jŷ-aH D@4ET JR8>)m�Ta4b"B l6L&QT=%ۗLtHs UfvOc[1]&I4pMک"5RlcfYبr93W2ߺY=$irȥkmil===R!DvQ1tuu@(;VK*?ݸnDo0>>}+1 uwwY8̲e΄qJ#8r!>gȖG3)i;GՊ$*`0? innj5 ~(Jd444YP($^o2DV՝Vd*J sҍ/i.Y]]eeeP(DKK ZV; 9d=<vekX{5= v{ k]^î`aaWװ+kX{5= bL a;����IENDB`������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2011/08/image-300x225.png�����������������������������������0000664�0000000�0000000�00000124630�14502137606�0023523�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��,������r|�� �IDATxity:7b%H ".*ڒ-ɔ%۲/o99799ɽ~H|xeǒlٖ,R+w4A $�b'�OtLs̙g6�1C ̑2\úu0Lܸqt\.eeetvvRSShdΝ,_AСCx<OC>C|_ʕ+$%%cǰl|waǎqZ>c,Z ]FUUǏg͚51776& F(D}}=F�áH`0PYYƍvu~:Ns`0 LNNv"99ӳD q8ENN)))LOOcZq:c0>kQcQYYIRRL&Yx1ׯ�HYY>,.]"+++Wt::G#~rss%++"V+@"n7;w$ �p\p:::hmmehh35N |>߼oAߏhdxxݎh,Ō�n"''Brr2~˦M0LLLp-֯_lҥK 155RRR n7�&P(hDDG5;;;\ZZIIItuu}*᳇b ` dp80LMM}ƒYd2p8p\$%%199dBq\3x:Fb!c!1C 1<49bAaC  b+bxhsX1CÊ!V 1 b!1C 1<49bAaC  b+bxhsX1CÊ!V 1 b!1C 1<49bAaC  b+bxhsX1CÊ!V 1'S(*}쓖%bxDBCɈt9%X*bA F| n_ÜkAE'hfO d!?+.Ϲ&h>0"ށ=>$bXA}>wX>,w(*^ABBg걄P }]䕭!AFX )ᝎk8Ss1s3A�Aw>`>͌c4DkGbc>90#f ztgcISBA|D|>߅9u >Os 6'9|sk]c<)a  b�=>ܜ|< T\l>hb53CBb:I >} N`ǖr.]饤s7}nҰ츦G&x$$f`s�ܕ�AQ09 Jy(<ӐBu4=SCʥs醘H%.>(Il$f&'ឝfO 2;5;ݑ=`avzJbjF SXl81mZKO' t-V-$jSVB! 0{5/<vԜFk?yy^BEќȞ]-|~jS7,K$'+c[8 |xk+(..\} 篌c^ :1̇ 'XRlj،|U,ϢJ+O_\FRnfۯ]Fkmޯ&!wl4tpL'tm$Y3ߔ˶ǟbhdcha0>;#z nO�$S$15㙗gãyᙍl6YZ^s_p{yl".](D|%jyruRyU7u$ל<$,  pK~f5+›W7P\GgyLz0PU4|I+'onc936fqQ:Eyכ'yf*_.~~幼}wCdfմsu\1ၮaͺ}LNa6O =p/]--A#x'w>&f `4#BSӻa}O?'1{V\s n9-bB0$oR6 2l?rn<%c6)/� WfcY3%';o&>:7@yA"v_6‹_A5}C`4j1<u"gBP(O0]TJwMx֬^͟}u'ccO8qukGJ*:LBKuc)& O`0``0S>+26>{.^"; f W讜1, BЇw`@0&L!ޙAJ22 30^Ùt9#4  t gt,6?@7ILNWO?ÉnHB7vͺ.ʥk'yA~)sR, L061??̝K ?p{\Ry B nsjF&(;\G3޾[f `xK#sZ rعyrn09<g ,-/r><`e�A0 >n[6m]>g 8}gv.ŧVHW^qσ˝H0d8P{X/c~ F?a L^|T/wqdpBMs8#_]Hrbx=FcT_l6emi 4F?&y382K6)r7eyO03eӚBr3r7.˲X4GmC7S3*|q6^;Z&O#=t]m|֚H[~^x#u{Jͽ PHh{0_ѓ$㛻w   ħ,LI-@mkOZ4ڦRy-jeE z~ײV5$<r1[/3{o B(4̬@f^>#}LOϡs h4`I^ĬީBE[N)<RTEK{!#*юVh%yb?%Խ.'D`m${Q�ؔO1좻p7’# 12Dz?&bxX@5X} C 1Ġk4r&t:s%P 11.U\O (Kz(TVo)u4#גY:RBO5[k ~,QUKCԞ՞"w$R'UViD/ͫ_NG_Z" 5𽴌J(ѕ9gJ: -Zzk#ZшTJb҇>MpXZ=JkE,J''-Y"EjjP*5HRT$T^z(e'ZN^I6TW{%>jA[~l+-D*ҳrzjVД>jhT)j8rr'Tr^ZVK5t%\_9oTpQ/"C]SjҼZmOǣf+/jYϵҽz"E)z\zeT7?phEIo(C)jPSOO)Gzm%}M 7K{|T#[44FH|FHe=ZDcѦԢhjIF=:ս/4uS u:Z>5ZjWEɥ4Bc5Y4xF t5uNrmeh:]$窦If%ȧGdPkԺWkJi1[E7HF5%2zAf3~B0>ZX,Ր3ZG wZ45*TzBn>JirZz%P*(]RsZzkՁZݪɧGVDʧDK9#IN5^@ÒTWWŲehlldŊp8+VƍYnSrLyt"ut59fgR~Jt-wjtꡫJy&rɭF'TmQ j2زe 6lfb l6k׮h4b4l6***튊!RH)Go&R%njlKKN%h+<Zy5ZJQrzigZvVizdJQM ?!!ALC3i)U(5R*H), z)U ۵hi)@<z#V5ZS(=ʤ5->Bj+z=HB#C:HDцR^{G}2`gJQ<ҵZ(&Z1KFt6EiPM^Qk#O{TmQaɅКȟM_"L$":Z2($VȮ)WךWGmW zV+ֶj3έ6�a-%[ł6u%aL_"=TFG.$L;ҒSM5=#@zAJ4Èd[J;9hkѪGV{J(* (RE jZ5>y9-tKCZJ_-]/1h)5yhh&ډGntQQDșɧ?r<SvSj夎Vͨ52w$ݵdU&E-O9?AA-2ˮd+H,o?JrJuA:Ktd(IRGVza)5^ήԁNDk4R-%MT^rLZDUn$'\O#Sj#.3*щ%'>-jPr,JϴFtj<#92=Ƒ%WIz3K-;)5ԩ^9o4v\dVj|䖦J/)jL^+ xkɭKxDr<B4)L Z\|jiz;^]"M}ڿ^< }86W*'ϣ[K>i痗ъNheFKnjU*S75D3M-O4J#FjJt\.<JBkXkh։y iW ]IyhQi} #SJzUSZ&+ %zZὖځl5PIˎjEV@o ҋ8VjrANDZ"WSEPJQo8ת(SjW5rP]JMKVRٔ)顧ڌV[`U[6Aژh4t:IMM%55Uq6륻"l6ohh͆!++ x )ݑHLLԔUFӌh"fbHtyūMMzdddh򚙙v >4EY0L( 558<*e8Nv;PAN'qqqzՏv399yO# a4#Zw|Z鑮NBhMжҋ 'N+WGqʕ{"%gv  ς … |~Ų۷o366G' qyWXRi*ӧ#柘WsDnnnF՞uuu\vMy0 .N�8}4.\wߥ@l+ssse𧿿Ck? ofҕUN}衳POVzEwSO=Enn.~^zr ]ݻ),,رc Oxb Of``N8`۶mxp\ sQl6e˖qYÕ+W0L$$$~fffZ<ގdϞ=$''x8p�###ٳ#Gf͚5TWWdΜ9jR.]DUUݤrN';v젡)VZ|_$++ AhjjEnn.waϞ=@uu5ڵ:vիWʢٳggŢ-wMyy� vy룦2L&SSSTWWn:z{{X,Q[[K?O<W\̙3sNhiiܹ͛SP(D]]LNNRPP@ X,AL&~iA렷;vl2 tuu۹y&CCCTTTpE�wnsAq=z bttJǼ&-HJQ<<==yeֈ,s-Wm&E/}UV#n4-[pu|>k֬ɓq99t;wl6R, 555A8q̰o>RSSihhv9p�{ɓ8.^ݻoˆ HJJw} �gΜ n޼Irr27ofhh={pA8x v]z%&&rYV+fff477~z�9񴵵j*뉋#>>'OG}}=6cǎ~z蠳z1ѡ<y͛7c6inn\v sǏ^`||9\.MMMX˗/3<<LMM vD~mVZűc0SOqYn݊?~>RSSq8tttj*F#022Bnn.iii455ֆj̙3,_KII ~_ ŋ,ZAL&EEE|tvvKbb"LLLׯ_;vHLLrAHMMl6:HJL:ֲdVrPJzɟ}ڶz*|~pR+**bnnN\JNNfɒ%bq?`qfQTTD]]O<cFA 332ӹuVyk6III<tuuq5fffDPF||<649{,\p|p\L&<,[Q!??drrrp\\|˅l& j*<HRRdBAJjj*eeenBɔ$�yyya0 B!�###|>~?v!.. <)))[ 󋏏<ϼ6`ٰZ]|n7`d6mD?AL^^))) ªU$%%%Qgttߏ#77ŋ(ʒjUl6-OS"h9HH:\^F#%dziJ/`Vd 9, l޼͕+Wp:gIIIawUUU8Nq:__W׿k ٹsة'55Hrr2˖-QYΟ?/.P/^>|M6j%77F-[FFF[n% QRR tww####$''c6ٳgoߦq711xv; ~?===L&5kֈl6JJJxq\|y^`HJJ"99łn'//ŋGbXHKKgtttoƭ[(((`Æ \xq6l؀>�� �IDAT?fyAGVVMMM'ʗŋ\y?n~_;eҰX,l޼*n믿ξ}p:[oqEY~=?ٷovMkk+~!گ±Gޙ"K -ZNKn4%J&>)[^=Qj0 F|>V)<q!`_RSS FB&q 萼^/6M l6|>nÇFCCMjj* k8 L&~H/`X|/d. ݎ X,V+ 0>> я@;A戋;??9 xn сA1R $\/3338f933nj yXq),ʼnudBh!۹91ZZ  55Պ'? vD�RAbhdzz8OMMa۱͉2Xֈx)Bh(/'a_W|ߍ̛(|E>%P p}}},_KsR蠄hLd߇B!.]DZZF)O-u֢-FZ@+]3 ruJJJHNN\)jdPUedVkjO>\4)QJZkҽZeIQs*jQf\հZH'OWh4n:Mj"TNSZvUhJ+ɬd2z{FJN3v y_'IvJPj)J/yC(Jeeo߾b!99͆_b!5 .\vc2HNN&%%իWӤmTՊJ|>.\F5iȯn7fY& HK,&VZ%NW0ŋ#dzzOaa! ejjE[I5âESk/rzJu #zTAfXO-ZrK{m >/P($ת`P\+ |>&''yW>333B!fǏgxx# i1,Fv � Btuuqč<a٥v+Bjp  7 `nnRGP(Ŀ244/E.G333[L& q;(@@ @(u/ە'o(`0xxc% OR4,)ErR))AVN[+ʓ,lQO ?;vׯrdll/| H^^< \v,þ}|8,YB||<=|ŌrYl6uuu466244ĭ[(**b޽tvM]]6l`߿L&^z%LNNk.֮]K(ĉ4553<[o,Vb||nN>MKK 7rQnJ__?)6oL0Jd<s_Ooo/Ν;줻Iv˙3g0ͼKttt`28y$`%KPYYɡCݻ8<ׯ'F/'Np-JJJػw/vׯs-ؿ?.Jf(&''9z(\@ @QQϟZF.^ ###|k_cddc4y')tBCC,^kvٳg&ǏzYz5=zP(޽{y&dee|DV)P0:|ڦ&CJSjtԦ"zQQF͛7s ַuuu455?mt:yg蠷<ܾ}P(իyꩧ ''fMkkkfJss3tuuQZZ*/&&&+WZ|[ѣvZæM0TWWs-N>˗q88NV+ՊɓpYn߾`^Lٴi۶m=nbٲeTUUʕ+YjbZZZhjj  ׾FCC]]]P\\,&''˗/}#GxbN: |r*** ..o|:uQѶ--- L8{Y8w|>hkkcլZ3gPWWW,09s ---|{cѢELLLƾ}b/ryAGaa!7nN4yG+rhMhK!j9F9m5gq}ҋ7ϢEHMM%//17LިիWh"?輿HX,fff) " *ANNCϮ]w uϧ^f3PD1J\ZVZEss3iiituuqM?ܖ`Z#33+V(Vn7>B("%%xHIIzx<; :ެi4t蠱Qh$ )evvFzzו0.a~L&ݽxV5kp 5fgg.+!^L^^+Vl6Dq܍7v&4-[&ɣ=9`ҼJGGtђIKgrgDWIOVz9_sŠ%IIIl6ācϞ=\| ٳg;wŞ={QXXHZZPǃb!..@ Ca9rD[O(..wLtB8q@ @ee%%%%�D^^稭M6QTt}/ŋ< m6<[l!%%ׯ1999.~^Jnn.k׮7^P]]MVV+Wwߥ{PPP nȠ$zzzMIII`X(..fpp>#l6֭#;;;wd+WdddJ|>W\Sbhh-[PSSٳgٸq# 466RSSCUU֭m۶a2Xt)nP($nl=y$,]r ,ZlAĉuV`Æ @(NSOILLŋ86oL\\=ъR^нrzePӶՕ+Wt5�a};QK!wj͆ݎpwkdd7|~$$$Q(Ay~χ U|n92k GJG ;Dg0#p aghLMǃdUS~\>9lS|`0|h{R^^8JQA+n[\iHF# i#+oog49M%9<CI5hkӶ믿`a95yd2[$7L8N݋re`0"U\6pY N-CZ7y5 xb4ERۅ]4Up /r"Sh$>>^wrZpw;390L&O<mmm,_{) da&m+R(9yj-DKns9-5GǑIJPrV⁝.Wt{JQQbY% }%9-讖GO齼ɠ-#kjjZ2HECꩫ?u[EBT(f&�ci  Gkk+x\~ƒ%K;{,dee#ڨ&5JɝJZhU{&wUIw=rESk<j}htUQOR5zhHѐl`YA!|B!Ynܸngҥ�@ @BBFL&qqqlf0<}t\ ‚ ގd"++k^l.]t))L&qӣjפFy(+ͧF[~墹?rz|tУ(u@ՠwj>qǎcnn'|+W066$_}67oޤ/~*2228v6*n޼dիWsI eee$%%q9�/݅SNq )**?$cW^yYEEwvYbG8b)!Lm}CϨVCлՐe"9HetR <e$ra<҃9͛444zٽ{7466??r 0==͆ (--eIjkkY|9+W466w:::O~BEE333 _vO>~._,w ]]]P(իWcJ\.{ 7.yDL9ZiR!#R3biJv/#)ʉ6Rl :q4 RSS%;;ǃl&!!A`b```^y,>n-ZdbzzP(Dzz:ӴBJJ )))撗7QINN&33$rrr(ؖx\.&С!ɡq罚a~Њv\4N~8-Ϙd2eΞ=Knn.PHp >},***DUTTdn߾B>CV+_׋{t.\mۨvVX!\bϟŋ<s8N{=ygΦP(đ#G^zL&ybA@kkthFBdGcH%6RHxTmQiEm,l4ĿH#&rፒaa)W:,O p_N"uR%(unL EDSђ!R DCSMv%J鵓Z}u^h}#k$ٕhb+E}IxH y _Ly大f^nа ;BzTRZk0]k99mȤO RҴTF$<r,W>rj6Kihю&J bAG1V*E+< -P[VЎnQψM;ӈ\$l zi+ɭWƘ˨iv?oOj~zh@oġL/9-^zic.BBhhhRZ|NMoTOJK*+Q'RMoZr᧕_M|JSSX\Z!9m=Q;v(J/:O.\V.|DOzVgJN_H&WmТ!ͧSoW+9%z)ɮGQ I#MI" X2K)]Rj.RвVg}m :OCQNOi95jiOȝ3p:VOj7lRjdWrrZZ:k -9#9Fv5@*+-[Ii(*:D8R)OKKkF$ZzGh#^zxFKKܑXҎwmIByE7xF*l+-,LpR><HZ+zD_HDGNl'o?RS6&\{"RhIEjDc +(.L +͇KӕKe^kEP|gJZNK`tW{.r5Z˦J/4E%CsVUF\z?SGVzUe9ykh!Ҩ!wHzɢheTxFH-~Z:)AjG=ὒ j_i0Ph˿Q\I%䶉FV5aSSSܼy;w/RDXPy/IrϴV n[|Ij=|^/bv2|>ς r|t K_*})iGGjiEEOOSSS I BLNNs,sss QszI~K#yZPGW.V$$|dChInGVZ֜yHww7'N 55|݌PPP0 ..}-Z[[)((�*} @ssRRIII]j|>N9sl~??ϨgѢEܸq455l23go@aa!e˖zٿ?qqq8N<Ȼeee�?!W^cǎdiD"0 ?),,$11K}}=YYYl6ιsDOOyyytd+5?FDcO'AӶիWu5gA5B!>#222xiooe˖qF#6lیXr%<c.FU\LLL8ׯ_gzz hmmeڵl6&&&hoogݺu244իܹs FHKK _ꯂ;db >/"nɓ n7IMM 'O^z{ׯoAii);w˿^w ``ff~ﴴ  fܹCSS^Q]FII ܼyP($4188`˴4 ͑---LLLj*`ʕ=(#҈6kj9-襡E;ZQIɖ"cA@*++,_~jw===qQ9Bgg'什_B!>LCCW^`0_ �III$&&)ytt@ @GGgΜ!33233immfk֬駟|1"*))ahhvVXAII cccfݷռ˘f&&&HJJ'~ܹ۷Ƞ8i^jOWWټXVΝ;ywHMMe߾}\pvkQ__/KAP|棏>7oxq\a2DN׷<Mo_iڤ6 # -YI5J<ȧFOVNwA*/P>;F[[ׯgڵ\.q///gxxK (..fŬ]@ [ϧ�6nȆ $$$&vI�ˣ 6p8ij{G ;N`0'))D{N;wv dddkB|l۶7O0,p8 *+Nİf3eeeTVV(cccl޼r:::bӦM344E|iYY]I>Zq-xDZZf6l뙙!55)l!_{^+=S+o7jkj<>JRM>-Z)P~rjc҇n2سg*{ny1 lذZyf3˖-cͼvx7ޜ>}[n122Š+7;l68wnݎRZZJ]]ZL<yϳb ^/+WF6oތh7  ci&N<^#33gϲi&RSS ;vl84�� �IDAT�_^Q\\ !w_C%KXx1LLLs7/~˗GAA}}}|>DIG"%%2, K,… ܸql6шj%))+WvURڇRz ]ҢG>-VJS.ߣ`+=0 0<<�Nyg۷Gnn.?HKKrnK.!9qqqv&''q8 dbnn_W<s)Pvv6Νb}vn$6>> ő۷ bӃhdѢE|>pӱ/ w_&>ljj8l6ccc bԩS<SۉILLb033#ڴ9~8;v!>>;wMjj*n$GGGiOMMq5 W^1 155`tty牅YΠ4Fz.іUG\|zGK?Z 3 CIh'ORYYIZZ@ .�Uķ[e˖EX, A|ҥKŷJ;jro~__m2֥6-Zyr$qlݺ2EJz)SR^}NPV9iY:XLjziыd;|; SIÝ0<Q0'Zσ' аa]L/T0 At5>md9ViM~hI>F^//^ԩS455ݳX.Pz۷mUANƍB!<]]] YHF ]hN  l6ZyBۜ|ZhhdkCo|l :K|566o~IHH'N/ctt_vA>L__E;ւ >|fdd1GMM A`nnzKvx?�:(ɠW^nJv GGapp0"M< ֔V3Ҕ+ip8Ok=Q,L0C8---L&Yt)###tvvRUUի9z(yfg۶mv(--evvbf144 W^딖p88x W^6o$nw^fgg裏橧y'- O{kr1Xx1$$$PRRB0tVhY)))+V0;;ѣb{ff&DoN{{; ,]Çc:::s;wdŊkhhh c8r###<XVh=555LMMhnS[[KQQ9}4mmmk.#ҤG~)S[Q޶_M.5$rE[ł_zY^}Uߏ麟al6---<|8qlɓ'裏'##_\|..]0ΒH('&&(((СCl6***/}KLOO3;;ҥK),,=z'x{@OOuuu_O:ŗ%pNYlݺQ 򗿤FtF---LMM|yR~?N~~>\z|IN> @;vڊngɒ%8)((`zzZnܸ}}}\tT~BSS _;w8|0O&''GΉ'W^!??Ǐsqٳg=URD&ڙҨ&֊ӑPJ4% \v;wPTTDEE \P(Drr2999$$$0<<j%;;6 ɲe˰X,Xt;ฎ3`� fp  H$AH(Ѣ(kDzXJ$&MUI*I%ReW֎cW-(oRH E' z JNVf_wu_o~~4^/SSS8N +rL&h&fggS__Oqq1Cii)a"b4x<n NlF#8N\."hhhI:;;sQZZJ4eddUfgg܌hdvvx<.PZZʺulaL&vzĆXHss3htٰa޽1n޼,YYYPTTf={p9ƨcZijj"bZ)))!''X,+oGZC(SfHՙԌZgSz6JjUAi_] t:&�|>VO3>>O~n7?8:+W6 ={ddd`p:[N,<b6zt:233୷aȠF\tSSS>8z(zlO(,,$;;&~se믿Ο韒"F8pp8Cb4ͥbbbBu@~~>deeɕ+WX\\@TK޽DQ\Bnn.P+Wz)--_֭[Q^^NAAiiip8شiٳO>vI0䭷 5#oj^2| ɿi =V9;Ys5_V] )okoUZP($�J:=vY.\@VV}}}/F~rrr.iCe$8Zthh4hБHp8Lff&x\1<"MB!qlfqqlf3H^ϩStܺugy&M>jHGp`͞($PI"n?Pߋyp D"vU <Ч`H7VHGzWEI:t#]ݓIZZHdźrڡ1զ�ijlQJTVk-h|u궆d$a- rhii<rQUW=66FGG[jnTst%G"nݺ(7o!:բL65LTtO O𭦻d:UM*%jqcUGt3l'P$YTx,y՞ɿ[iMn2hiiEd#d"YSD8ʥrzZqFN-\jpcU**8e%F-fM3J%}-ռ^ Z#{*eZiɞZMZ> DNT`M\S-wPTMŅMirUl y2L襪dxo:T{_YV?QH8Rmj<AWuDb5Pv>5J&z+Nf $C&Y;%lzVLI;Qc¯RVSCv%XxY(yPÚ6j1Sk)AW^Zih+?m5>Q%jMTx2jRi| <ZL_v"HcJ+G~Z^3ҡV]ʔKeU0b5;ZR3Z̈hMEAkɣ42 R/1~ʣj;Zݪ5F-Gd^ |zV|u ||.j'^"eK4OeA''/ŷVcJƋ,J|Zr jްY"FW!tǗTVR]Úݥ4rڑr KVVC*+9<Ɍ`"w[w"K&2\jhɕ̳NG\NWVʪ&jdC/`Mr!RV"kV9Z<V3&dM5T%(ZRL2% go%^%?j|ikx^ƣl?*uX>,yGb"t,GʋǗ(*)+juN \a:C*AA]QʠeSyJҭJ%2uՁG›%lcq-JDp5t>O]}^Wki`USB5u}V³>+( <x Ky'''YXX`ǎl6U~)))O?eϞ=⌟O)ahh}8 O{nTiy]jrZtd>s [l!??@ sxꩧX,׭[>tGw2޽K__gF@빖/_xMbxx46mPDzOƓ'|2ZQ*Q%>t?44l?СCYXX  r>ݻWDr39~8YYY<ovv6gϞINN^,~?XGaa!x\\bۉD"~B999ft:hy(999a~?p\z=.>yt8&@ "OR<^p8! \<DӱZ֊@ h0pdgg):gqqQ.y^^/ׯ_|B7n`呑z bZXX(@@g6x<dee9y$֭#0;;+dzaBD"f3@@nK%$z:p8Lzz:`<?]^^vt]=&SRk=NsVڔ*w w"ZZy*UXYBiz |v>C8;vh4r5(,,ŋlٲ/)))a``�$< HKKc>};v룣P(ÇZ!uV^z%z=>|`0͛appp8Ν;tvvrkk+`zN<Ɇ hmmE׳~q-K47ޠ@ ^|J^^3L&ۘL&|ID׳w^yW裏W XVFGG1<䓜;wH$`?㌍vyyw(,,djj_Çc4ux&&&xt<ܼyR.^(y嗅zt:)((`޽?$ebxxX,Ƴ>|njr}biii؈^q^{5h]]]tuua0b 0??�8NZ[[wׯթM+ hЩ-ɌjJV.G>11AWWXVN<NN.^ȫwX,>&�?<EEE,..bxWػw/~).h4nSWWG]]333ܸq{~k׮vy׾&:D"ڵ˗yؿ }>P1"RO>`0ؘXK~z^y<x@8&+]l6@x hkkr\~ }{|{cqqp\<x@ԑbf144$ +BYYׯ_' jk7P[[믿>(mmm~o׾5ܹÙ3gq%ꨯΝ;|ߧ7oz7AZZvbϞ=TTTK/144,_p8jeeetwws]|>~)x<6n֭[1<|dddD۱VIe帵C^w]W?VCSE*UH`I?;vW^[E~~fHM2>j 8w FvX0| Myy9&7o-w\�l6E"E˯\v9qEp<=ISw߿eff KKK|>n,'//| {HoRPSyy^Ǘgtww3<<LMM h;weffGqq1nҥK<qNcii3gΐj񐑑AZZ&IL5o߾ ~� R\\,j_.JQrrr())!???'xbh4bXb``J16vfU>߿͛7EQEEEPWW,677N#??_l3oe}RN빲N-^Z<&­U20�1Y??b+F@aa!Lzz:{e||Wqܹ O~zٰabTZZ4iii<s MMMbm۶q}\./fDMOOOqq1֭NNN[lƍl6v!nl`XhnnNN'TL;Ŵ###'ij00 tvv}ի3`Zͨ6mpPUUEYYl6#S[lV7L̰a1NMM LCCf^OFFdeeSOCee%EEE8222hhh`nnNК˱lx<0dr]`޽v***HOO\asrr޽{ձyfN<ICC0_^\]]WWGVV6lvSWWGqq1dggSZZh"CE$hyʩZ%>9mК!tUGGGGRM4_<ekqkn5:\_SHtƯm ě<i.yqr])yFZd˷5nN4ANՂ*;$[4e``'ձZR2*o4R1QT G2jZmKW8계JG)*;ֈ!ǯ VM9HW'0j2Ӕ[u6ѓu$O%6o|e2ӵ:$hJɓReD֞4:<Zժ7-9Rɡ~ԺJ^RkJcG-Ex|-4Jy4pCpXЕ%LBk[L 5hɦlti}.x@ Z NBOB+*e@ICB9x)#~+VrښM!J ZC^" jsj_J5'%Wˣ-n~~Z[[E)֨/x|y*ri:;;pٳgEdc-K?~)ӪE-=5)q&2>s̙3LOO4+ ىh'LLLO<ࠪ|hw}Qϟg~~_AUx<>S <s%/ԣD~է*)ʫt V}Eq]Ҩe~~ylll x^z=f˿oDH%(**iN'_Iee%hQ閧E`7255ˋ ՅnWx(++b0::K('N`6Eꡡ!&&&n322:b 6%�� �IDAT3==Myy9YYYLMMlEqNG$xSPP@YYDQF#@@444PVVF__yyD]]{199А0fa|>"h$==aN'YYYB! "//Lk)%~տׯc۩gjj1YXXWzۿ[x ݻG[[67 %%%F;]^ MfVC_Y&/]%U%&''ʕ+ܻwOln?)&/vexx;vڊN̉'B;w^ϱcضmoO<z-<}ttt9}4|$77w}NիWҥKb-exxh4ѣGX,ܽ{^իWazz X'N�pi ?O<f;ᅬ˘L&~ӟRTTѣGq\?^l\\\Fvv6o& 3??Ϲs[… Ӄ`ԩSEuu5L&;ƃd~~ׯr駟I"Ο?O0Ŝ:uh4ʹs9sɻ�`0'PSSᅬ̙3XV{=z=mmm<XV?~ȦM8uꔐ۷oOr}7o… vN>W~'NPWW'|_D\KO䥭&exz[UG~Ǘsϱ~z{{1L۷�6l~fիqV+b`l޼n7uKKKKdggSVV&~lܸ;wRWWG{{;Kdffݍh䥗^bÆ b-Kbddd/ /`QJKK^n祗^f1==ͺu8p�nv#IkwCCCB!ٸq#]]]ddde233ٷo|"_nnfo>%bƍڵf&''PVVFuu5c@pw3 /+MkjjWre�x)--`09u֭' , dӦMLLL022Bff&VnJKK?c-HN2^z%N'"8+Sqq1K/QUUEgg'bW0ZWZdQ[7ZZWšְF#%%%:uÇ}ҙ=NGyy9CCC<P\\bAc4YĴ4 VOWW[l! Vb`2q^/G؈>Yhzr8tG׋baqq^h9z(󔗗 RPӏ?XMMMq}233 øn, YYYTUU8&Ǐի444PQQ!^N撞.J:0LB.IB!^H$ᠶ3gp U޽ˡCD"l6, v`0ȑ#G:>oߎ`E>z{{q88MZZ rafggWRHOOmbvvn222F"@̌(F?fٌ`q'++Kȗl]&YdkTj Zz"<ZZd}ԺJt>,vqqׯc4ٶm v8ѨQaff͆^',qIE Ν;i&߿σͥ1Tvj0 LOO322Bmm-UUUܿ_.))v`[ n߾n'_I=99INN2<<)))ann)6\;w}kPoGG\~||۷oSRR"ι\.<35wRZZJ]]TTT022BQQ X,0h+v1>>Nyy9,..uV, mmmFN't˻?yjjj" RXX�TTTP__f|| 6011jejj! frr~֯_OUUofaa֯_/;i0tTVV2>>Nvv6, bp�7kA LMMaZJVl;Ie<Ọ^^$Z+tG~z噈Ip*+h5$+ra\.%ĭ_Ipq<(nHkyVG5RC9T _2JJ֒=?t{h#8 j1eQ'+̗Y2|5<L& co5׾hzYP]]Meeei@"Zk5Ue*Tkw_+-^R^/RWšHBDVXbtz+6T:NrIE|>C] >X} ׹y"Ajӕ@|a;aѶ6:$巇Xb<_`vvV3B;FWWܺu/䤦jyIzz#G<Q7`)rJ4@n5ihl`Mq (.\ 1;;˗l0??caaY.\@VV[nettjn޼?..`6I7g>䓴111ng޽f .\`ff'|h4˗)))a֭ܽ{WZ߷oܹs7o~z222x"ɟ &NnݺEmm-7nO>aii Ξ={r ڵKB255c=h\F#oߦm۶ⒿL|>ݻW299Ν;СC4773??O}}=)UZH(yb8hɵB2<'#IDZÒn >j233ѣG+x<Α#Gր{ӧO~3]ӧikkwyɉ'O>-}6tttByʢVyY\\d~~3gΈkܹC[[Ǐ?V}]8~8o棏>"==x<FGG ;s) 7Nt:n7o6v~ Μ9Ç9y$~X,N'6MՒ]dy%}*nԞi'z mgITN˪T`UV ضm|>GB}}=:MW\aΝ;444-_gt:ٲe yyyܺukK333eJKKl޼׳D^^MMMTVV2::^g"8F,#;;ltz=7oKii)7owر70::*.xǰZ,..b6^qBqq1---Dvv6wb333jafCkJly#ĥ̟(MI/Q>5:jɺd,RK AWaMu:&,O~͛d2aZinno~͛ٶm8Dcc#fHFF:tjkk?)###oݺW ?9{,---ddd 슋IKK㭷ޢDzz:v~BEEEb1~1<<ƍCgg[&`Ú ^tN5+zՊ^gٱcH233a~~>>nȤVjiFDoo򆛌H-O2\@˃P3$jr|u*`mIt:L&qhrt:xy0t)= qYV+ׯ_Dp[;0>>NVVCl0 BtnlXVB,,,Ձ@iY >f,MMM] .--155 ''Xu+χba6q\nN'\.222D"dffڈ5JKϤQZH+Yz"Z@jdyEOXt7*w'bF,#/ 8s sss455]Jrkm�SNOn"rү,Rd8Rir8(eגA(yLTt%dyϾ /~oJf5Ք-/#P9ju*9?JujLxQZ _j<SNT6yڄ}w*Zm'6VGWˬTa;%뼫xQ%j4 Mj]-(uAId謦Ajӱ̺J0<ASIQJV~-k2|TyLf*z_v]ao %OΌ;Q9Gd8临ʪuN|Jyp&+щp*hh]iɬųo-ʪ񨕖*o(YdAWU_ְF@KT`5QyY;/-y:aj-}\ID "]i5D2Tr]EkPTiTڬ2]<N<h%o_f] 9jVDjH>UJ\<6rJlήUyɟ*yQ'ϧVqiA65%QgIpkOePLƕ@^*X%R~kuLhn2޴N;IeWkTJp"*٨HTN ڛyZoBP${&k2ݫЂ?*5X="uoFwjKdhS)|Z#<dH$~RգG?Q]&W*8ƭGS+ikuDkC2a5^]WrXՔPNdU:~"i U©4jޣr$Jf,xRKqApRK)VWz YzQkcJ-[ɧj)ӕrjut%/Ra:pZ%$§f$,FIh0X-DSUf-RLWZQW5HKKKܿ_5|,?ۿ[~߮֩@ ˗w=[XX~:" .@ kzI Q WVHH0::*n}*ZB7ƪX\\%D"ܸqCsTt&pҥ.zԢ˺bhh(u4p=\m']+GADEx<,W߭'}ׯ"  zje<ϟ'xw^P(D{{;.p@ @$!--ׯ344# bft: hTDD"b1~% E B!;&¤"'_>)) D`0( q\j/|%|>@`ŠDlrB!񸈴DD߂x|N$ߧ!iiiB6'Ii4gΜv ]e |D"ܹCOO>h4*FE[bۓrz)2_|*y׊Cz._Taա%F1NQQ3==~;innfӦM%r ;;sΉV~vvMMM|>n7Ogg}Vy&6x<NNNoB[rΜ9C,&Μ9C0_:qΟ?ŋl<tvvRTT$E{{;gϞ%oSN111Aqq1۷o޽{⋼lذG@$appO?ͱcx< /q7VTTp1233y9sy9wF^}Uˉ8qk׮k.fff8x .]!ܹCff&EEE199Iyy9 ++rܹh_ƍnbmmmb1^~e:::ٳ}z=###tttKWW?t{PYY+lfqqv؈… \~{ ɡCHKK~8@OOhqFGG)**W_%33s5^C7jAjs_gU2XՔPկ+|y|Ü>}'OMkk+TVVreN<Iss3fff'//Acc#---D"q0}/s\.~?ܹanܸnOOOjk188nJq+륭^{:n޼IKK ---zPHt\oΝ;LLLDa~~h4JKK ٟ{LLLPUU˗XwoerAy:ę3gxWعs'~t /\SSS444c`hhp8KKKLNNbX(..JKKtDQ&''ٿ?ǎuuuuVZZZHKK1nݺ0dffqslUUדǛo̙3QWWGkk+n[xyyydeeqYb=wimmԩS۷pez=W\H$…  -"kRW#Jʏ<R.+Ӕ<qh}Uj ,..H߸q#wx7q\횥lذՊgiiiźЦM(//b())wPIӔV+ITh4 ';;tez]v177իWWzx<8*ncz{{E(xӼ<n F>//,ˡڝN'֭EFΊu=χ墸ٌQ󩩩Xj6),,$33?FKp\޳f#??LUUU8Nt: |“ B\^^Ѹ+((fU(..ADa%Cdddeii\vͩSZqƍ"Z;VTh(y@-Io9_rE*0�1Y??!9555ܽ{`0(:lÆ 3<8cl۶ .088Hcc#UUU8FGG粒YNbFG?/"eeel޼bFGGq:ر{޽{# QXX1<a20 9rh4ʁ㔗cZtrY9p�KKK<yZz)}6ׯ �8qzyǩDaz8v|ߤGm6lBkk+:'| �~?C\p8DK.QTTDss3~zq(**nhmm`0|$"Kp8X,FYY>:lnnNGnn.uuux^vA M6Q__/ŽDl133í[طo͜<y<Y\\dǎlڴ O[[555477c0vl|U^ŨNykPRGE?||j*O,ȑ#W8$9D)IA+y|,#c?KŤ)$$O8F׋,B!F jϥ?p �� �IDAT4t:XIF"]Z & Q///L ոzS xWӹ\f[D"“c.TC9_=@xAӔ^H8W; Qe*=Q\ /k n-9yg1 t:\Gӭnsap` (O*jzikuWz5}ITΣ2rZrޕ<#F_9Bʍ\FGK7z!JJ%ɴR65XZzT_R+$_[KD|Ѻê5(]DL+B^Fz='myH4WVҐp;R2ʲ6Hyʹ|&/'7�Z7i e%sӓ5*|DK֡&\׫KG 6֔tVV RJ9訕KDtUKWZ]%ϴ螨ѫr(x8~xD]?mԪT; Coo/W\룫KU._'|⊆E8~Ho<<gϞ](4`P(ѣG{.of``@l'^VhɟHJoܸ{D<+6K.qibHx<9rv:;;¡ |+-ٔD:~L-YɫݥNvecvvVtH$^rLz=~?'Nh4J0//nDevvVD"ѣGijj"++ pӘL&QRX??O(((޽{E_i211A4tܹXVT_Y|ͭ/زex 񐑑<SSSb*r!>Ӝ<yF#l8򘙙ann4bj.mk' 299bܜ\ʕ+L&7attbP^Ww(?1[l!''qÇc6x< I:b,..b0x<z222XXXtK ,--E}۰`Q'J?忕ϵp$WD|+ӒW9 QKLi}w4ܹsx<γ>ӧX,~oÇټy3~`yӧl6SVVFNN333~ػw/>}055LNNq^}U:$^W1>>N{{;9wɩSHOO'==+֮r~۶mn`.E0OnF#;vh4o[qdcff ,Ok?N?h}q ڵ#GPUU/"&;;1zzzcbb!&&&x9z(VUlkrxS(q|_x<NGGׯ_gqq{rz=333ܽ{۷)++K~?'O`0}v^/ /c|><`aa_ׄB!n7,͛7399I^^mmm<x{xܹsTTT Ş4n7?ʔF̠(AZ^F5S9+U*;uiZGG/o|Mb###8No X,ܾ}ϳi&b/"6&''yWظq#~BoQUUE4rtMv|;znܸAUUSZlܸQ˺w:ַDG.,,dnnrR[[Knn.۶m..9~yhVEM&p Nbbb_72::J]] u7rax7LMMt:M,M @ӱqFjkkYXXo3::ܜب;55^' |ff&ܻwX,ٽ{7nݢ7x;vh4x{.gϞ%330�vjJJJ?b,_Aee%^nݺhŋv|lذ|;qex 8 *;ZTZ'S>S4< 5C!&OƯ$$3_RU/oN'?~`0׮]# (7odffFw F.^ f*ՀMfYL"B0)p` 77Q0 糴$pN:d";;[L$+_x<x^rrr9xhkk{hl<vwE񸘒s}VTd7orqڵkrX,mg~~>\~} �ֆf### vPWW<t%bbe61Lf, W^L&Xh4b4),,$++ Fqq 6^zatF#W^{x<[;wqF?##$Slp5h꣜%Z/*'UO$S͘ywZkGZD֖-[V*..fxxكl]vE?&={PWW'"ݻlwAAccc,,,@cc#NS<w8ddd` //l6MMMaFGGyٰaXrjjjشiTWWc4aݘfn޼I}}=A *++'''uQZZJzz:<֊EEETUUQYYNݻwS\\^PuUUUڵ\N'iiiz$??={`4:,((NSS*޽ 6t:�[laaaGEEEEE撛KEETTTO㑛bݺubxYY6[6 OCCdggSRR!77]vNjR]]Maa!oߦ[p8uxiii'G,ykNS٦Mra4ٽ{X_ZRDRudK[ɛ5C̣eqTafOO8p�4_Ś|zyafgg |&//OuвIk󱰰@II 9heW?%CR_ 6a*V␏ʆ")uqƻ(HjC!8Q's1�vŎ;V5qiuThMΞ*TzɛR.Uy>PtbA[~K4P @yZ~_4DQ6FG akU@z-GIG6ux<dh47 �F"UzFp%.imK @@܄`6aT#bz&G@kښ+=2o<Ԥ`^bNSn<񐞞b Dr> jiDj #5䐊?*(T`GSSS7i v&''ʯ4&�ׯ_G??/9/x뭷TI̥KXZZZKgg.q <b1N<ϟÕS15I^//^~Hb``!<oQ%E~چښ2R.^z@ JG UW6ϴt])TaUz<:;;e||۷FWWSSSlܸL۱X,2::磣p8LSSL&zXVs";g`.̅AFPZ%zMXI*@~?`6?%[&[dݍ4kʲW,2 7``xC٘Uݧ9O?y[laxxX�344`^%p;ƍ7{.o2ıc8w׮]Nunݺd2IWWi}ݍ륦/޽{F 딗eFGGq\N9q6ltŸw癚"~<z|t:hhhp�d2I}} !˶mx< ~zN'###B3mhh`bbBQQnMnn.' a0,,,PPPfQ__FdH4[l!PVVp,}!kB2!@W9!d>P:FdvUeּJ$S_|x<"333$ Ο?Onn.>[nzDQDZtuu100b1~RRR'|Bii)_5FO?FåK0L|466 n$X`UUV^ߖn?~L<Ν;455ݻwy)tuu0>t:/^$77׋#??v<x!n7֭OxkܹO?B:;;Cm*,..rmyprw:X,|>'Od~~[nKT{zz(..|>ӟDii)/^[n hmmjPZZʕ+W(///Xy͛7j9<>Ή'p:\p .\իLOOs *EMM mmmb1n޼XV9p�&IQ#4|7jDzՕݕdVjRWYVktϰ<22x7äh|>CCC:tǂWr\${ц 8x ׯȑ#b"l߾}٧VeaaSNĎ;hѥep8̽{tv(۷ORQQ4dSVV`ߦx<ȑ#l۶'OňAV+v]\C?{ȑ#LLLKKK w׽(٥�ٷo%]vo2==- 9|0"fqqB:tz@p8p8x<H8&g6o,[mm-'//o_XKK hhh'qKuuub1~?V \%\Cɦ dnl[MPҐէOr-KW^5Y/5GYӪ󴶶 ^@ g(..fjjzN:ʼn'''''ODj~$9IFnVKeeLh4XVaؾ| |$Iʕ+˲RWWǞ={8p�DQVSSg}FkkpД&_ӧOt  O/z*۶mh4r)!''Oq\\t>&)n7VUP>KOݒ4.w3L&'&IYt:T9F#:;vp DȑԦۜ<yJJJ}2撗NήRy^O^^Νd2I?,,,F9Q͝y\^V(ե)RR~˵6s_UY ktk@ @WWVUx5L&`4bqF<:00@aa!dՊb!PZZ399IMM Av./b1JKKEв%''" >bIBYY'r_Ip2޲eK4TUUxDa!J mJr R)q8TWWH$x<A7h4|>6oL:9N^/yyyLNN ׻^n3{=~_R\\L:z)..ÇAlbۙnxzDO,LrD  PZZƍnSTT$ۋm4+(Zz=/-Kfl3 E*I_dTl^\6㨼JPzek}%הV, vRdHU&>(tO2Ym;[63.5*]E;Òm%Z/T.%TF*Ym[I.T591@jjnJdryZ&V_RHJWB1 ?J3R)&''eIDҿX,&W$OzVwffe$㸜3\+">e^/p8k;'Xkx<x^.irY_lLf[/6 H?%nqtWUZmgL-D^6 Σ222q5<xӧ9fwf=9sFPȅO~Sթ$́yO0@H8<DF$gΜYbY͎n޼333"W_}˗h: R)._Lgg'=}hҭ_)P(ć~()lZ~}PzPsL3YGq)+'x^uY/Bd:x)~Cێ;�x1eeeD"n7۶m^ڵk300(tEOO555\z@ ÇF\zR<h$HsH/9z(T|~`XD_:::l޼A>cJzn* tvvRUUŵk7Lnvطo_5^ݻw6 ۷ogvvM6H$L&Eҭ[gݺuܹsl߾72>>NGGssslݺ-[p8v@ ѣGh4ttt7߈$mmm:$4%αva||T*ŵkYI)- tttgr*++IV4{zzhllDOSECƖʮu\%mE^n:V$!ufoZB ={Yd2L>kƥK8rO&LΝ;qNA***8{,ӄay NbÆ ܾ}|*++h4P^^.V$6As3??OCCtwb2hkkN~I >|qM }6< L2;;,,,G~A#v'|B4exx~nnǏC;wسg'ON~p*++ <x^Oww7W^e߾}|\4.^f V; | t:޽KKKaIڧFFƍLMMFioor (Bp+(//v N OСC;wR~?o 䙃[ծVN;JD6- 6ұlF~*/]m"{eVz$ٹs'  l6h4K|Reee"l6j211!l6ҷq:lڴI,K6h0LfhiiRKm6�'\"H^\\FQWVV f۶mx^z=eee8 :޸q#|>***Dt:MeeGss3ׯ_v܌!@t:\б�� #IDAT.M6a6I_ |].uuu$IFGGٴi.K0x< )// Ҁ4͢;vrX\\$c6ٽ{7333<~x/,%%^1ϦMod.(k}{+AiPKSK*')=JfsO6V&W+Mx֊o=aI><gnn1񴷷S^^.R;NywH&=z C>co~�w僚gϞQRR"aB Lή]p݌TP(8~vvÇ B;DQhjj޽{ɓ'v4 EEElܸ{SNQ__OGGH=ǩd2ᠨ*0].xǏ DGy)9Z,və3g~GCCeeeH$h4Ξ={1|#IZՊI,s=(c41 .`ppX�կBTTT ٣d'1yPX$oONyϜ&Yxq>,I2Fq8\t@ (**aS #qL&at`0@*D" fBἘH$BbJ"4AiBPhYp8L^^©fa6I$b1L&DQ,EQQFp8LII /^$Hw^#G]4Fb\|"nݺ/~ ֭[`RzߧX\\`0zI$QV%(((@1;;K<n3<<L"t։D@ hP%f) ‚j<533C~~>VP(`ZV0dnnNФ H>䶓"9ߦd"u˿ *r/r�DwKK˲lŰyƨ_J<929Tx_ *_*뷉dPSSCMMyٳGl+5پe".~U7YhY%Rd2Ioo/Ϟ=<LhTdH=d`rrRbb1n^ JYGOO㌏+5S嗲XDW9,%}!O>xJ}b!3ӿiFFFSf6J]v\VpX)^M+)Zo3nVk*gir˼2)\-mGQΟ?'|!co9$I<x@OON[[W\atttJWv>s1YHaΜ9w޲>* T*sתt^y^.\@0$Ju:&p2HeYIryB!EٶTׯk8s HdudLy* ҵtelJ|eEV)e;Bn{mufvgC*" IchFRJh4J<gff ,}/((l6drYFD"!D"�ˈ$@8J|&T*" n:�&'''-K5Jp`4Y\\-cccN)**"tvG~#z=:N\Cnntmxy rz)//' y< 4<8hT%{V}t@4! 6ԒZbjS hfggW- K *UPb-tf 999$Ib`P|ȡNX*Txh4l6 9F B$a~~ܹꄥBF1LX,zpLZ-NYf֡9*TPl"^Zđb-DB!$ *^ $T*E,5R6H$*.*Tx)bZN'bx +LI+Jd~P%ABD"fÆ j)**p8L XçNX*Tx)C>޽+.\PH0HP',*TH0KKKK fگ@aP%bLHn xN'X|a̪Bp8,dBM&+FbY6 *^ $`M uRBKD0n:! K /RhDmJ &eBTPoV}!S *^!6,*T28pf����IENDB`��������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2011/08/image.png�������������������������������������������0000664�0000000�0000000�00000441216�14502137606�0022604�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��������� �� �IDATxU $%IGtA,ذe-kUw]쮽ґ* ;JBHHHHϽss;dn͜<9{OTEGG' H@$  H@�&0x�͢I@$  H@$P"UA$  H@$p`3PE$  H@$  _ lv1SV[mU7.]\lY3wqGqUWO<D1tbݺu/ H@@ /w/Rr-/ߋӧo~_p{{[~]̛7W$  G3,}m^NS&�/GuT2V>O$Xc||s+bb…׿F_6}?AGQL<}݋>KD�: H@@# 4x+^Q̜9x_]=6lz%/)8,V^Zw?zK85mڴ⮻Zŋ$  H` r-^,Yk>hoo/F12dHqWY<sQ?Sg_s5ŗov*8o} I;7eʔSNIy_wuرc˗7x W#7|UC9$S8 ˅>SqST O:x|-N:>}҉=ܳ8䓋C=4IzkZ衇~8y睋]v% kwŮZ_qVK$ ~'w;nJmo{[LjMFdT曋~$  H`4  -Bo,}+>я{lUzU1f̘3LA&InxӛT]ҥKx?O}2j{P?ベ}C_WUGVQX. |xgS:StL^xar͊ %& "ΪU�x-OH@$gauOSgiZX|(:::}a0~teިQn!3IC' H@{/~_"�L�y+);|1ySOz|Y'Mvoc2SgqFt?W\qEAp �`?{sIi12;?я~ї|?˳(OL+( _|q2z5yM|Z$  l֮][N;�"  #H;gaƌ% H@Z�+}kkk+vTý oHlc;ݙxg$�?i{^ƹX,gx]\^V\tE]&�/� [*4]veoqܓ/:1Hgp I�O~iy)5&0?ț$  4+ ?ߧ0~0M _H0v$  T<HZiK?W~i+x9ߟvs=}Wwqi,;�λ[昈q#ag �==;P-3Ʊ 1�ӓe y#d׾fBx.7w#H;$y%L\$  H@7 Ū;է$  D O?5kVq嗧wTdV2@/[wunדuv"y+,?ոw�0.'ӛk><χ$K.Q?Ftx֑Ow׺@$RzsL=l!Ĩ� }<: H@@%߬ևc:tȎtVyD8_XewO~3zOOaD{�u̪=9JN(Tsml`6p0jj=g}`/5mK@M瞛^|o~V*x ͑DI@*V~.8vr]ݹڂ w\n|&X/?yz$juN�T>Pq6\y8p<[�[x/X3L(5 S^|7I$  lNKH=�sNoNJ@< uYs˿+W<vnty߰aGJZo}[׿�9.wL,T˯=[ts:Ow XϹ�_>ROzƕ$  2VXm`C' H@@h C$  H@$ ?/H@$  H@$P''�ht H@$  H@@+p$  H@$ : 8P'@K@$  H@Z�%e$  H@$  I :]$  H@$ �h-)$  H@$  HNN� $  H@$ V @+hI%  H@$  H@upNF$  H@$  '�ZAK( H@$  H@�u4$  H@$  H8 ZRF H@$  H@@�%  H@$  H@@ VВ2J@$  H@$@�. H@$  H@hN�Q$  H@$P''�ht H@$  H@@+p$  H@$ : 8P'@K@$  H@Z�%e$  H@$  I :]$  H@$ �h-)$  H@$  HNN� $  H@$ V @+hI%  H@$  H@upNF$  H@$  '�ZAK( H@$  H@�u4$  H@$  H8 ZRF H@$  H@@�%  H@$  H@@ VВ2J@$  H@$@�. H@$  H@hN�Q$  H@$P''�ht H@$  H@@+p$  H@$ : 8P'@K@$  H@Z�%e$  H@$  I :]$  H@$ �h-)$  H@$  HNN� $  H@$ V @+hI%  H@$  H@upNF$  H@$  '�ZAK( H@$  H@�u4$  H@$  H8 ZRF H@$  H@@�%  H@$  H@@ VВ2J@$  H@$@�. H@$  H@hN�Q$  H@$P''�ht H@$  H@@+p$  H@$ : 8P'@K@$  H@Z�%e$  H@$  I :]$  H@$ �h-)$  H@$  HNN� $  H@$ V @+hI%  H@$  H@upNF$  H@$  '�ZAK( H@$  H@�u4$  H@$  H8 ZRF H@$  H@@�%  H@$  H@@ VВ2J@$  H@$@�. H@$  H@hN�Q$  H@$P''�ht H@$  H@@+p$  H@$ : 8P'@K@$  H@Z�%e$  H@$  I :]$  H@$ �h-)$  H@$  HNN� $  H@$ V @+hI%  H@$  H@upNF$  H@$  '�ZAK( H@$  H@�u4$  H@$  H8 ZRF H@$  H@@�%  H@$  H@@ VВ2J@$  H@$@�. H@$  H@hN�Q$  H@$P''�ht H@$  H@@+p$  H@$ : 8P'@K@$  H@Z�%e$  H@$  I :]$  H@$ �h-)$  H@$  HNN� $  H@$ V 0Bۚ-K@$  H@6:~GO4}`ڶ$  H@$  H@@�˖b$  H@$  H@=hzk$  H@$  O�wC1LB$  H@$ 8I@$  H@O�t`%! H@$  H@0O�&\=M$  H@ O�l�IGwtI<p[W._:PtYcԘ&NNa($  H@$_?v�;b' 46֫#XaM$  H@@h@USJ^O*rkQc)'iU+>[0ewտKއ2أb$  H@$ ' V"[+=4 tvMS?J% H@$  lz>?{c xUyxJ+ÆLC6ț^-؄%3v]n%܄C'v%/ H@$  H@@G,w3.nj呀$  H@6UM�h맗�Wzq[&;� וYf tuҖy'@y@՟$  H@$ @�u^ AR4Zn$  H@$hM�wwىٲg%CqۚF356ltʥ�QB`:J/mk( 6<~K@$ M@'�]M^5LzO5# H`hOei/Q0 / H@J�+0qeb%cHW5+S:`xSjo;!܃ey797E.֟%J@.o< |o9mfgzp|oe_ H@$h>UFʳ2$i(/y!O5/ H@Ee'�.Ocb5lĄ^̾\tG?zĠk+k2O85juI'MK#GO~o<u 3sËKQ9~:_SdXMLg~23U&7~)qcݟ}_0$  H@h �\yi =3m:'�@zZjZY/?$  H@@$4KM^g.׬ٰXigI)SCλH?&+?t膽yܘҙH\ޮmOC2O/rj՚T>2l$Q |@JjM2$  H`ch � ]%Z+}CkKi%Lզ kGc栢0SC H@$ 4!UtiJ~ů<?al~?ҥ~"g}9,yƧ?`sOL?zzo[WM~HsLÏsJ:>7w<d~wg˖.OSsJ-M[W; OMށ0hP~ڒՊ)-K~{{ޡ/ wzX]zgM= #FMg$l:k,OYU`:<%Pm@_wH]%Ș)r`OSΟG;MR?;dh5,yS-|<w$Ljf:?aɯ T{̔H--[<? [~IU}M+!qcoΏ2߇N֭]N==NBť.gENyG܈һuƎOGG H@@&�`c{'P{6y\]ڏDc5+҄C[zĠ>,hG/_/_"\ezq~_?(&V/ezK?^jD˝ >qZ:y, H@H>7ޠ>)!y~fm^ -~3w+%g/W*cK+K>=W\WgWS)[en-h7~οh$=zyw~NU9\tj򤬺#7rO{?tIfl˯s{],(.cǨlC:|#΀jo/Lg;# \JzuI1&,>4$ŏþއ"6[Лt*o _4?D9cd:~߽/3ƽI_ɟ<Eޙ|{{'o=urxxN-w<w l9-b?t餓IKW<'MXY*?Lm/]<7ݚ_sc:\жnm:|#yC:>`ݓOMW=6[gPs9SsyK$9ȣ&L:SD%mYlEP4 HYJ3�#GtIiL5t-hyHj:k2r[NiA?&."^|_S!FĒ;ŏ_zyCȿ2'"/5$  LM�q5kV畄救w;>]%j)|~%)>9_r啂i퓎'+szwT:>'ڴo]EW$?x嚼2/:TjˉҾ<t<tHoL{kxOWH{z^3ǟ+7_w q_G$Uץ}$,͘wo'^Wo6ȝ_Oח$P9<Is�� �IDATи1yEso꾔D[[/t鏾ۤ-Pp 7ߕ}䷕K{I>&VJgo4uɏ~q:3fTw1˷6Nv~cwQ>ճ~.1'98KR:?v?OO}Co?oYNg97?jN2图ݴt값̷|?;,|ět; otWfo~$  pM�hopo{ՁN15l$ ڏtToE:,yi"@PcB1!+ӋD rbZ2.E:3Zj#~y^$04 U>zוʤ3E^!*|zϦg|#Qy>pY3{U wkH~<b:G]MJA$g?8KNXZ~{LJksȐhOw_,3&zp^yyC 5畨/uY 7vL^| W[IM?toJy瞓]vׇUnՑBAItt)o~jGer]<t-O=kHǿ8?3o8 $,~xɯ\?㻗=gʼև?3%]K߹j]>~z Sou^?g%#}"Vv:}#&o/}o%5y_ϼSnŪ3=.{?|9_M_\1st3ޝ-ϽzQ:C.% H@>W[jx}#[[*61VZH Zx|]W-ummC][Wty r .ҫ̵Se~4E+ [ NHSJ"8/ H@>bU~V1 ᖬ+z~b}mW?i^cSO+"'S*?=#~v-K QZdE|;nWb]_< GuUͷ?%ݶu"JK愉yEd17%^Gi"'K9 &DZ_H~H/n ^h~9j&o4 H`�a-݇"|k ]Hg?.wF="߿ Z<64e.^uDMcK-7\(.(?5cFgmt|ʾ~wmhΓyA}ߜWڷ-Y匤u_<=זs_z:^,x,߁l:?ߚ~.z8+}v;*;/YuNkϭ 7ܜýCd.Ru\/ H@Pغ^-0Ъ]Myӱ@9X9se~CN[[rÏR=xPKiCWv 9+]L?^鄼N.6E:r(P)xӭ+L!(\1%  H@@'�] [WW啒CFTR׾Sƙ?K5D{o^ uO^9]?咥Y}1\8o6\t^Zy_YC6$?VKor㸽Vո'k敔mV|Iw;^88(=_|K~ /K^_w)ɿtѕq_՟z7$0 ڏTv`~<;<|@?||@:5xhFӷN?e~>WN.u;*=Uך;X/ًpWh zks>sIҿ/6]֏.MoO_o}+|?rDށ1zt7wwN0.Srz/ynY:_ԯ/G|$  H@)6[F H@$  H@fD`ؼ|G$\}e1fzUԪ'g#{n)ǒ㟟ُ>O}]ɏ0.|M'e9$wHoV a37H@|ԛoR,JѣK�5*?yLo,=k/~>g:臯XꇤLBDnmwT0xȰt)W֬:>Rzų)ѹ_ve:L;/[wdίfѷ~h;sKG?$K r v2|҉o>옼3KL;^wbrt}(K.O csL-vy~mq Φˊ96ŻK_#ϼs-v|?wn$  H@@W+eI@$  H@6WMSM7= ?Vڧ>ė ›^PO'C{f=zɿ_3ON?O)N~Y:^"?K~YKG>?0 _RRZ Rzv4ο ܐbTy=VvߥK;K@~sϴ1]yWM&-Wq#mӐ+}>s脼\tD*GO>WRv*}V~yKƆ|Wy<if^YK-ZQCs9Ux|y~QL$gǏ 3zSˌ+??lD~}?S$asK;fn{:=]_N~ a1+=d;N;ڳ>o?Iǿ?$t;%;ft$  H`s L�sW 6 X/-6߄_)Xz_#~*WG8~{.W{K'W+y, H@>+Ͻ]VzF}ԼR0yc)3C-wI~1cQS 3/Oɟ׿)<(N'"kK+R_z߾4'XKH'3ׇ=ݢяKzݙB_7>K?,hӷ+C<­)K~8k釬LB`^#SxJ_W+G4]fM:YL7Ѓ}c3KSwWg՟gn;GWzt.;*Sί78y?~EX?n}%Ż&&Ǿ,vQ̚0G:8t|A;'=X:^hI_~!UZίnHϯL˽ci'tskvqt>z<!Oח$  dy@.e$  H@$  Hh�'+{6|eKg G YN$̙W:oEvZAr+SWͳ3?a_vM~3t5g9wl@#0tغ4lT~ʰ쏾?׋%rK?^}ɯc; flu:Hߺ,~=W[::}5ſ3!Ϲ-]xn鸲\{H?uɏt~ ~2?ۻٳr/~t~$o{m$ass:]`H@6�l%  H@$  H`'0@5KNךayI(:,"\kz]s:>3;!r#;r/[t_tyŧ$ Lߎ)ygy'ôr:O+ זN_-RmvH k ﺙ;t"wLfb:5j~\"~!GpxO8;"$  Hn52 $  H@$  H`hNV]TI_ɔCe-Jț&$تy'¶{tT$  H@$  TWif]n>Pod\P+%$  H@I�Tya)ƳIYȡ1%u[LŭBQ$  H@@�A4$  H@$  H@8�ӣOA)[o>˗Lƒ7uў-y^/zG�6 aa%  H@$ &p; FkĆP l&$  H@@h@�GWMyȃ'ͨ#Gyy\<זf* H@$  `(bH@$  H@z"0`v�D!׬όuSmg$JC"~ X.X&I;nY~C A%  H@$  lIA!$  H@$  H@@w�~?-n܅)9$  H@$  w�R$  H@$P�%  H@$  H@;�~-$  H@$  H pA$  H@$h 0[D H@$  H@@s 4}๧m.s$  H@$  l>j�E$  H@$  4;)L- H@$  H@"0hC9yY$  H@$0N�4 I@$  H@G 7g H@$  H@@80f$ H@$  H@h'�ޜ%  H@$  H@ #@P$  H@$  Hy�h{s$  H@$  4� CmF$  H@$ pyY$  H@$0N�4 I@$  H@G 7g H@$  H@@80f$ H@$  H@h'�ޜ%  H@$  H@ #@P$  H@$  Hy�h{s$  H@$  4� CmF$  H@$ pyY$  H@$0N�4 I@$  H@G 7g H@$  H@@80f$ H@$  H@h'�ޜ%  H@$  H@ #@P$  H@$  Hy�h{s$  H@$  4� CmF$  H@$ pyY$  H@$0N�4 I@$  H@G 7g H@$  H@@80f$ H@$  H@h˺׮][XX|y9 6;vl;::5k4ѣQF%9/)H@$  H@6@KO�\xgܹŋI&;S1qbݺu|3Ϙ1vm�iL H@$  H@'-=VZXlYhѢb… K{{{1eʔbĈiYW^] N$  H@$l-=V &ӦMK-2?~|1u�c ƍs5%  H@$  H h C �Vy֞Gpf̘1M�2$  H@$  H@&0Sf K61?2>|xOב �I@$  H@M` s-=<l, H@$  H@@'�&%  H@$  H@hEN�֔Y$  H@$P#'�jfp H@$  H@@+p$  H@$  8P#0K@$  H@Z�5e$  H@$  H \$  H@$Њ�hE)$  H@$  HFN�$  H@$ V$@+jM%  H@$  H@5pF`$  H@$  "'�ZQk, H@$  H@�53$  H@$  H 8ЊZSf H@$  H@@�%  H@$  H@H VԚ2K@$  H@j$@ . H@$  H@hEN�֔Y$  H@$P#'�jfp H@$  H@@+p$  H@$  8P#0K@$  H@Z�5e$  H@$  H \$  H@$Њ�hE)$  H@$  HFN�$  H@$ V$@+jM%  H@$  H@5pF`$  H@$  "'�ZQk, H@$  H@�53$  H@$  H 8ЊZSf H@$  H@@�%  H@$  H@H VԚ2K@$  H@j$@ . H@$  H@hEN�֔Y$  H@$P#'�jfp H@$  H@@+p$  H@$  8P#0K@$  H@Z�5e$  H@$  H \$  H@$Њ�hE)$  H@$  HFN�$  H@$ V$@+jM%  H@$  H@5pF`$  H@$  "(tȼzbɒ%Ŋ+uqGرcC )&MT5x"Z* *bb!g_叴-[ҡ\CMiY&㘴 vb>e6lXsq@+WL,GwF%֖tMiǏ/FB?kw+'NlXG/NqRڿe3vFcƌ-y=sjFGr>I3\M7cGQ\ja=/ H@@KO�`tu]ܹsӀ/wwNO<n%za`Or<Cɘ83g&j c0?~a|c0`M> ⾂afΜ9Ɉb0>~5VLJI|xꩧ�A!F7k䵲4]hQ3Kmz=&bA�� �IDATh�c?&o$O#_~ibw/MQ p'2ٰ;{tzwJnl9&wߍƟ>~7ߜ^{IjJmdfOr;.Rl֩m XZ@ h � Zf</]` & 4V~hat fM� Y`7$h ]`A* #)S[mUZmCaD1 B6š'(1&1bD`l3P={v2ޙr-ޜhB}3LM:b0$)'L}⮻0iL7a„2 }>ްп0֟p2{ы^Tu?mq@Ϗ<H#qAq/@j;fb7O=i[82D=fec;2}m6?y$  ,-= /Y q ᄡ}wLjL& `q rVXӠ/(T[NV0#~|bt6XO[ƍ>0XÐ<#3f$C:_2Mviit`cjeMsPL>2 m[`0gN}r>υߟyh?c'e=>1G>#/2"/r3K臫Hoc]-M<mI[1B~a;N8'�*Ay, H@h �V"XEÐeES%n E 8 YaEؘ<H i; 0`qBl V HaT`A &g8Rˀ]ߓ'ON;P0gg�z:uXZ�]Юjd'3[DۋIFLP^Û&|@Ϟ#6&s(G1MOzh܍?1:'~U7v;EӄSy\K@ �X5ep!a?)-b (F([1n00$,<ϫ2(ƠekLPF|[(* `d~tc7]3&A <u!Pɟ{Oܓ#R)f8K$h � x(3䬬 gAq3?+l㱁X! b -WgF8a VI[D>2xBSl>LNcRplA3} "+혁( ۗ (' 7dLHq+(p8dYI.cǪ ס 7a`"v5>țO:=@|CƐq0b .pyGSpc',ɏt_ CҀ| taJ:?S`FCtᐛ WʅwC /y␗4-qI.PhL~p`#Gxd"O^+19u G~@VXQPH1P6#3iЏ ? ?>*K=_c eE6 =U0$|w[ C%ty?Dr}t^8G10藲sI7w2RD݉>kt(oOWkC]2Աh#L3LyxTzr"tGSX'D^\#}xnxEȍN G:p74>2~>�Yҥ GZpÔ{dAsOs-8r 7H y ɗ>�ۆW!NQ(W$  HD'�"7oo1,7wnl=C�arSlû ?F 7b^w�sw8_s5�#߫*=w\Z%@s!YVytV]ޜ`8Kȋ*" J&<UzUל;e@]vY2^gvdz0-I1&q011>1z1ȏAQGE9y[9iKF:wv+^򒗤Iaȋٺ8gN;X01D}@PV2_z) XD C9$<s/N o D4u<^1.` {/9F/r:.Oj3l9F7dzG.z/|6^㎔,e e#߸Ff F39Qf G&€ ;/ kMua쮼&HGL&.-/q}_&HQ'J9cҧ…zF RsGz?KG_ 'y(?y}{Y= 3}a7:馛R}nP pQ6"#'+Q g_?m6@C#Xmٝ'W_GaGBW1KжQW3rsCn }+ilGInG]KLD!/e0C}_ h%A٨o r뭷&RFM[9tI =XEiB^O^.VaE>N I`qϸ[R^?OO w`=$  H@ C Gp4cp<101> rf Ҋ+kt10T1ȗ1c R :hKe8 #! -b38BN 1�[8aLba`cx딓s|ț $#`!'lBZadrUs1PFBǠ&2@p V!ם#j˄  #LX %Zd#`�? BGy0!|pp7+eÈ&!?"muF2b Hz(?S*�#;G`SxSȋ:@~t0ɏrP s# tH2r1 qPI#SGyD\8aS'{2ƕ2 7&HG|䏎 ϘqvB)Dd)IKy(0A)?0iC٘hB%)i/S7OI7Dyt#<yLs|`훲7G^Gm~ Ѯ);L9GYm8 ¡X0,iC7?tϑ/ h01HEGG܏z %z&'d<&ePCV޿ÁzLI0ISrSfm2Ts!,}3t$ J@0sd%F7^; �n1PW\V YA >&7r V1N >?tC4/M6 .>aYMPbp/c!{_+ap}Iy110\ ! @`,cQvdP(AVBcda8aQHcC裏Na[VXxto8Xm"²ᆎ09C"<L)vb ;I Đ-qL0Wٹ uv{VY&O¨%>oy艴('q} Xq=C6 K|\ȅ."XQ>K)3I]K (/)rq1LIٝ 2+ta`CVv6@Fv I iR>_N̬QW vc䦭!ow7� i98QiGzo\ɗt•aqsC6XP;QvV0 !vwOR?| =7H/t17ҍ>2& 0uȈ\?\Ñb'/CLQO#@'IH#d 9ɣ':GykO޴#НNLS;# ~Yrrq / Q2GO|h.L>0@F?q@#e>T/џŎh[ġO"?6~6 =>.Q7WurRc0FGIH0I?Gݏ _GtvpINA;! u_' H@$YO�A1QGna a pC8ύ#chb\`q#8U>q 06q ޸qcXb\a`0hF [1#/Bȇ!F91001:#0^ b(Cpr~! pc40f *%ׯ@c F @'ʎ0:,H<8F> Z-F_ G 0BDX %gr'e".zĠ9 #ޙH41047\aF0" )+uC 4QJ6rnb+67xQΈCaÎӝCSV6:#M`&+ Oh1[zB=' `MCzل ]2)|F>=„}IOH3 F#-stKb҄s}V\. 8o|34<~zŠH rE>py ='xu:_9@"]/t؉D=!.u$刭9IGb�EޔrHzuO_\9l?@!r3 sB?KטCQ7'z p o~3yj.Lg„GEB܏ɏ ?~v}SVDinsآo#.g-|{)=g\o=2^ćN$PIoVKer$G Xv'X⦏A#ܨ  0q}q䅡 p000s ܆A1|Q^ 6"'1^0BHȆ1aq RV @ H(3*2Pn`cc b10M:Ȉ 9" l Y1P&d 0 lo 8LJH#2QopB}uc'χzHRO 7uldE&�UU xS^cHǀsgLJ{d!F)YhM>0ȃePNmȠ3NkBK";9~/LP.Xr w•I=ƹv>(y R~}JrM+&fvhk5ʃLi13uzO3D]cpBIIpgtLBO_%Cd-ᩫ<_7~ {b񡿥,?>^Wu%uz +[ϒet` ˠAMPىz[DcB{-m!G¡jr"/rQV uh;%mA/C@}" uCĤ;e=u[_yO fCV[t$  T'�irĀd0h `D30㜛1a1�qF 1pÀWQC^cFrA qB!,֚>�i c10Ԑ C =胲2&m >GA-!0bߤnso.@w 9Fv{ʇG9sI |'_$-oãotMZD|"]T+ \ RbE <16acpQ)K]+T~!#Bh/ɐM: L`O8QKQ7Gȏr /(yʍYqf`(S(#iu&!~O.I*(zȅ+rk5J'_9OcUw{¡_횲!p&y>z CeSP')lp%lBoKAv‘m迯Kwe,?WUΣ<Z~ǀ2S&+û> F aĠLLšpuRVL;圉1zEmti~vLAYvL"?Ñuz@}NU+Kyq9GC6A>҅;rqBװ_Z~&L8/ H@f= .7׸чiǍ- pܬY%FQRV'}00 Y+A#F a{s> e qN�4XyC` UAd 䤜7\i3_\͑b@7qB n>d� ?db'b+瑏0y"1+c8ʌ,DS`O T1bY"<y18EGȀaj9眓&i(+u4 lbWS~ "I\gA!/iDD vÇm1gV);u2H^0 >5c.Gz.W^hɵ28|i.w!:aWe!?ߵ:š9.dA|pݢsLrM>-ҨDž}!<+e#Pw]ħ:*5} LV_ۃ½'tI2q>Se.cBd+oWQOʯ7@|8Y~|8O8]y\YYӪV!ї$  @׻~_c pq/T4c$0Hf +ap`LÝxF^2 ț|1JY 0BV[ߛY`0Ȋ d)'1c:ˀ Csl �Q>drqÇ;|HyÃA^ J{r'&_('zDŤMZ6:&tJ<Xz#d (COpa610A&&Y>ṗ/u%<[v<u#//RJ̔ks.nȂ dSK=>CT<tNV Lx0.PrGѮ(/\gЈNH7u.>Ç:ŇtaWk59C\y!#udO ґH >@~~;\0&ܸ\~Ä|UȍtK}@. o ^Mq}!y#ưZ]L-D�;e<^c>FפO&mtćcAWIQOȃ^Q'#ש/MʅO8r~C1yK"+3Bc"?"_>:LX|d!Wy8K@6 ZՏ`C'  7r v0|0*ؚ]i/(.F禎11[/1 ˀ7cA >1fΜv>0!WP\eL�P. pȎ9; "I`.=93pf 10r KV7GyÒFkOX%awL:ȍ \##Qb20 :q= .o^arA& $mMh ,@QO1\.ʁCwġH >0aܣ;NR,Sw9䃌޸}ߛ<z|o#uv:Og :GJGH!7|hW=87pݙmACGFGȃȃ'.Hsd}T=@�� �IDAT6N1C>ģ !- tI975GAHH{-OGQ'>}ܿsp]}0J:ZH? zBCrSH35.&hzL}U _K ??WieuOP&2=: H@@9!�7T 3cA1 6縩c`L`ac0Hg qMIs @o.GZ'. +[ � o0BFCL?s�A@<cTa1#L KbDe7e|@r(d�MN:3IÀ"^jr./*VP?_\cǰctGt<8(/F9uM9۱FUs>& 80g=iR?rAyɓrb3IE9+ר+×@٣|fPC(<}) iLp:C{+Fq>� i{SG[7 23C.U:`<̩|$g8c/&=<. \5=ݠK6 *O!?Deɍ~&VK�ÜPA>O5GҤ>Yy58>kt[E=A_- i"/u `b#\Q/!Q&yN8[=uOx yrm\ȟ~2pzL'PNÏ80Bn$  H@gڢc|`, p " ·A17 u 0avm鿸1TȇA7xVp1\8cCӇM'&!8V`h##d/Dу[rs�g1h100^{>(/\pAaQ? j.t( y|܉ozGnJ' V(e:(+uC0 Us#>bcX#fvv1pl~A+Ҵ[&f̴an zS)L )RҢ1~!pk8?}_2xi4Wdd'1eSC8S^tE7`NQI/z=k %|{J;\}NJ6]Ѿ/K.I}AXޏ֣M#g xvЦ ?>9¢kx_{IG}g;OݥL A #8֙@q_:v,�x苩_Gz|u_rS?/Q#u91aO|6,@~ v`CMn3)@pGFڡN$PN`@N�pirSfMph 0Dȍ00*q& ǰ |oɇ4ȓ q`cq!#aLJ<e\Jv `H-a�cr^"O9(+'app$:܄5+\i0X " *,r`pxr3Ƹ"2b0`&F]VÈ%zz z/xɵ1@_Գ #G/d'=ExG}F}m$OW ha�=:dS?.D>réZr6a+ANHGVp:G8q 7G<Ҡ~9.T"-҄H\>!7 K}J O\8~y-D{E^�XM<Oynqh$+;MG#F#0h~P&-/h7y((zH<g1W&=7WsV),Gѯ%< eٷ{}H|z96=\ad7uc@=(U[DF> C~t &MdzJ8W:E\>KZԙzL^8T)`O&4iюD8O{vOܽ& H@pof9`X0x`70깑sˊ%7I �͛tX˜㎸TJ0 O8M (0xM=9* \ɗ;7~n1y,0(h!\@8"#kItpȅq<fi"\11!8y`uL ?3XHf=Sf1 SO=uMJ싴0'msv蕴(ˇ8KS 85Pȋq=m07|pC:?I5cħ>>2(#"/<('`y\tI|i_vȇA4y \/$>nQ)Lfp%];PF䦞#.&>ywsˠ>utK%}!m,Uҧ�(oGd:Eu&F;HwtN/So ClGu6p"!' A YHf F!$D(p_}.[sUݫztه?'_Љ,׵ 2NO㴣&ahLonoƇy!/'3~6lJʜ.*lPO+t[ԗs❝0#0BݲyĭOS c<x[{zvݑs~kq6:(¯ꈓė4֌a~gDx ׆}i|^$ű:A;8t^t.lGD#qI,I"P",>^'T.�μ}ؗ/\/` ]9WB K(!GRs)-\t[&&I<vJ"-257p؍_%d;F`}v ?IL¶ 9*g. qJbk0[ulgi+Ʌף, -B2e<ә l%>Qrmm||)ϲy##tm("0٦ho ړXTsR=eāvg9bL=Ϝdļ>c|{pbs�;c}60}Y,D֢ĉ1GcvbD*=謝+庘MKn2?"P"0?omg:[ڟ& 3%˒9+)�GÖP׵Kbg mbˮ-^Z%|H>v,+\:-o]IZ~LϯA]m t['㰮Ӎ'LɞkHBgF Y\C.d/SJxik|E,; xexk1mYTK_pO;68k|@ƺ<y$acl2` <?;wv00A/v;>r=k6V#nM/ӷ@(EC �s_a^"rwoM4%-vn${S7{{{gIid,{^Vg|]*E˹L|?3[T\e;q'C=\}CE̟;31s.ooC_SGW29|0:~#\Civ]=qSmضlӎn"P@(ApA4j"!N;,w׎-ȚûC ڡo) `!)XC[@8wk;ggZ@(EA �kcb7֓20*v;2OkR6E[= ,;{wmo  �f㮥"P@8Coږ"p.ة̓ ÔNNeK<OS'x nd5<nqA~N^)E"p|8 �'I(E"P@(Ebz`'՜bgԴ"P@(E"P@(.�V֮"P@(E"P@(�0o@(E"P@(EV_�NgÕk_J{=}�(M|Ńu4vlR<4ã<DJ[&,}vd50-/<\ .p8~=*LNR{ƽ!nG (^򓟌N_\< }93GƉ4>/~1NJqJdCK.կ~5Igk(E\t_)7Fbwe/O2HBT?fs~=&iӟ>o}[׿\U-�ğ|/}i|gK/~(.>X68YD;}s;hEqhK.=O|k/~EMb2~_ơER'=�,F9sf,Zp1QeQߣĪ}"P@.�l +Foc9y9(:XxC2ȿ_W"l (E|k(2;@8%L �g=k<I$0B8,XyЃ4S>)Oӯ' ˣ|#$Q7PcQzٻ"om,pï|+c/cNz=eމ6"%ݷ)ƣv?f{ #;waVwE1Z|8tcP~}.ϝQ)E"P1]�89WղU2e{$ܝvA8\;}$|-�뢋.�ɗ*>qSԑ[ x ZWᴮqn?Xȼ򕯜=Or׷m\O7>?яf?8@ƽo+q~TvqOOc\Od_X @)p>u6>яqdg/ctpw75k׼3YSJuY#qjE"P6E �"uIV!v�j$Yj;RAFoᆳ!Βn {H%Bݵ`.>I:"ŃQaɟe8-~lO ȅ<d}׿9~=5 Ÿ/L@8Ǎ]h,lC`9qxwۦoGAP=y'< vY1iaM fsysp}͡U; Q@(El@�6E֓(!v|ɦLd8g:#'{n喑Tۙfrlv^ YNZA"`tX tQ=`ax5.^ r;eؖG ̗s!,�\5WḂZNU@(.':QqKmG4fWEk%v$v$];6n۴~$vw 6>K';=G>c[k'(;^96;?mKuCv7חFtݭo~VTuܖ+Sk_٧ȻuٖuB0`t;H.埶3_KB7%8M~DaJu8imd@y_tr]]vomad6 ?3{~;$d7;SNya#.#6 ,On@S/tmU Jgg|p]dۥ_'VO?޳ vq;>`볢O<{^]Oh~"Me@{t~/le`fpqMa&s⎏_T@<:3QME}:G:l}&F{Cy|g,E}i2溃E=, yy٫^'1woƟv:qOM& j&]]{S "P.lNF(뮻mϾt}q"=B_g?n=5ߒItfICqD:ɑH ']R a'?9~[;_O�&;c/|a<L90џHȍ783~39H)vw_ ]%7셏ĉte]%Hí:և\NI\ȣ|@Gg Q>@/~4^җL@. 'z_s5cc.#>@g,W>y"E?E|d~ )ȏ-bo'-~}{;+ 1wW#B:yzя_ +Mi)Ȁ>gKXM_>MK=O62n:pkz ^0/UtHzWc\az_lo>C6|ġpGwX}UW]5A'(>_83[n3+}~kCo՜(>l@<|ψt~/xsqR!ݖ!)O?OlzX2__qc> ]:HЇ>^{5?pݰMy>RʝPϻno?tb',83c5c|7blL>؁?/KaI_!|?"S}ׯ|s (^+N:NǏXz)?6.MbzQ~�+\nރ>uި?E"P8 �FɆ/qJҖF2 Y Y"Jxd(,DHe—I%<;'>m_Kv$&Hz�!3$!`< dLbb!qKy R) ."ŢhRK$S0Dgo%>K^?,XlWnQ&N郔#0T^t?) 'U^<ER/*I=}kϏZK":H)=hM;$t>$ :Ac@|@6u8M[_??oQQ_[~@X.;bÜh\&neҏ|6.|ybћm6]8OcYH ;qOWG <̗[]Wl:q̾ Xȫsp]D`٦8ϗ>[4CD7)`z+>`j+~1Vi!_U<�f3 p�-֐}ҜULГArGyo,uc9N?JƋP@le =}kn6b>qMgf\@\S}zN&qGyu,iOΦ{|bnY5mo"PGD/�b|HB$fZ!BJ$8vs|"H+^A>$-;Ȁ"_ g J l!4W2`'{*NR"!l8 QY?f;H$E|`dI$<IIy5Is0Huu6:=,J0lJP!I73�ڽG]h\�� �IDATf'`)F5z#v]rC , K /XH${l$Tm+ƯN _X\@aoQGL촱%Y<鉄Gq7oxFc~&'Y_?9< dI%}+,nƅq V|>?||Iob޳^d\ve_lel`^CQi�m`txa]r~q]~N|vWY-\F_>4ňHK/= oAtD /tpM +>>;Ƞkvo$nCBȦ�tt~CGؘZT0]馛_KCNJkxMH0pAQUz02/3\زi'x\7)N_\2|�c˸b;a Ẓ́|On@}\FW1856`$-t{yB2Okvm!Ci;|d34?~̃O?d:~b]~`Gupռ-_@(�Kۗ/b")E.AP&<K4}= dE W}@w|ɺ$4ׇzt4.II$(3r udRrohJ$ѝH7!kXα.GAgDB,Qqw6K$0>)JEv_ d lf+S2DMn>oevE6YM(| Y?lSW"dOŒ]>Y+yxJ!d#T S*bL0;]IJ#[{{TØ?_mIKؙ6[⒮gQ߸#o<A}6n?!u$xklABF$-r8�!xEW\cCȿß_`*>UuKo|?m:ط߫k1ﰍ]0Я샋6pBL ,R.X=9]?0 1-7lj~[7iO*[~9,rlvf^'/_f͘K! k.JXmUsK>?x̌?Kmѯї=0CNǵ1qm7ˇOcމ=?ecxtޣyo[|["PN?볓Q/E. L}I% $AI^,"Hv$H:ȱ.qH|K=I _tџOɚT[ɇIЏ0$@)Hrvn GoXJ8% {xJ%^b,A"%& @Cċ Ch_ %ōߴ (ɦSZs}r,2 .$I?8aI2Ko0|y4M51B`vXR-V$Y'|%I7Vt(\x,+==؜yX>2dC$#`W[ȗ0G  0RĹUq�/" ]a)Ί82̟7;:xOg1 ;zO_^ 5ݰqu7)ԟ~7o] 1Nw&bט'++|OxӾ_z[X{O鰫oګ>g\w:k߷qc~IumSacN\ɼg.[7 )EӃY�H}K}v^5 zD!I".y�$I֗% xfLȄY4H%/@;${3I:*; I%wlXWF.$.dK"RG, !0Yg"#gdߝJ ֯ xL%∾+tHhdw [$[!Mі,:!9>|D[1)/R_|'D4=%S;/u)8#OGEkz+ҋƘx)sxY8D&;^}vmq:"~>s:ԝw v%iW`^eM`ņWs-<d\9} ?öm LM?M|Mav*l'2㓿_�|^#_;ua5UO9?2f-+q9w}WMt#Ůk\q|/6͹}OȲzy|jΕS@(S�ϗB'IBu^VILK�?Dȫq�ɖ/o |#@xӛ4t%iOmXoӉ$9"ɢ)J7&0x.]U+"rCw{?$9-Hj$@xk+FLquuX +]mM~/i+I=$~y;1C<8uBvɕ�OcF} ;%^%|п0"J|O_/‹v Y=/ 7|oEPieb_8z?y?hIɜ*nG7䚏Ol?!:J"YdLzA],&.2arᥝiwm xm/d?| l-{'gwF|eY7Y>?(s輋y/z"P@v's%e^Hd3h=Ȯӗ/t+%Wvb,ig-ȄGIF%;$p$)i#̥<"N}z9ߦem\W+#~Bg "m|v>džy?-aaSy ac�)v^SqLV@8k#Cn)_ G^ɰ� K'dAMs8}v [tD~WGWc':;??vK_E~BqqK>06Wd',]~]/Q_rw�|V$37rm^mƁY\4bu9/vx$>E;H>Ld.K?[W*8b85Ov^8`\Ɨι) �Ue]zp 13=ŪE1 1f{ȸ,nĊ6ꈡY*?}p[5$Φ:}(E\]PB"9B% ~'lKΨkvVԕ24dߜgX%s�b!͟H>$O5$-E2B~$t#yv]b%AIS6-'9o~ 7 ;|!iB@$fKT%krs֯|&xLK[>%6ŕ 1.|]u+$ba)D$i+|$Ž qےd}H\釄A' ]1o=@S]v{`|Mkn(Ao=IA>obeŸ8x~ߤ~g|tM%t+ZldVٿMwm889{a!̻n?=lE s&uy3/svf~'1+΍OpX'~xĮu �u_C2\,zn /2lqm2_Ӹ1+↾S(m7m:~,�Vt =1-~cG"PCLٷE_(ۃ,-I<@I$=H>$}9gAe]~Iv/ JR"m0J%9t" < HDO֧]=kRWB6K_`#"A 6ێ+2 8_K~a.s#8~4\'g-XgeXNjP[~E ,@I:\L1. ~׎ON=lpq1ὸ̲SLT[Vd? p0GnE*z/Legx_ u�x{,w釩Nt7vluN v>$ <>~q`.Z67iN/g禅?Ml]n;F>۴1F̍tXW"nƢ[X0bpc0UjL™8ԏqG?1Ϊmtʸf~̏q6K+ӸGtNL uqLUl$sռ'&{"P@peondr8n^rK\%a@TOeG~e9H$gɲ[,)ҏ,r"Q/ElA?# H$,Inu $PrzC+r$:nKX7dҮ>_em,H1uzl"F, }ǹsS̗GD۩B2ŏ~7;U~SLT-|j7-6=;5~�w$NqYл;�w'&wk<u]%[ eH&jR{B4�J-Ư1߀,wS?)!2Vp@d,L],8K^Kk8!q,�^zS7x 71;b|[Ƅ~:o_6n}A<ĕ:`x8ݮJbls1~- 'sCƵFc:c,d\[ֆFiܘwave8?02ղ|yyz](E^N/v_$ɺ/)%H]ɚ/_IB/V)@\#O \foH,!8_Y8+bl)IvKl8Rȱ�Kl8讍ٽ6 Hɧ$lYpML$=E[mAI!>N>M28*lGWt-i_,u:ԇm*~c`(>$~"KƒiHM<xg ~S)5;ȮEq#;1.^8"S|/M;g (|;O7̘|%Ed7 %; (b{Y6W;dnMov' b46g/<bG;jO/xcT[ W+'x1~| ^oNm48?ゎ!E,tc; 5u-ڊms$`0XW~28?7_b\`?f2&6/yrWl,"� dۘbi=[3>n!.?kzI/qPwYqMEW!{ ׌ӌkg\{|)↭lϏ3i3ef7{)S@(E`o~>h@#;ԑ d)ɠ/oN\]Hdɗ3rm]:ɋKKWbN.=$-M2zITHoI]EN] ;u]w<Fzҗ<}_s2= F_rHD>;ȓ P7$+P{iFu%~$>tt")lnI~$L:<[vJLq"{Ii7[U|I'8 m,7\/oS_/2/_s}p5aq#>WaЗ~]ak|dW[1Iā]0<sO'vMm Ƣgb^ Kr3vBda-+K7ԁ FoƝE"A_6>ȟ&l2ٻCqї T5'[+tsdl<u`y;+ͣ|Ga "7i+y(bL:_?x%ׂ7rɥ+ďfsd1l&&򖷜%C/O/WSv&֍kؒcX7pJC8jDߌt_~b0:!Gy*A潡d"P@`]zJG@H$MI/�͹Xm$E|"I7<~GM �nGWF$~I蜶d堯$zm!r$1&ym:ok8nnω+|${Ȟ]$d! #uո꒶k8G.B7y<?%82vyߙ/*쁍vbc<~}M:o8d?\c.JqL.翩|Ο 0>|!Ӟ_ Y@(',!voq"y='āM60潃vE";vT[!z)R /a dH2\O"+WB("sQ[$('!EF.r@7mI;UO=>O嫣\&vFI{0Ưvo|~>.>M_5DXz;ez>疽-HBemk0UGص[3D79v7ŵ*;RW22-'\߅&Kp$]wT>OJO79/qq~oQC}+ߐێk;nq.n vue"PDwдڱimZ$m}$z?MEY%+6tN<_4}ba.€,sQǮ=>vr.;Ϧg~X'%鱨M\-u'V͵Do}îNS]o!]m~*a[7ն.٥dcmwۦ"PN]�8D<TDjC3R(E\  ~t^{x^�!}<Ds ܩR@(E_�p~ި7z#%N^%V-E"P#d{;{yxq:"P@82nü<TIsbQ0n7T@(E!|g�<XxﳃۖE"p𳰣,]�8Jw(E"P@(E\0I]0"P@(E"P@(]�0\+@(E"P@(EG �x�"P@(E"P@0ء=?ǞxG|ǥ?toP<Wx{~蠇Iѿofvh:XUW~,E"P@(E@ �;~]w53-<Aa/>|“Ϝ93//>'>ཝ{K8w;pk>O,<Yʿ`|xTB(E"P@(E_b/}i| _tE.vqN}{cG ;�hVǜS_hL/X~;ߙ}_w%.]=/|a"C[l{"P@(E"pt`�M[ vݺ6R~n汫nvB p{  w|ߜƇU;!;ݕ[@(E"P@�vcs�Fq 11^Afn�;t�w.g1]�H;<KUw?OFew庭�� �IDAT"P@(E"pa  +;<)'< C�W6"P@(E"P@8Q�;nm;n{l؎6?|o7vjc75<oGWr#_r{u~<96Sy?Mx#1~=]Wn55d3#)vǍ>a@ouG܆6~'xA~|A/e]}mCNzuv:Wѯ62شhrrwG#8GN@>g}G<ĥvvd!ͣ2]{"P@(E" S� !}n(DF~W̞ψg?pV i0< 1jCt_;yO2<;{9,z*gW^y~ ?İ HE>ComG'{MO #2n kD“e]6'\׿>?>@~hg>st�9 {om6 ݽ=o9?`?ߊ ؼ/ 營,(P=m<g"7R׽n\s^}x/|,ˆ9x7Qz\'<W_}u�9@(E"P@X^�@vf[$ 9hWU;*Ҋ#a_G[dG=X;D"E�T;qvW"חSEfw(NHd^C䔮Tnvɦ?YˊktЗڲ]H/{@)g0~Z}7p(2_&V2`. 0y|@>8Ȃ{-rF\})r> ȂS|}`71'Np,X,bĖ|ɀދ/2|n)E"P@(E�!HdGuow7 rGH umYE;z eHn/׏\*"{ r GnP#^:H; `*0,d& ȫu#w]w>#lXAwDOz"Epfda2>ȶܥ#XqЅ= ]<;`L&{ᄨ?яv}~d_}Y oI;s,0w|#oHw>Y0_PX>0sK(E"P@(E (!vNvAE.Hby9,#i:#g�}FlbkcAudq{IQ/K擞A ɢ 9M96#l@:R>$H:n]G/w t@HSIf O hQ�Ug6X0@Moz": �8‰.ف,{u\Lz;?)E.d;_�"�*̮_HGqN ,9:h=],nU\ہ �0"P@(E"P^�@=$5y H,V4#RjJ#mdz%ISA#vE -X,[kӂt(ȵ8~d�Oҍ-vxܤ EHc =y ` ֯Sn!Y&gU|mFl-2@ ΀ zuNm9 �쀃8"e1G5';p^{ܐpN\X Ў -E"P@(E p�V#H?LٍECCl}NAJ E>!vw#t#mͮs!yB_.le"lubbէ9%}Mn䒅."r `&u{wgO[6mRa>^a\w; g-H>{,Z�r'e]b ^hagq/ lC -X{Q?\?E"P@(E'z�B0wc-;nuGB/?l5b)Cs=yur>}zUWȜ֍\BT" L^&I6[97m~b9xߩMvΑc 2aKt`DvWaϹniYu#F{s';-İ;E�"P@(E"Pv^�cj'խ~pq®#vp-�"`::"! �d.0RiYkia LvK{Ц#J>gȫn8#0ʡ;07} ׇłu%w=n9Yxsү?[XA!ګ 3c_1A8E`5~EsdOȡp ~OK(E"P@(E`[N4@+#jދ_l$Ӄ<<R:H D4[*-2"NA\Ն{#l`BU Mko,&hKE^$7fV:z?}5 RK7*2gX<o}h7NmѶ�_v > ?9a!nF]o CeX",B Ʉx5Stk_yo& wN R@(E"P@�!J9!*fYD8Q޴dG\?H+bzEA �Ƞed4D{>$"H%AHy M9ݐLofG!~N". OlFl8!nk9!d>d1vBvZACXѢ<CFL |C_r,(!ⳇ "|_Z6Y3 1dLuQ_l c\@(E"P@Xﳞu-u*b)A"" ߶OG>2d?UAF*!)B_Y|@RM(H #+s||AQ8}^$1yKg\rٟ \uUZR g]s O:+Ko#| [@_җSB_d�9�] E}̡wC.Xp:zիƿ>K3BOxz m6pKu[@(E"P@("p�r_BV'Is QC: 9w>Fl6{idE"u!d;Ee5u,Hm8]DuytN?H]gw3M^ta^݂I3Nv!#?+@X‚lxXh;v%&*E.۵<:uzz6}m`#+UOO H;5N <yRyz"즣W;Ah?>="P@(E"p`??Ho d-"a!LSvjG[dy;>[ !"8#vq׿!VN92$.#mv$fuĞ}$Bؒnm颮ĒpЖ f7[At,CA wbvgExқ>H/f}dEWdY`~:p/h6twGa``[lrM[ Eb2 pgKiQE=kK=rk)E"P@(E!we9 �!Ҏ!�ٵ~́p#<9[;̃aFRٴ0A]mJiY06U^%FK[#}Ѕ/u%36Mpo16% k~ѺE"P@(E^qeZ�Iޔ؆ԐyCt_v~x.!c69Ua~جl"?6n]_ȵK?JflFcwI{v[ "P@(E"P.X7 6jx(E"P@(E{z`{X`(E"P@(E"0E �S4"P@(E"P)E �Ա5"P@(E"P.�L"P@(E"P@(.�R֬"P@(E"P@(S�0E@(E"P@(ER�pJ[@(E"P@(EL/E"P@(E"pJ)ul*E"P@(E"0E �S4"P@(E"P)E �Ա5"P@(E"P.�L"P@(E"P@(.�R֬"P@(E"P@(S�0E@(E"P@(ER�pJ[@(E"P@(EL/E"P@(E"pJ)ul*E"P@(E"0E �S4"P@(E"P)E �Ա5"P@(E"P.�L"P@(E"P@(.�R֬"P@(E"P@(S�0E@(E"P@(ER�pJ[@(E"P@(EL/E"P@(E"pJ)ul*E"P@(E"0E �S4"P@(E"P)E �Ա5"P@(E"P.�L"P@(E"P@(.�R֬"P@(E"P@(S�0E@(E"P@(ER�pJ[@(E"P@(EL/E"P@(E"pJ)ul*E"P@(E"0E �S4"P@(E"P)E �Ա5"P@(E"P.�L"P@(E"P@(.�R֬"P@(E"P@(Sh᤿o;=/뿆Y8{<{C2?ٟɟD??3ǰf}Cm1 v?Of_OOgp#wU]~p>omӛ??tbweo~3|x]Ԇ oco~B·韝tׯ)u'.pZ叩M'ʹ`_4X|m`=l̟bmYׯ~7ud./}O7/7)OYk"P@( j�w;׿>fwf?F_|ųg=Y>?['-(_}{^ ^y{$'Ƃ%\2{8Et6D3? >{0袋,}O;skm<O:T8.ц#;vBlN"luX_J}ӏ}cGmq?6xX(Ik <p|c3ՖwQ c 6Ӓ)S9%S'=c^%'?wq,9sf#>1imSۗ']v=ێu{"P@('S�@ַ5{g_f?яD$D߂�'yԞD!c;hA b~G?ѳG=Q":l_ -{{{wA׏$c!^M 2o~#VDo 2DƝvdVzg QdRhK?Xp@?|'=i/YjCOԱ=n,!:U?qbh,ׂkZ\c#l, lF ^pXgT2o| _~_sC?[$AvpωWs'?u]#N egc٢>m[-f~vǧ?k�/^+Y(3[@(E"p#p*�$SCH>$Rl!~ !H$$v,K}>hA U/F􍔾owo$Yҽn Zƒ;3e}O##4;^l�qCGH_A ~?g>3XlbmgϮڳw" w{!~W\1y3yv@x.d(Y?vIۿ,,#fy'?cco11-08H?A$.cݝ)뇽=_W?nf*bY$6mo"DP}/}iĝkˊks#_v^?p7%su?݁`.0_]w]�E"PdH.%vH 1B$.WG*1-߁?o"O*R-^m瞑p g\_?/2؃ nZgz vK;<\S)v+, !)ǭ̮!b F X~ MY@( ""(8YGz}!0 ܝ!Vc5)`O'>7Sq?3㔐F^1r^{✟\ y~ce^#ԋK1NӺ��S>𚅏Ux0֜Oیԧ>XWo1.1k)E"P@^I!IShWUcg4V1 o*;Ο"S6I.D HoӢ;>;vj_r-�@}믿~,#%vb.$"/׾vĝT|k I dS �K_^L"D;yEvu;G>qLb^`]s B=ȶ8Y<9gxn/0F.a]dĢDsͼ8ucضR@(E"p�IC"XpjZufdюRv7s{״ ;,EWu]w>vWO_ 0yIs {%;M*vLծ\껦JFڏEE0-BXT#C p]?3DPLgyln)ov-V->N ' I!66,"h#^z!?7|Xb7$Bߞ#t 1_VZ8L{C\/3|3_sfla?,X/8_dvU%̧ )bD\x=&ڑ?θ6ƌUE::/HL;użyA Ggz# %6�� �IDATت],G25WLL^ddiPߘr' Acv#2~ˮVQ@(E^�" =dIAKj x!Cu$K$)L2%I.j.@$~{3ȗ/I[©3/B'a� J\ώsu1� zHrp5׌ݷ=vՇd+`) {`?"i:~N;d#i�C7X!WH{&-H:"8/lm؅(˜-Fd%U↽C0o,Z~Bx aBO>LakL?w 1&+>Uf~YgH]#~y{DqG&^:ݐ1XZ|/ mvx3/>g)9M]"acRl-.X]{bPOGyA}؋!؋a.QE J\t7k\^H,[[8fyVlΘ1{Dέ:+r߅Q@(E^�9I%ڲȹIGI6J 9U$y!tK{#y -sɯvκN_?{$}stJw:KCD<d~�"'_AR/mv2%`A 2oAGmp2D|�1E:"H=Ȋs AGg$/- Y@=M;sd3C(F^շP6IgEϨ^w 07"+e-MGm`f\3JG-@:/DYChG2b Aハ`U!?06\'':҅~b';R`?2k|ʏc~amS{٠-@wc)sR@(E"� D$vI r`g[9_$ψZTL!vJɳvIC%Ȋ6w-tݶ$iSK]-8?YHqӎ)LC{|'H`bAǫ>z- a`])dH-IFM74FapUW /6-$`MdO_%Ȗgw<dџEם.�" a<חxѓP f: .~uNC߮.tQ;ߎ\u`jQ\|˧10>_wJ>"r.vHX6/ĝ?O><lŢ?cwbܝ5b\E.$c}} Dƅ󘱍ϳ!p68e1 ',*b41/vNN*{q`@;;l`Nv,b gO9Xo)E"P^�J$MǑy"BBD nJvo7 tuȷ_I)$ْY�q&S�I!DX;Iq"vI+yROrU<%|щ<ZH@6\'BJ6tAސ8I>A$MD#u~LwɃvգo!Ē}$LN?f'NC&QYV!?p|n0s rOoN8#>q:] oG,!| $#|+V!{ϡX/)dxϷcgbNz~f &&0Ɲ7`ml/eS4?E8ȰEG6v mC1`v+XY`+_ʳsQI\eM՗X#{gwwpl5'Y /Ƃ,ZP}˜ӏ"P@(:ˮO !BC pJ65I".01`"$T2i׉lMlx =>&ۻ%I^%>#It4 Ѡ;[dIv"G8؃XiADd|a[;W{, $Gj-˜~A Ęnyd pYpx8p%\;oT{$ftc7r0 ݰGnBWQԥӆ|vΫ>d>XxF+!LFò7muʦ,ty};㽸҇0.b.yoGbdKq4C-{3.,P _zI7cǹ,OsJHVMeM?(boWb<k>0W 7s2R@(E"� @Jb�ЫwJ@$u!BHp*B~2$) D钄{m+ G"UNpNy`}K]/Xt t6< ~O;X_Xmٟdt~DN~V=CuqH]YZ{}[,��WzL9ؤnb:i^M!v/nqI+™>&L!a;^q`|ɹKy>w>#db="X6v7?U\$O};["|k8g|xf�wb{vyꝏB{Vc(,̝#n$:"P@(EB@D/� HI (Ae q'! vn/+|0ȼum$ 4)p" )E=N/1uHX/Ri'-8f!d9|\%mԋ<2/lc#\vC*n9:L )i>JG<S][d_+ń{#nJ ;L{MɎsQg:v^Ctěyko)OKqXE%yakqmjL^Sxٶ"_b‰]g'_ -Ki}81"[v$}9)mUZpi_Mg1sAltH"P@(E>c؟�#=] /‹ QG]Or)ic!=nF6) &rrEki%fau5It5E2.I )c[UsMN?uҙ#nsEW::$9_SlR泃=t3]>`ؖ(+gY$+Ar?EblKekQ@_; "9pQGjws H.?5N56+M䐡5g6Vƀ:ctj\1N69 x lbqy{W~:7Xqs!Ξ~cd_WǸ{_hڱbMRɸSo2#7rt>׶7rޜVw:Z@(E"P $E{Rn#qd-Fj)wu${UvI]C>9 !v, �D'y9dy;@M]BwWL%\2<Gd?Y@bOp!!, HW$4Gkajz׻9z_? H9'{N4,;s˿DtJGP}Co:CSW\҉bB]C 79|kr0MO1}pe b0]Ƨg.K 9+avY|&ȸWחzY|*Őkb/=t|^٫ylZz&޶7}G}NV^"P@(E4!p�$H|" nmK%ƒ@$>B;vx)!x F8wN"$R_(us9#zwHVzI^ n.\;H#_#@:} 6kٔd }i#g3s[t?M#NYɎ'ݝ~Ah8%0"O?ّU `/@,;-l#BĚÿ$]Y,+^.qCo yp<^!Ҩ/u@\7^mG{ѓ<w;xڻ5c0]f�, Żxd0ԢqE(^!o#S``/ P>[oq9tBᏱSt"{Z.1M_^ރKƧXg7Ə-R@(E gp'I+^񊑈vm#qKC$>n;cHx B8$#$vBAܑi)�@ߒNIĖR%ΒU;{w-W\1`O>{٪ ɶ6#dM74ڛ!_?/;Ch؇0 L,|ӕ s>_!}`ͲB>4=#H qcn/2+U# o'?ɡ#<]x+ſG]^"V_ZHJ;.DwN̒5ɴPpX.}y>=ׅ{g_O#RkAuĀ2{ټB?z_FJ]s5cXR*gAH:|mp#ft~>OO9B Ǒk˿O۔|5_Jy{^%(mcxy_~{^+E"PiDT,�HIo2&:'E$H5 ɳEId"Y\JrK`@ ##Yi]=:v}#,ɱ:raDqkJrF&S;]g'#pDV7)ў+9/ȞAp'!w sx- tbdmRLƒ~ G&GcF3,Gd /LاO]nA`IdiS{$7)w^6-̈G]쥈?Oqg= 6{ u _!Ą/?獛E:f;dS&V`>vX2Y@Ģk+]uNpq7ڐmhKEuj%rݘq>=8hؿʆ^+E"PiGhYnw!C$@]U^rYFr) HqKqΗ\.L"yMrDvu$!CCbH<y^C*ɺˇ^I~ك|F閤Yۿ6Hs>!1ozӛ/<õ^;vd1I>9k2}s+}. e_hX;Ȑ[٣>>qWv:_BFl^מ7Ğo9"?܊#NcpWCW6ꇽ𥋅,uh&8iMM%ZUyl|̗p|ѯ:.8e3;ƃsnr}ӷ-c Q1>p"d~ 1?}9Sz i<b##�V+??Umott{cx0?X'}_cob*{"P@('S� AIjrHd%TIcZ|pK&%&v^򨭄w%&CNCf0$%^XB �K(~a0C3` +\oz:>)iݽw ZUV} kI YBO}rX#C$@"Bn|!O)]D "9ɀw }'ɽst]QA=_?J$%{puX gdBAE'mݲ5, yZbO|#~&5aҖݎ=jϡ7h;(Rs_¼ݢ|ote:>�J"|aMI^|^{L6c?AOH >?}&#~E{m}+?Wk\# G}0>9϶~(E"P PD3R*yI\_USi@ڱvSiK{): bGIEhqo/ .@^"es =I.2Muv,Ǣ` S\aJWo0&aIurT>r"װxe Xy4_-ԧ_-||9m}-ŮƏ~) }.{K -a8ΏZ#T,m,O"˶Z"P@(L'QX,SO[bUwɕ %\ɪj4v�'-I :}C&Yu6w L*\,xr@[;]\&qƇݷ)j? mHa<3v=.n⟧6Oe^N)ޝOㄟx Ai}qnJR|w9m:mS@(ECA �.q<%Sl`|U)ACk:)mU:-4շH!/IJfGջT"%7~SM>[$mZ'|+Oj9f`˿l`aP~d@?]f+Mf[@(E\tvd~IN%ݿUۈ)!{dEUl ˞X؆Iҝ<v%fABr+Ahw $z]n7J)U_}GCK/{M8v=v\E"PgJrB A=E*`x K<%Y,ĀyIb/IlAnlۑdtaT$ڶ�aQa8ecYq*ߕT~.E"P@q]�b@(E"P@(E"{o;EpJ"P@(E"P@]�R@(E"P@(EL#3m W@(E"P@(E`?�sAŋ^^l ȼ//c/$1/}^% _ sp񷼈 y{e|] ,q1/tF.wˠKMמ 6yK_E޶%ӟtop+"P@(E.�sAE*я~4>7׾6>{@V~Ͽɟf r߶t AoӾ/9yСl~{ɒg?쪾$}x׿O~dIb… b#q9r}C W]uՠe`,(nvm@|;O~ÆW_}xR(E"P@� Y$'P{jJr%`ַ5v-%dKF %%,xzꊮ{ C<dv{{+Rӟ? �[x>O>=яB]$IgWg>{,Py:`%?m4ᮅ/9yjf򕯌E0rmo<Mp+?~l_i�� �IDAT"P@(E!KdǮ-2;vs^"&I"M74{0{_={ғt'y<җ4v\_�1qI~rb\wD@i G>>0=iO=;$ˊ� %h>s{k<b* z,LHrk%|R"渚7Ɂ7 cλ.-k�;5�R@(E@�βu K)J-رoU!V_X 7͑Ǜw)e9EB'E�d햳�aoOj{| >�J'DRrv-xz??خ99@{]Ởk^hIut{ Vy Cr)E"PvG �cw[ P~o.cc󘱳*=j/z62g{,Yr-71g<;/�y*^jgT/[$ܒOxKY{Ƃ].U@(E"PN/�HU!Hr%#Ї>t%`Sԕ,{4vEeGPD(M vx]%%J,dp~{m$>O~/& C,#2d/[ˎ-]92OOoyڑ<1@;A %1hOCnFM̓ h9<|f`ONae!臎6ۭŇ`d]D}ᡎlÞʶ7bm3/N>3.,QG-0#.w?E[ƾ~v\~0O{A;ՁkWK|rF-z7Eq_6>J>]}@c�_x'&6`6)Oj.|'w>0n2>d' ~?`/~dRGQ|w9dȘŶ,<r_g3&DѠ@(E"p8�C-!سG?%03)!?x|yTWPxo9@]p޴P*j/8|{i]J}oMWgYU z!~~=poпG26Z`.TQM8:R,AG-ئ'^Mzh?G ޟԧ.M^6akf$?@_ڱhԧ>uN'oɬ>/_rEv _$$JF ?IbR'a{Kz+ 6`c‡oݼ_}}ɛf)%əoM_JbNk@'c�Oxfy8nx#15U~&碲8G?=~s0п͂w6@jC_f<clԞnVAm~m\D=e/{ٰi)׎5L}Y�~gӯ/#aq )E"PE�zC�U,@cɏ@Um'} 5/:{|Qyl^=m HFSaɮ`^)QJJ_"^=^ $"%;ڪD5 Nc×[$]0+ OuEzI8aԱSmY� `J6zx+ኌ fs؟_'u._`S Ys:| yQ?Ѓ*{1*Aq%^ɔ'~gaCvr]_`,iב]F0Б[ cY;O{xǴ'N?7;.I'{I Ӥ]xFYѢ—_3A;}=x/aJ0F[7َ|65wგ?z-c<` rNȈK<,S{8'yɽ8 g@ ß?Ѝ[@(E"p8�@QP&hhuɯ Q. vqA@W`XdGKR`O`MISΔOLA{󞋉d@+x7} ӟ/[,�t\" xVoE g;vH_;y}4 psI 9|td%ؗ KlBƵ^; ]J^%tti: eɐ$?+×_ْ]{~cgv J~ߓ>_@f ܶ'2{O'B$1d? 8\7=�u'@$r]6`/g� fv9WN͇aO?飊~Χ`+߹nѯw]6{4Z`WU믿~C%/yE_cri\7؏-*8H@`뮻n '2x/5O' l.qwFcN8mzֳF_6vOYp#s1{7O '<Iod kE"P@8 ��1XP& n[$D)0| <-`LiAA{$m<ԡ!p I0J$}I6I=cPp,p 1`: t%l._B遇7k{%xxl<ttHA^|!>+& | s-jH$_Ev AVro!C;O$1 6>:^ ȡ}ɧ$$uذ5>$ڐ1-&lCn]L]8X.$*|&Y<\Bǡ _>+<ɥOv!V^0DmUV$I='ǖ'&L/8[7,;q2A8GiYGEvk00~?Addgm3 Ɖq0ΰCȷO%rsmq?l;QR@(EZv7ATP-y x bHN�)xIxy�a'J$ԒE)]!X[*@  .#w,\CW;]N2-m }- G[]3r FbIv _W|mzpCsY!\}xd7g;?}t \ГĐf}gzI db9%iӎMB7g2-0 ofၝ؈?3O؏|vntqع)kals0;xo5X!,S{) ?a֒^Np/bჰVWlp_B V837\8^fs}l^&ߧ8L9O]ȉqiY ?}^=!#_ӟ,wLh]1㣞JNeGx \m;΢GƌK&)j"P@(E`T )I�[p(H4 I V%C |sv$vj9I^Nς$Nd@=]H4@:$E2MЋYq+pN?Dd'u2 enhPz:Ȉz<l";Wρ`>z�4%g6I$B =;dt /FAwzt[do|%Au?E/:Ja37%J=}';@K"DzB?>%Z�&`⟟,exYp/nt/S~K ^=um`̧_XiC&%)O__98;"/ \{l6QgK'~vUg[>%E1ȝLZؒON/` i7"P@(gs�  .w: Б]{5DJɨ�usYWG[zvd]EYP/hlFm`'cd]$ w4au�]vmC$FlMozHN,Ȓ'@"MU%Й>g>I ޡ)>vKz+%Zv|A[+=<ٛ|ΡNp[k8O_%؞,*0-] 탯VE*uOr{&$yz/: |`y:-A3wE~&*p }am|}〤M-x"6v ?lgU{NxÏrxB�529Dm~jq>oFyp~J#\@(E*de;0`uJXIDMi҄z@IQ&XVO@-PP+v|wE$cx7;FT7A:jmaѠq]țHO_X#)_`N;v`$Yiq?I6 _T$^K%x %_<Ȣz*+:I ! d[�nxm ?rOfx)M|LJ >oKOeu|B+~lB/ &ڳ~K/- 8|/[T>'e'URk6I.YU {}uSvV=>oSCp#:hKݺ mGO(/vO( OfoLѷOwG[< 9tΛt_8ү`+UbK<]'3~h?E"P@1OJI<K <Qv-*ڂq ѷ)Jxg SA A` F%CJ<#kv^0iދy}д3X=fUr&)<POJHxoNU7jP$Iov?z@m YņPǎ?bWM} {hlζ$U ZAE{bQ!zliӓ?C:Gɞ}^k'R]]hzYvd#;bgxϓ2|Wȯ??go7n!׻6`n۵ K78H&eX6f1[nŻ4{{^-<a>VY>?|I{$g@goz;ڱ?%k'^J<alqR@(EˍHVL +p Ғ \SG'.+;AO𨽄V2+C$Ԃ_C| K´!m:HDv <ڐO°tKjzr;%V⬝!�X¤-}  Av_rWIlCWfL{x }gYa0lŎ85Y$wlB:[,br3-Qr@:CiLGe/' )dܻwW$lE?8)r_TbKMڡO6rA6|o~sx+h?^o5I>giE9w-h_3e z: &y9 3f[mF2*>" %[x2gLtY"c^>NOWcq8MK(E"P.7z @ ޓ0@V2/;%1җAR0oV:/}Kǿ, HJX% ~AG,wvH6_p޶3@[ђ i/ܤµ(8&g~s:^]ɻ Wxt� ɐR׷-#з;`W{$ h d^ ~뭷Ϟ $;IvkH&l[)`o]Q:׿flʷK6O@+(` ѧϓ, ?YoXx;9k1[Ni-c6;7nZWGH!IR$QO�v}gGN2g\ȬC_�O؊/9c;6o?,c;!O-Yȉ}$?уǁ&+}C?GcC^gnpG,lg5=uCVo2aI3?qNX?œ!Z@(E"p8�`!(<+LI`u $I/(ͻ .vE9: h%6]ɿNR9꫋T4×, 닻J<j~mB3?M/An];ɌDhU] $<KP V_E"hS=IDS2awLJ77/x3[;Kȳ~a' r㙄NYh!3[d@2IҏV6ImN>,H7l"a :0W}%:?{aZ|w;i/c(YV2K27|| wّc96=?dHq  wu&!$2m)~ӄ<yYHo2}/l Ox̳�`�F߂_ԞYVU|[p%OGNl8{vv8)-Z Gξ+B_"P@(gfu %�V ς67@N; Tޡ@\"1IX$#> GE�(}{; O@U}AC2^X.y /|V?^Z$4 o*h%Y *}Cw'<a_'ZH=$~&v]UvQgm%e'<,nj+NI^ȏ/)h{DGxlClM>G_aBv'@׿~vtĩO>JB'<ئ8%<-_~ Oث]~L:8|cKh%|=CM&}cvIVˢ%v|]'cęFbOɿwy,q@#I䰸 7&0$~M#:dіϮ>?!Ogu؛M%7g᭰/?W`?zC}.KwuS2X$K(E"P\.$z`TCK<qߔ@?`S(xJjyT^vdE2>->h 4U h'` Rm�&9ESPLV=' w-ӄzܛ&T'ŀ\<̦8KV%TU}?HMG&ɒĂ<�I2$=ɏ]navO7r]i!e_v5?73>YT�� �IDAT2MpcO~I&~Bnj^t.h?|QNۄ<ߓ~?Ⱦ8 z *}2}(E"p ebCS5I h+w%_8~v|;Q op̣RB,XNb>Kt \ۖQ|FǡL\w8Wk^=}G /vOGYG;vk-/zpH_vE [t ȽLz=HJ>ni!aoixzZhşs 9㩐eMΌcCx:� dv5Y$7G"P@( +jan޺`Mu$<1`գy$ԮYtiM.|ѡ˲�9.⽪ݢ\ e-7E:!Q{~`BEі-t`^+; ^ ȸϓ=v~-i"?ʳ>h,Eneu׷3mvcqRcF(E"Pޏ-.a9 %  D_i�gۉ�A�{ģ~o7#$ǮltiWއB~O�xj"�  {�v[@(E"P6؈/v]0#&Dz%7v-ltU)s@[d]lߴ-wDOaO? 9[MHW@(E"PFO�> { E w_P㇞k|;%ORxٛ7=^]A"P@(Eu.w!"P@(E"P˽�U"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(j|z"P@(E"PA 0c(E"P@(E".�Ƨw@(E"P@(E]�83V"P@(E"P@(~~_wS;N.OWg׽fw݆lof/f};=tM?|\z5]zיNwTnFvr'?xlpj?ա_w/`?21׿\b1Wcqf>?q^o;<t8?.8pHR(EEt2KCk_L2`8% fyC.�ӟt}lOxfl+/@~N �Y舮D0~ܳ <`?x,Z,ud)S-|#AS=yσKlO~Ї>Ii �gJgO gG??;Xꪫ.�Hկ(6v>糇%@}o}/~q,:>yϛ9 OnEEE(~hswC@(E`A,�g?}򓟜}sHveD/H |31{ԣu+/| 3$]o|c|ғ4F⏾n\<V o}[vu`t(¾|+c1я~X4g|/<<ӛ>;-"u l_Ϻܕ"aWOGbk8hg?ŧ,0jg>󙁑�,BޗZE~%}"`޴e7Ǜ;[@(E,C �$_GR'>q=bMq;Aw+&ME�i<o Q-8+v<… CWb�^90⻀ w o o|#91 '-y7WǞVb穿gX8պ0k~@c]rv>zXD7oJJ-:K:?/+syS7ɿ&OxBiK(Ee@Vy#1cb2oG-XE?� vMܛ]nwyhZ+4'$:`CYO?O =ti`yG+x,;"+EH0wc1ոcwڬ+ƧROv-;O{<khl'e?,+͛׼iLJ tK"P p �1do~ܣE'vyݝwy?=Xt /UXĎ͔,]noۅY˕�_LdW&!`}ŗW?8ԒDӼe}c3=,~t`V+s.@(E8J�&q7<-]Kv[1WvJy]dĎjH{*pv:uw>vNlevM;c2Ho/ dԆ! $,=g<RȅXvxt6u@}S,O . yR,x;zRGQ_` &h\v{ V[:N!t0{9Ư+Yz| | ?]ڮK>߄-^#me؏4˯oLnѓ}}wK4ዦƋ/\} lcQ{aFF}Dh>IGt+\nb#(lgs <~^vAbƘƆY+/dY#kǛ^{Oz9\8~]ԇEO1"s]Ot! ᆮr~|N6ޑA}|"[ N?x"y! |ɣ>}Gל تyܩ[;1#T2߉،h"PA-�3 C�'21f4y:Mݎ'?ɳg>c u<M&[ &nIoA_?`$Mh PL[xz^㷙O4Gv4>PxM! _:tGp󒗼d ӄ7#(yk_;=C$4nw/|<g#!n~J<)FPOz,>,kls׎7 Tf ::cc/|}8mj%($e??>b\sͰ&~ {#A<#B/S>:G4Oþs}An)i@?n7 ~>$e ӧ]<M74ig_җ^Ŷ}C& as+݇?3"~K)*w]Ay/.CMN𘎻t7nZ@Y4ۿڣǍl`%/?$1ؓw^~Ozֳ.&oŢ3wk%/7+ln0.鴨wmn喡wq]ccD&~<$È-/li |]x{tK{s;ꩃgb]տKh-* <|#t17QF>Xꃮe1g43-MzW96E:Z(E3sc  $&Q҄hS! Htz)t8h<L$AI]�OЃ@DI>tLO}S#&p6d  ]4([R$4g/_§ॽBG0 BG"A?x/ lAHnu ]SECfر r< ٍ] *雀W, "\M -I]nBn"ҍo/_~'~DEŏ/>Y|'/1EnyS7KwXOR芏~Kc>}Wbv~ ; ? @=r)k DS\AKv~it0H,Ut 쉗[XCgKb=um%> s!ٛQBllOl72>cat\ߚl cօstkBl'9)-,$??{߮;{8a##kQ&lM8+ފ~ k n lf':[c @N:+ÌN?_>>w~(Ej HP#YUL&Sx ΄+1&뮻nK k6 DT%Uv؂n+᭍\EN uI<ж!I"7Z|BV^%`0XwMF $C/$ۅ=] J.m$t8 &LmtL}Ѕ+aګ^z0rY�(\ $a{z 螠u^vde} ȟ3 4Et Me* ˁ6~9#@/vq$#tS|~|?b~ݢ2_l1|L&$y,=<7K>c`6^)>'QrNb+vO;αE}Tw$ G gI|Ovq_;ْoy'|Od} #>+T귋J+y?|4]Vq z-wɼH&;Rog0{,l7a%+tOstd<>m2!|) 1O)Ǟ<,d`g lqN%HXf6?}s'S0kv㷹|M/?e؄SS摷c7' QQ6lMQw_җrOK1'v2Ư®|R?]M -Lが dmtn"P@8dhd,&FADI*H`dE:vg`p`Ʉuo@@@FP(0QM{=wd`lvE�*sL ȥ-ŸNT+�R_�,H"c0$~t ]ДL9]"5 \^dO[I>с"Q9 ;%:]bK`.BFu%mO5rH_#r19N@oU/~_ϑ%ڏ#x[V"_w&7Jx; /g6˼_3Imu]g7iN˸WN{ߎ);}i']$r>F/Y3YFs>AExʎxlܝuwXÔ_z &s�_e;?1[N}ESo>^ >m; O $<טЖ2t| ~.Vbm<7Qgp|)s 3rH{ؓ_ؑmo{E|ao:.h8-KGXIKS1WLY@Λ}dN؁LoR@(W.� PjnrDk"$MK lp M,G&eXY`"C!RWR+ ?^ IM*2.+xtC] |kXX ~ }څ],$C,9,�SpO@|tW{9O;h#\V6m! XP #)CJ4=?_Ц~)_׏~76صM;<?~$qy}o'iO~ö|E_y~kч[�K5G> [Cp/[zv `$Q 쀉I^΢2v&ݴLqԎӾᳲ$Z$ktfkH`g?{ L4ᥝyb#c$kg<_|ѵ`a?! U!?;e2$;Φ %GJWr _'gSt1'9$ZG_t:AlZw<oﶅD-eWxE„^gv%=`&# ~m~(E?j@baLV^U $@ ,nWׄI:$ ,Lήi@9Gv/93IEzRKGe%@> 2Ãp(S(+�ٲB6r A؋]N6vBg8lOv蓠wȉ*9K0/P0}מ )M%@߇_m{׶~'~_;Mq]g_Fd/j˾NrvM_j<t^f% 85*,;b#~gch4n.I?`}KY15~*lਝd 臇ﱛ3e|1  T|@r}%%| YR_Rj~G ?Dnt#/-~0ҏT|}8Gs >7U=OM3֛+ҿ]_ֿWt0X<COqKXQc3ؙ轍FDxg Ois(E\9 owZ0$ULv&Z)4|y4N@mwdiv0|LvLFmuYF^  ^۪ %o�� �IDATARBU?t\ `E[z X𜗋L8:]{3z:Moz#pbnZñ^K}ռ˾Vx_IUHП$+<9דڗ]l׿~ׂ=< j$Kv]Vby;~?ߧ3{Fd37%pϘ#H21OOc2Ǽя4Ǫo*K0b,Ua|2{_?Y*YHy?1gfʿ3BM_FlyyM;t; <ݦ# pd6\8~d X}7Zĉ~] L )D,<4"P/�  z gVZ3:&d;:&k ]Mzu{k6j#rnՄ+MM6 &uڱ!I@h2OpDI!hG%!?' ddS< ر✀Gm0,~jȮ' Y6C˯"0![Cf3k[x}U?ƒ (P̛7u-zmkqV la_„_/lJc>_b7MїȨhCHO Jp/,yy=OUw#.h2Ú-;;y2߮ e$Hs"ڶc4r |B7'yqeJiS]=L٤y:zH.3.MBtX,&_�2ϛdKyZV3E"pe pP �L&萼 #=] dwD鰀&ZIRnr7]z8h e]o0fv Q!/H%BO69OZ;&ER ,r5~_`P1_` fᅞw$3R^.x Virlma>v;{ٛuw$܇_E7GBVo)%Z<|?yra!PA>RH`_._CNR4 ;2|>촮_ЏhWd#bПKDta32ґM+ *wdf0fq׸ش$g 60wr\ly?sȾ>3{zo 8m|ʘ=fcGG|r|H]"å* :{Q?>3"wƬu6.̧x[gA�67/S@(+j@n7 H]>I�)YV]? "85ћ&y@/%[*a`&8U~A'hI$ Iw-ID!a E?WH`,H0?yOT^~Q量HvhkG#QaC5zѢ!@6]2ӓt#IE`I/|Wዞ|DP`ovv)%ZCɲ~~IBv 9г{JNqHO/&m~ɟ%[W~dq=$ƋU<Nqq?чVd <a褿ZІ"c ZVc?,l8�&ei“?"2L+~w~K/|>>Rϧy[\#+{1R7,Dawd"G:}U_#?Jho E )sˊ(̼iΛ7"P@(AGߑM&XA Hj26?&j?I WP_icNp_v>�'"@Z5 Go\z@�xKR!nB2Z7舿mA& ߏ["NvrG_ N@YШ// [ցlЦ3~'i>yY+%Loy[%:<um`p~8Z0M74lLϩ%ЎMXREu&3f_}eXdUW]5|N\_E[T؀OӪb'Iw}KBȢ qkv$v]t-IlL'6xLmdیt鈶E`a3xc=w}Fێ/u%XFic_>~Bv\O7Hr}|pRޛׇww"X/p/͌'89KY`0ns,fA4bM,As:oη"P@( FPniΗLmIŤ D-ysOnudN+, M)^ILIh<I2k|%dL?:V$@[t.Yhώ6h/ ;w K.qF` mrfB]!)L,@ZN^|RkA-~d#Erې c :%gl>{-+hK� c[/9軍^ w2J<hϗunOpM_V9&7m_~Dov$32Ď^=2~/8_S }8?}:w 9'c8'd\7([o~Kng}'2O{dt}:i~dISx'5ve?c=z<:)ڲ~ {צ >S2Ƈ gv*|1|,"7tfg4:򓨌-xkA׌dZ7OS9}c9<R"&q`<4w<וI}х^dh?W|o$H ? ="P~F;E\6PױHy$m z("�P!83Y &Fv`MB0KLƂDF DNLښ+h6ѣltIR іO}\#`ɢݧp@<:]A<h X"7ܒ0%uکv>_@J$FF>${tIb?'?9I^G?9 +]PA&'?0r}96~kz&:iK~mi~P_ԏEE mG_T؋ yׇϮǩm_ [ғ?y,oÝ3S?K4_ |v\ɮdWmD} )2GOi~ݢ2k?@T^6fEe#m`CVqI(ᤝ?뱗dLl{Gsd|dwm"~z:=?:X, ]>G?ppOlAW>OSp=̭~*;;zkr/ImWؽE/vӿl&ct7nWя^YL?^p]K}5xmjw"P~Xrj�\ '&v 6L‚1p� M-$q9`כ3Z%$I\^}6d_0wQM'$ςjA`%+]%7s|0"m7ԯvp\_/C; Ԧh^KlG$RsiQ/qHRV_sѵO`._&wy]}>%?˸HGX83\`_8/_2 7� E,{b|ot^ZskbqmUH'1;W̓rd< -EAcuR᭣z3HbW.˂7 qɄ�m-&[To d@}dEu,z>+dJ >T٭?xr<NWQPNȡ&~.ئiw9 Ih|cYqoGaשz{y 'Y)輨&8Nq6a:OjGq^ni7z_%_Ӄ dKf~>[(y%:d'(>"P@>-b_@(E#o/ �ew\}c!pY/~NR@(E`\>+F(E3GS<-yR-E"P>p(E"P~-=::ɿGx]"PAr?cr-E"pjx!:{|%\@(E#p.�qxE"P@(E"p\B0Z"P@(E"P@X@�VӛE"P@(E"P.�E(E"P@(E�tnz)7 y9ӝC/srwxvgxTJd|fLy{7k{ö =fwӝ_W_%\ug?+mfpqwqU/ frzoRϛ{^ïK:zƨ;W?O-Sƥ(c?peTRR۾"P8G3_L%`t̂6I"I`@}�VG=Q|V䘗ȸo|31y׿#Hԧ f_g_͞獅~#?5&�]~Ͼξ/g?٧�D""-X9c:ϾX?>'Їk }o;GƗ$wf'#Sg=,nw[ɏ,%ЇW]u՘C-,w%57ԯ4"q_'c"P@�.~ǂ,X8k9ַ55@Aze|1< tx2!D1?{C2�a;/| c!E8o`ZSXD Qb'=I-N|zxfw$1 ƌg5ɟŴu pw}P>+S{d<Z&yF7}ӟIS[�XduOHO_G"=3΢k>/_q9ϧ֕ȟ`|`^'y_'c"P@�.ͳ �7#@~0H@pR$.\AdO2(@:+:C�hS3EscVvLI ;E;i3>-ϫ^Bd]-[&ė8&Ѽ[f?ndi-�X79[-ld=ԲtU&׾6ʶ ??Ðu:/䷠N]OO/_;MϼWWyZ@(E6]�8m7 "ڙ+'>1~;׼bR)h,)|$vC)2AoN=Um'N=icG} �v%""Ѕ//ۑ;mYK `!rl. \=~w .;6 p$w/+TcF?Nj|e:/}͉�|2y`m^^/e-E@�.@}’&^'dyo7O8lȖ Hֻ<`/xP[d£苏Zi) 0$-?Mjcr-E"p8�;VgIߚmJI;Cyێ= xv<Ɗ:vUKOIbNP"6/~q z/?r9]"=N8K6sM;Oz]H&6p =|JmF^:tTჯ6" FDo6}8[5Gv2HF�'2<E;o7ȁ?Z[.STp&]89}|0d#~K?|LV#yËiC60 xe>vb8ꠡ=]":H_~L~2(|.2m<sE/:[}xp1E}I/]VNj!t`'xaldT EGɑ>T>>;[8f,0o,ر7_g}otqlc<;XWvv dX5.Ӆ8`w8)Г0֎їLљ՝{cvOkt=͋kst` {Gƛ X7~r~~C3~"T~';mM{~j/2z-E"PV`:7*&}6?q=)O&Nu3 $x&{(vlg^ @oqvM&r$O}S#O{F @5D#A"& oIb+pQ N$`/pkgGb�6 >*(Pz۳ƒIP"`J�Ђ??Бx%d6{Ar뭷^`NJ}c6P <p̐O~hQx }sGLgAy> \с$H}; (k~}s7ȁ=}sHni/| ^>0~h7dvT_ҏ|t$NdkfGǿf;3mHn򓓼yҀoVwG~./2!\}H6}Mq 'wh=g ّXdx RŢg_=}^!~/4x9$dؕXxK_:w~4A{%)n?ß1tq}rqc|F_F~n_OK?|>";{o|˘?Yd/|ɼ_ӟaURBv=®T|xv~{3pb<ecGȜ�X9}q7t^4F3�;|Wa؝lnl29?X7Ȧ)G`o$'Aul@(E �`H`-yNd8(MX@' xqɘ&\ AUlH, sd4\''=L :abhxJ" 6  ƗsvdhcKA_;#=kaH fd OdOP:LlA>GO%i'p'kG6‹?#skH'\MC}aN##b$hHl=>(th YϦRqm/8%]\*p@߻|#6V!nz| 7۲9yWF }= ḍ+!bAzOvg !�� �IDATOwȎ~Ƨ,L?>S_ɢkx]?F_6щ%\}&&r-b&~yVKO`nNa'|]O? taN _)GYwlDv/\:㍱ϛ'?ɵuldX|왅2|טDo�eK(E)zd(p8 %oI=Rv%ID*@=9/ld|L^&YL =AZ}VgrvM %)!ۍ Rt nÎ ޮP9;t~_< GG(<`‘ CI`Î]U$dҽeE$I$DH�I�:,~>g;3 w a;݂'z8e ' ׻50s/ 29a"1"Lg+v `>갏]SV%%'O0 4G>_DW: E/zk}M\` p e/{ˆ"D֓!v")F_x2n{ r]wu$p_]�B6A n|6r2Hg=k ߣA~_V_| F7%/m'ASm<[xSp e,[cEF-Wz|3ަo;آ9!IOuό-2Sf(t8Fplg>с-m}?я󢱓dK /q[5lqy+t$&??կ`^~uצ/.aS0/]�X{"p!p�&A U 1 R`@dL+hބIޣ@aL)A'~bIV&GC=@ͤ,Y,Xw6+'(P}dJ"Yi'OK@ 3(%|O!`}9Y;$ޒ~aUA/A7PXd mo:O/?:N6ޅJ uU^.+8Hɀo mXG~%)hIvG'#)Eu�,ttp5){ۇ vkGWH)z䷈#1 w |h"0`%'L ~Ė08e@7 }sQa ٟ垱w_O$EkV=A z0 `fb= chf5h.Zk~Vꪾ}0ΪYhKL/ $ IblG\97ـy}"3caK_8ȫ?|M�;@{Sye~#9C‚d+^ g+`}/c#'j]7/e<ѝx68ȝ9ptd~qVR JoG.gꃼo}{t<w[GExw07o=zr MtȯݺLO M7<k*Gm;)cFc8v/k;|#,-E"P U2"i1X�E) t91 'fuclt}'8|xdrpqz-�38/ۖg}W&ɖ82|qdh#G. IL&2t Ρ|(q\% /PNC062|ىvONݾ)9'1]l6Qgo}&`mџ]gpL?=,E `rȍm~3n 1vOq/28أ~2<؀gc]1d53ƥyɸE7;a|zƌct)wknPO�O]$�6X>Ǹ5>HE v^6-S}UbOdjWx c3&-rK@'Iƅm/tqHFdA+e}P_sg!flָax+$۱n?\'>S t؂�}!Stj)-E"P pY'�,ޜ+;Nvp9;@Vq\- G�kp*7^8n4-qh9.ɂN~'r9(¯(VȎ@<d\͵ <rZ\=Βz {<qd҆7IF={ 3zNv}<ÞNLJFC =+yg|x.zB] GX 0񟱝GAg&NMϱ]qO=|W ٛd$$;]R{8(}4+AdҷvNF.i'`}Ξv? X6^>v/sy [d_8f<g  եW\PVxǘWqltmsd-Ac[}\x7gǞ {ưɆ}>9o1)>Gݻn(r㧰I󌣥"P@�#NsNX`9Q>+Y\9m {mΙ3$g-;f99b.ꗣq 3'vO]>Sdn;wV)rs5;&2q|9%<\9t񜃳Grx[2<Ő'NHrz ̃&8Wc܃G)i^/w)q= -0732*c{]!o;x'gS>Ć؎çkq)S;ɱς6')'lck: n/ svyiNM 4~laLJ(F_=O!VƀP_sgʺ8g %__Cs7?2Ы6ƺg0F$$\^@(W.Cr>rX9YIVv@E3EQ4z2pp,|<pa65 �ubo1wNEغO9:܇ >`/N'CO/+>p/~X)8Rc)q܃%YpbKK; tBf$zc%*lpeO`'�G<㕽kYD?r G8]#:u,Aֹ蓓p@K9əvgHܱ]I='?ፌtI&Qw|;Xcds?E]<Lqn'6׷\eg+R){xJm5WL>KDƣ}O2<vdezUm\29sqzʶ0Rc]#߱=?O xb9|qtXr:fFt1>ߋ@(EFN�pd9v?>;[,vKΚ$(r9LE3-5 {�G _s_q;:sm9B 'Y!tp\vm8I2q `CǚӈR<9zaKp'ݐ8뷸d.ѱv(8,$z*F9<>> L{.Ζ7OgcF?k2c-8Qmakva rdIޅ erٸM{RR0֯$$2ތgc P9y]Q}1!{:Emqg  WOeϞ26-S}%3s\x"?l!N"OeEW<ء5%%KRՇ2{r}b`+7i̟svM> d!;7_5662n(E潖@FN 爳 ,A> `%ID[o^-GÈ'p(n+s&9f-|n<K�#p:g5t9#A{' h5E0rP.F1>|< T2lA;u@N2" UgxzW;vˆOl8t H`% O8#<V1iS_3Fˮbs86_:+l\ikACpúB7x1c+z K ;KN:>qVyL28`3slMqgV2Jbƚ>6-S}7l8ls% l3؃ٚ IN e6\7|+Z[dp6c)ێ<R'IuƲ6dCOq-k _du]ƌ=Ыta=0&="P@X~a<,p*#gWbd$\%On,5�r7 ⮟m|M7]M`/f; |FpPpH� ƿ{i:szZag'$-5n$G8TdvLIzHvٵk*p+#Yw󝃽SGGF)Lيj|68d#>N`Clםi]%*8w|]m 968HViЅ1 Gc�<:fz|Xpgq!&ݚKCc'?74!@kcmpߌO3 2li˜/|mc86\sod~ޙ}q~Զf[]X[%"3&0_GGF;OVm:jiNd}uݿ%Z-Y|GnAK} o)E"0EN�QQ@[P-'a8s89�"E9M@ق"n9"̿@bl)ڻf3$뜝ZhL`ÿF4@ux\vE*  Z%ѽEe}^dT |M$YO#}6LQB=Ivں'}_4BڑS@g\-~ы j G_xГ!G?h%v5>ލAcOؽ7c0?W/sn؉De8gy3vbct=g<%O?+?Xv'8dG4%MSb=:kf)iq2glꑍ1tͶF'mp% ' ƫ1SKl/v )>!>OIJuټ7ZeYoR/|D~m,j\vkaw>{x]$Ϟ2ߐ%%皱z诚?3';cKg߲Ӕu>7:m|)[k2آy`](|\@(W<*Gn,vv98dg "[&HX4NbI<q,5U8Y9Zx ]]/GApg<x#v>'"yy88':(|]2mAm>~x Wΐ:<}o`Y p.| NS"9x@^'΂rɃ.qSsOе6vT/G1a$)O|i YlK? OO]})Ou#<=ȌG<8 x 6v,?3><N{bDb m2-+mMq@dŞBMf\>c?a?eE{;zopxk^8:?}É~AOtOmPx}gX?xgs4fKChO= /<S#ɾ]*8 ʌu9t9.x ]r?|LL+NuZC>ѩDɒzc=<;O3f.>W:6ߑgZKhltmo3l; e׫Jm2^2C6u蟞Ị+E 0S= iqf|Z-ր`uggX;.D.)qm8K GC]e}>_dIDΝ6CAxvv0C;噍L{dz4}J?c[|ΉH.[%TMʶ8l`(`oMJO;>  ޖƵy dױ針&XBsӒ絛U}q4ǃq`|`|ѹ=uqy-et=;u}/Nv/)A>oc"P@8z^z?̩]s\.h=#'$jNIKp@ JUO΄�rU}x?T8 C[#|-6Ov9x}Y}^_l9Gٽ\9՛)ucKdɳiM ŏB?c\73.Ɵǘ!?k<2.Z~`d:\Sxekx4Lp[53S;>B_>c3Eo<'6|m{"PB`s/28~Ĵx?9&xnCڦ!mqܵ>dHߛҢA󴬢cZ)}U;dMhݤ:iOlo#clYۙ7ѓ;>u|4>okw)XƓcON}NUإ+=I GK(Emʱ Z[pݿVn"*xL4dm9T_gS/"P@/M�R+E`K!?o%G}vxl"@?Pކ_}M"P@(Ex/�'@(E@ew^6o9�rTjV{E"Pd{P�Bt@(E"P@(E p�'`\@(E"P@(E�8;('E"P@(E"P@�@(E"P@(E<_￸𿡽|k%P_jso?h{lh_yyo~cj8쎀|^tή}{߹ŽzKYV?u{>L|K[@(E"P.'E,o./^8::M�__<xs3gCz}�.\U A|9Y[o]w[< OX<|+CYz`S`cGGgZZ~@l"I�vq_rIK"P@(ErD\$�~_.nŗ!`_'|SH�hp}nH$<Ń.Edď~7<<>l=^�o|;CO|~x6=-0@0S��� �IDAT?=MR@(E"P"dq7/ ;yC�#S %=^@CKU&;.g_ɟoCrE/`6ؒl_W~OUZ<򑏼TYevW A\JE"P@('D\$�</`?O1A 7-;q K n/z>vc>Cz9/ҽ;~XG<w݆'�aoI~ʱO�LBR"P@(EE@0.`-UW]5łm�gE)=1hǙ|T/l/H~ ]�|X'_=</kE"P@(EFN�؉<<mnG=}]\\KkW gV,`Pw{wSw{~Sn'HN{$6 <t!)dR< ̼ �#|>mCg]}}&6_\ûvq7׏'< ѿ= [`Wh+9@6!~aLFNMOxG>9cϺgp?ɎG=}}_7}TMXG7$h3>s}'/ ϸ^MckIOƀ |ۈ "P@(E"p\ �AxC$x{?8د|+]r{mu8Y;~/oS8z߄GĂ}xHu[<OxR}*@qM%! 9Rz׻!x R)ȹy:'= _rAA`0 _-j6[BϋɣE/zр]C?O_ 7 Sp%wN{ < |oGoK_"GA@}~~6 7 8A;w�峟졞'�n 8Շ+^AgF䀱@Q0)>' /dӧ6~2';[9.G˪}O|=ҥaaya% ?8pN9>g ɰU|FOx;[ dGSoOySzMܳI20=y =/ *1ƍ"P@(E"p\ )@!p�C⾀Oq9˦%`tAUТ@P) :DpO$1K}>]|ON|8)D* \«O+yA z >@/ad\ +t^@G~LC١]0diC.vSA#Z0|& m<%>\cIɎ`?rÞN6{=2 _REx粢CIѕ>2Љ:]=Y.럮ÞOɤ% =iV$XqR:FPuٸ|P؄?y]Q/Ʊ'1$\k)E"P@(e�t/F_wuC' |�AϿx|>a}؍T:1p BIR(p4/> oN! | '$Wv$;7A;v_sQ , K`я~t=A9@M $찐8/I kNkG^I ]k@ g P;'lh&C>wANCU_]tK 9},v}^OOhKb/}M0>KU �4k'$(\sk >,𤂝v0Qߓ%'<#P[}.+؃d ]hcts 7 O cl.Zrml|R@(E"PN : mSE(X 8L # gU 'n"H`'ٔ�2 |ALVr)]T2 Gx8\8!GP{!]`$Pÿ>.X,�8W , +h h=@N?hHl>]'/< 2'n�0h rgt,[OuI A@*!>dUo~6 ;tMhѱ= tx4 2/pS&>pQ=l0>ȳ5P =]ر3#%$N̽AW=8;^0䂁hJُD~x;.>&)IZMߞP%IcDC�4kE"P@(B`}{HW% #t@1wY*(UJ_o,&>]$dX m2\XJLfI pF =q`gtuMA�J P>/`Q�D@]FF ]Ed.UCv^] :W_LKOBA#]KE)ɎZ}4:vѤ ɬz^/ڨ};/Ɨd>:SAKxH'd3#gm3FWg g6();G^(ږ?x*/,VῊV"P@(E]8 � %/p] ȺM �GPo_cÓuA sEPAVA)`w"M'(;˺"Y`9}{_!~r TsH�.|;] %= ghя ߡ-蓞ɚ]_&;%3v %Чs#M2 8}vq=Ӎzt:?c!}+Jл C_"C! >M2P76Sҷ'{>V$}uK(E"P@T"Ǜs`/` 6>jAЅG#;�*`cxBNFq1h  S|Ɨc|=gukLғ6]W'/xM /,G/uϮOq?@H c;*x.<]ɮƋvDq {d[V/([^PaI)q>B ?ʋ6yv|}Ic`]Oۈvٽ1?S=~"P@(E6hq9 g�L)@xBN� [ueWv=q_QH __A 5UO�߫ yZrY4 ꀗ mUe2zIOЃ] ,| CurW}$\cMzR8ꛜ{xu]Y%{t%$�=X`s RhC{}I 48-۽}Ч='|- ?'ǴO{cev!DGᅎR@(E"PNs�P)�aǓmwhk^0@m'( oGGG/ AP(Xs%�%PG|.? h=}O<=7! kN y>' 'W@A9~r=&ß,,v`v$[}A�X$wg浑(ئW t�^P~ ]y"G| ql=f,('wcKUG?t I&s]Dװbsꓯ l)E"P@8 �@O.mr@P⚀C`#k9_WB_ -}m//7qlY]OQp"�@q@�)G�%Z7zva cj2'`-&H^dV_{%ice|/Ʊr �s]s%  4}dN8O] V- <Y]{2%O6Jfu%4["IG-؇N~郾ᄦ'J*]lߜ]p<`\gR@(E"PNOCIfNO}j, )}U7!?З%,IBnv:[M[]`O%~} .N;xj|;>q^{aoۇ~a&(]wuÿγk.ܥ<Cnkqן�mDN$ }M7 ;x#YſZPoE%maM%~4v%S:^Hs=/ mo=!%$4K&:/Ǐ*`,$ޔ&g?Ug\K>l?oqvmCdcki)E"P@(H� @^)`d{]QggRvN�D0#@vr 춡/g[�&8!xL# LV`-A#%xִ[�C^Aݺ:?A?h / `tc rF>Gu胬)>|hU} x9ݰz$  ?AW҃I<[lS>]j6|>ݡS܂_It74Ȉo^ gD' 3^BDxt܇!J2nOxWwOEp4Ȧ6zjx >FG愱<\@(E"P7{ف8p9A'!iwۣqA@C) 0 8hUl }t| ~sE?;"`AGGP>ѵL.  z K~VOOD8Mkؿ;?ÿ{Ya7}UCW$T~mq >}%>䋾`m6:i<'Ck##>2Ϯ<>HE;'сD:}ꟍ}? >a w>>?DzBv@xR'軧>Ó>6=)<#t_=8ve.>т?I�?vƔ </svG]t o8gvpY&["P@(E/|Ӓ\$�ց%s9uN>[4�~} Lw3+`DG%+U}kMx< ̜h/+ғғM} /, ֶ,Gd'QB|el{-|M?R$ =w@vu0'mK]zweM58M"P@(E"MqNO�!z}(ɷ}88rHkǺ_v}_pvWݴxчUAqWgǾ ؁,C}XE#m.;~2/8y#"ZY$zb[ӛk{wOEΛq2"P@(E++&ڇ(ij@!v-yL]GGMAVt_I-ǦemvvЍ´DMݴL-E"P@(E,!C=O QvO@MwDΰ`{>GE�iW\K"P@(E"Pv\M&�׹^wA_v.APE"P@(E#}q.x /3�/t'�8?+X"P@(E"PG &�νW"P@(E"P@_JKQ.E"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"P@�{@(E"P@(E=�8{:)GE"P@(E"PN _ӝ{?Oo߆cLW_wz׻.r?/.wůA~?A֔3?-{Ww^8`DO(c~6u{{qm/`'wݯH9=_=?2wT= c\'cPԘo@ˬ 0g}6nNs;j'E"pE pC'886 N�s_Bap>C9V[o'< <o_W~a{Xl|&c`nl8H/g=kHL|_I<\<!sNrߜKe̷d'?!A<e?|}Ix>'}K_Z|]!v> P%Co}kH׽W]uU�#d8+cT> . pG CepS kfA\,E+s�!g>3?_<v _^s˧?G?aI ?μ'�邺|;OO~򓒬�� �IDATt;EvwMA g.y8\?#x|zja߅Mk_| } b<AHϓ"nvOzo{Б'�-$J%~q C K ŀ.ޡu=~O_!yغ֔o}X[b_!AS:?k!ױCPE"pe"p�A{S�vQ_Wꫯ^pZp/x\?v= IvQ) 9s( $&|޶N=I@WUk�ʡ < z 8m%}s7:xӛ4?_o>*=ə'{E%/yBxi9s21|%L]ͨo_kϗ}sṞ2ߙr~!aDD]笗ch!ֱC+Es�y@|{]-WAW?v.Z?cO$s:Ie;T@Aǩ'3'c"` ] @Sbţ)`''EYl!>a[_}k8ܰs#c0>{I�Xtl˳.}'Eg`߹u;~sc:kIj.6H(vb Y6+wC$"-rؾ/"P@r8 <}nMG=j(>d`)IH Rѧfw�i"d#x/gRyc.NY(;GKzYw%_jޓ!!} 7-E_y|6;dE"PH�Qf Co=nt%nٰbCÁGªK]Ιv9h6x5x5 IFK=G'}Opǿ[$ 9w+VHxo?V;}{d{!kGǟ R2.v8ABӫ-Njy=r/I_ɞ$ h\c1M=<M_ 7sϦ|'WI)uw]2H~7ok Mv�Okwz/Isr-GpA;\/f7h7}f'ڡI7eoj!{Md1W6S^ FavdV1Gg'mBsd'o2%2eYA>ya<^Ѓ yaa6sxV<~ȧO:6 #6&ٵ J/dfdGWhmU\8 xs8#> qX/Z`G'7/`6Yȫ/Xk3 vE^|E~uN>y3})>"P@8 � b'=iX[B dqXo'8G<ϸ~4B|#٢nPѲ�y+_9K9q8=npWopoqDn91O}j+^񊋐ӑ�Ms?<O;'goսAto}u~ڒO{5}`=`TY*dx<\Lgg'd-MX/^x1%#B3g貁Ec9f&|{2FG?oF%E{ANkk^?`y OEl5O/JN_]w݀fx3W=4tƗ%džY!g {nRЄhgh@%/ w,q}ɛN"<dY8/b}sߩ]hy`.){yO=o6`e˼hkg)?Er8X7 1asy8:¦=Ð=wzƗ:9w]?aܲ[?ƆݏmGiy@30Zi<Z&Z<eMb+Ƃ9zЁY_UB 0v|)~㗜cl?[1Nd5lC@?s; !7$o71>+eS|"oE"p#p.� 8v87; E$*p 7,?r8mI9r9 q9M[NAp,:,�C�d8SǩD m @iQ-9`W3 ;+Ÿf0]!ܗ  s©ۧ9ǞG'A lPF/8f -.FIhp?rdMcYutGy{#\aJf6 1 虜г~!; _0l =C7n36b;h:M c?<7hk[�$ Ȍ'�A] CCBlPI V}˛#{A荾AVn>C}6;M]glÞN:寧3@Ǧ`K>&?z5W_tiޖP`+lS~3EƁ1n_3>r}65gs|Ogg~|6o;wl& tr1=yه 6lkΤ sҋu�/ll™\?Cn| B_S|'\o)EH�bye"xRd-҂KXw=i, l^X9B,!-oyp`-@] GOChqMc�HQW GC@җt9@&p.]8A+kcg,ф)|pЖcGƔ砺.\ K8կ^˿ฒ?ß3\" y7#@# Z\w/ŝf: '�y=@~ԅ98[0Bo>_)GEל[<d 8q7ky':􌶾R|&`3cQ3f{398x_BAp|`t=m+cp]V^'sXǞ ~%{qD=L|{Rѽ/٧hꏜƇ|hTqa?Ɛ]NПqz 7 s){,]dch:xm1иzًd^dl~ 6e]'o|iq];C}1|i ;ص֘OhЗ9< 5hn5Wb\aO5dO{3oK: ؅'cib.ײ}m -acx3gXs<c}oڸg,ZW?s_xf3:oSK%XǂO`{yms(EE=^84 PEhQđ`+,֜or>ggd&Nǀq Y9:獓>,8gřCaB3w+7|;8v\ gS K[8=R8ѝq8/N wؐM۸C?>8gV}rO4:R8E2'_X?Cag�ulkÎF|`Smm&(WؐoV.vds9չ~vVp^]cOgv9ƕ~9(g-OڱQo<ch `K}kN٤`ul /9.O7>N !D`oy0f\#w w}Gz>w\Ęd[}{p\Wy"%6<2/ aJ ٧c]:'!n.H^qB,6ߘpak^&0$>`.37v-i鞺I4e_0ό1 $]ͯqk&ƳkzEb&tMv`;p1S!�|UH@gfV@('"ptYh->s,ȜS"#p},Z0gSclFW˙5>9Y�;9shs8PsN>1d@rN F8'dqK89G.@;TΡCPOvn9t?'.ѱ _ч6sZ'zZ:p s]ġL1hm 8Ͳ]z<w%U ?cpUo"JkL't_|J@ /:5G{>+ttsa3N;%퀪;~;l1ئ7<ٶC@( T% )pt͚}޶db?g^u?~@wbo_6a ;7mXrw؄>l|fd48:cٜd΁_IP?l_�d3gҸ\b!/WgujG.z=x җ=39V3ƪ@(E|  h$Yp9D E: kpp9(=XCq|]'G(qB8#S'g,&>9AadqWO J{y'{s @ #/fsoJZ]-`x 6ǻv!{ӏ6lD`3+OTЁzlLc 8 ZEs?|g}Ŗ–P`޷u.9' 4n^ ⑾8ΘpLg6pFMs5<`o3vENXg<vfB|e v3~ؗz \k:Yesv>潁K鼈?moϕ>aـ*O$A<cw7G;gK7^ƭy]ÏL (ds͛䘖'}L1czY%z‡qk&93OEؚuqIhxi/|x{(E\4prR@v9I`baxގ];?8| Ss[9@zA|v] $; Nܿۿ e O5uu9OEϜ$8;|޵hKnfx8dw$O}4rp<iO!H9۱ӇiI6Fkbv7*#`qXQt6}\\!7e#lq95z5.ӎpϘ>W׏8?\drN ι1%Xv>|w-o`3>8J7w{}{szd΋pf[Λbɑ 85wvINb69Q/:<.O_W6Jed[)EGK HBl[dӿ3ރ)d$3, `6>$9| v6C _="PAW#JIf`rJNJ"79OJeN<λEZ`'X0X�tqTuTˁK@U׆ I\o0GE8 |m`uFOp89`:|^UTmLa0Svt>8١: MA6#'n/zgR$ W،j7] 9N }?M#<UcS\  $،i!ޢ<W6)hž'^glPc~lbO.쌹)p?6%Wt'|0EA&x':Ic?9Ve: zXg"y,X^AV8s(tj|3=/vݧkcRu{ںص=v<.܋}5RL>ލ6SR=�Ǫu 1w|91.y~"F&']n�_ N="PAL�X@9DZ`!gse-#} !;$,oο"yѐ Y9yXM`tYavC  $0ϓp~FMi <Nϱ %`8A~O+D3N֘?[Ag^2?z7Xi! #Ҭ?oĖ`tA~i 8?W`o.Sٖcofl~NJg6x3u銽ӛ]JN&?c} ةh6OG &B>Ž`)vI/l^d1v9yW~%˼ _[5>Ђ<-Jnc`v >I#F2d}ʻN/7bѻ*K9!zqX5[v ^y̘c؊{?9aꮲmum} ?vlr\i->y? ~L1k1si^Givd>wk0CGc]4umaoS|&gW"P�&p,N�p, >-ve,8n8Gz$\Y@;@ -?K�\8#dY\ch 3NJ)~ׂ2C1',1G]; �,wM;Y?#0.g|_;p0׸[w.uy.l`oG`>ۇCPɾ .B~B#9D�{M c7l=d`WHe/ڰ)|kD_mepß@wu~_faɘJR9˜F[9vn 6,d]Ld=m`.WtK1X5^엎yQi.e_Ʃ>`9Ͼ?<̮%=:~{c)lـq lബv9c:F%/}͝3mv9ꩣ_vC�ucR7y؏}V ^+E+2Ad:v9XK>EjAXsDs�y+GA㜜�ͩWR2!-Hw3!<p(] >8H#qzo}[Ȩ:B^8x0L9Ξ>8E=_`M<Af~S ؠ_©mJ9|q귧Otg{D\QO-(ߵ7HgEvM5\3G'x46> Z]>80gN`ާr2Ox‚ eG}=�h[g)=}6%`K's٣_ܽ@Î?̍M2<L O&X! S9vn.076v]vPO2i..l: ݚ%N@b{nlJh=Mqm�� �IDATCr*3X"7&@k1.Wӓǹf>c⇦2Y:n ~StH\6o0n}7|w(|g6n1>pOJ|p"P@|8 � G8,QSl {# E#8x?}<9_ /匹g.'}#`PK2@p @L[!KdƗ <ǁp G4'IAZ lOF I2 uɈ?zhKg; K.Fm|Ul9Ao\?zG g0~Ç67蟬)>&BW'l6;_0߅o`3z{pp|Fxag;#e'lr\^ЀS+^7ԡs}<r6J1~T:Uة;F#lƺMLN`l ϰV-A왭:@6vɦIngS^L ^D_o4ȳ.2VcڰI=׹2t^$xtg'vpYGٷ?v2N ت ozAL)GxegstNl}Y@ּH*JΚ?cF` ~ԆmMױkO[~z'(GN#(mVlx>2|_@@(E\! dNAUԮs,X)bmT,BECA٤p<YE3�C}<Y9oGi? vA)(XUN(9Ȩoj1/n 99$o#q,?`Mr;6~u'hZwN2>9'q$Qo:79ȥ >#3Lw`Ƈ>`:ǎNR?˃=WL6orsɠ6|v 2€F7pP̛ :gwy,n`13\8tP^ٕW)q,>7~A 萞bGŃz]h WAسlR{|?2b[fΠEV|2w]s–l.ә 7]_x0$e>Dx]Ac;2.чN2ѱ eżgN;#q? $sv4AX^g3GG =>$HI^6CV g?soLf>kÖCoc}l]02zl #ķ$<z +;Tjh>?uo>"PuRs�n۷Eh1`3?`r`)ƠB9Ж1㼠;.x,P27ngK&P# ni|OoR_9< Nٷ:<,; z]i{pe?r,+RV_k]K9cw$4430gϻǾ3,̍`u(yC] vEK= ML$ԣgNN]l?^ֵm2g^0~|Mc$6eW2^7G{uL]~/sޗͼ{|kE"ۮMQ__ ǁSX�s sÜ�'AI06z94 V%R?g<jM G/~~m~No%_ ѵU ~*A]ةv:htV٧plbhGO;<c@Ox."SʥAuV2Ə:gs=^v]I߫;co zt<c}nJ'v/:> =>~1˾E"w8nhnW*'hgqj)E"P@(E"p [tW@(E"P@(E C +L"P@(E"PL�2^@(E"P@(E C +L"P@(E"PL�2^@(E"P@(E C +L"P@(E"PL�2^@(E"P@(E CW?_wwq?-~_/;΋;Nku.f,㏜o.KM?d~O_Ȳԃwroq׻u/?y_j)ĸ'D?q p3>Xa^=ﹸ]2^{ 0׸y4eǼ;q?~3Wo;Jl|6gnmߍaRuX"P@8,M�߽R<)?{k))?яӺq&?y'… K#ȵ6UaI`\sQz%8|+C~a{ش˝KH)/s!`7q}sa+"TK_`/}smzXzx0sI˳AYas}1nu{ d n3ao?7_�k] 67M;$6_;%ͭs*`ҧu|hvn)E"p4ph/;7w`xAz�qvq8~גpش1< e?0'G?dr򐇜gys : .غۇ3:{C.zޓپg?1ntXC";Wl۸?=jrkI㗿 ۱<OSѧs[ր-A` uOd|_9?#>$۬+v#xߦ|3Ú{3WJ�_u?07}`}"P@�p8;A:Kĵdp+oq\p* f'm>~^o7M`lg]&XkU Mcgm&(aGOp܃<þ�ƀqA;%{$:^4JOQ}ի^5<M]e?LcxDxm珹NrX曇fg^R6O6lXda32JPC\PReR~@(E!eS9oN6RD0agI*2s 6Is-%̣�N4^^L^Y;t*b#4 x_Evّ Ҏ9pnn[pؖκy]iu;h#Dnz SG@eV'ۗi6Տ{x2nna,d}kmO}Hbs~Mro:=yQ9|d۷g\qsa\@(E`W�KΣ~̉<eP˪KW]u`r9b9*v A N1et� Iٙ<+OLu{0''|`YK" M‰}ym]vފ9屏}t'ߕ7}V"PH�ۉe׃Sp$8Y;y> 5^[�};qVvhv<&ц}T<vQW;drh*Rg,xkՁߋC]n{ ,`hKB;Q{p"B7.d/'}YAO>\L>k9xө]9 /@?\qpQ_/6yU.0Jdhd9٥G}gq]حz;ȣ?򐋎\CxMAChw}G&_HH퍇9즅2X5ɩ;<Fa*P-frϵ3)1%9Csw6o~u+:S>J[@k"[A2ײcMĮ3g]^yp##s* %aOd뇌WVg#c$Ge梌'<eE;kȅ㟓xdOd }wm3]>ԿC}l;1x &h}{l06W;Zt?Ձu-E"P ڣL0 `賟찘bmE߬[k"X%i>sH,ry]jwꫯ^x; 9|Ǣ G4p6?a[5Gu>+R)� 0>&b +\DEMd ls~ @(?.7ݻwfUR޻jZZU{nvjkmz"H}VHM҈$!W-BVz tE؊h˞og #Abt֗~}!G<J 6FT+pfE]:t.6 $l'=içIҏ !ic;{ >FԯG'[$b_?'{lS[=PÙwѰ qLr +vKw.k[{q{{`9)/+?E7q(Nő:W_}us 6"W~?2ďxPO<b^C!6<$Ȣ+ ńo6FcuOXgڈ qbEܛ#~6y:=3m2ψ}/`я~9`7=N|m 4;.qe@i"b;}(y OO?H,3XGs5״K:cO%{ %xSKeW<o?t� YS$qs"}!ރGwK:|qIɀYCvLf_7">s /F@!P8 �ҀK6! +a#%nnˆt H%E^kr7Zײ ա yHA%0l7w1lI<RAG7p7uk= ?ȗ;䨏Z oBSqGuG$\Wwms0�%D d.HL!1MtnNBGTq-M}3p8 m.KD݁YS!zk O|X̉?m1O/_%QlaI-}L>&ƆW+֏#18Bc8΍Iz$&Ɇ$XKgqaP%$ mcDE&$|o>g`$Y$x|X%^(cE}@c8!tޜm.^9`cKx\\YՇ1�B>Ŀ9`>L1g<OlϘۜ[:Б~&k>Cxp%rݷŬ{~]7/',oi!ߜ'V6wo##,m0 ' R|~b<xuEFr̎{Nal>׏C|}3'TB( 8;%v#bnp;7O;+ez7EUn @l%ںa;"f n?237]NR D;rO5q)'K< 6F)Ē<J+v QH!K<}!km�@lAv;{2-gYԸKAވjCտmJr3>d v!^>71/Kl~?XP5N8 "om:ؑFa~JXbI9{.aO~{{z~t7H$]w]ÔKLHa^rQM"nGՎ%_dOEKJn7azv$#Sߑcvc8Ә.䒗q7RՏs0;[/z`=fyHl{<Dx[29>$,ٌ[>{%$6J 1 .ٸJC̈3XbLBc3!R_r8v8V+#iCgv9NcX[OkŖ5+z{ڀc%[,;? ƆX fMgӹ?~0ﮙ?lˏ}cOɟL=Ir믿}A"?TW_=\nnsԿOK #"o.1st s.\\ړ{ƌ*xx7'C'1,}>1 #1P( Bz I2F3R !sCtVM֍atG,T1N!~�aA$D%nэMDXG\7zA#n"CȳF'|!StGt=ޓKwmɄJvG =/;0A]:ΈHT ě,$& :;@^[b@ᓝ!k2%cIC:̷b�qeGR3c;:(}n%~Dv@kKߐ-,63_F| NkZ"&`.Gbڋ6[ pN1.la  >yE?) GŴ$W$p0 L~b=v16%<C9 PysRx_~vls(ċqN/ㅬc׆XLXDZoK<o3:XcƲø귏+Ŀc us'fs'{Rlҽ?!vfkҜFDL{{ٜOcXOd~cE,7׈I5|m^%Jק8䗹oOy|qE>oh̝c,]e~=1>=}^@!pk` )apEʼn7t 7vX=K>@ɕ`"d ItVBqD7e'sEK!y H>!bQGvBZGCcQn }ؔa>6 \I BߎN/XaCG4$[=-)DxĿl_1*t/bJW gtGaJQ~wrH:A]")Fz-z#l@77?|şk?'b|IV9[}=qoCafhN?ry>Ecv`eo'[1𭱣K)ci,dW?8S8?mvlM|KJ[q% #oͽU1¶9{8tƪy6ƱoIpru-v5s9Y Wufu+kw_%Lp&:8f˽= _.�� �IDAT˜*}s+1^šTu( BAT/�f]F|%ipNbQPyqsutFqsFLG,5 ڸN6*9nvJRdB n^@yIw6K) ErK&R9,%8uة-&8 =XM&aE"dg$!$5TR?I.bJ?z"XKJlB\:a0/ѕpgkr"&nMĉ>C0a ^\X$ ɕ"A 8'`S0BOxI}o}Q@m# A9f_&Ʈa3«V $fOء9�6pַc689L Đbfvŕ+3nⒿV?tǼqY:!sě|nx(l1q8a_{IY+3~F#%^I>S1CR\AĞ�vD6x<E/\}#_{*՟B( S�RFp87ؐ6n^SqD.#!n) 0qMߎ�#CH)smH0oO"rAh@ض}I?Υ>@{? g,->>C裟U_鷷) -Y[Uohl yuWI3 #o{cJ|9?.�d}o򿶌ŏSW8O>ʱyH_|>!Mԡ_;}ad'颛v9rFoW>$H>_zmlJƊٞ[fWBEj1.}'?K�1˟9ΑmG|Ֆ~}qbkauPx c(d.gYt5w ΂̵n*$;}uVݩyY B(.\'79v$/H)"V%ڸ[E֗Ρ6VK7\$2!%nں-u|u2#Hv)޻~s~~A' ;OG8FbƐӷ{}ȹW5$*6Sv/%b:߻/%yFbA~>+؉3XPBMu;'a1ԳL؟pA*^t%lW:#�?vw?27}Β)Ū</q_p{OƹyX8=y<$N3mO~1?E\6K/3՛?c8NJJ}\I0,y"dj +?:1;2g.XؤwSkx?6nszƉ!3>an\A~࠭W}Wx9$͹|wW"w_ceqYBb9 Y�)n\Kɸp>}?r( BET/� l)LxE"| D,Awٻ#nHkȐHn 1p I^R;I}Nd7s;tё=H,HzJF=}HU|8?kv^-m71VbBd~֯CӏY\@>[bN,(|>Ipׯ\a왓1u]ENDdz@`3MI<VƖ5 KK]PY ca8Uqֶ󐸑Tl;-O72/U\)цCYw86+^*"$W~%?/?Z&c{90Pg̞5fs4fg[<Z^́i0W$^dū$fɵcdLopW׼ůs"{y|I?pop_?5Ġc,A$ּ[�?鎛}}z- B8;lOnnnnȟ uuY⎔놎"nԈ§>#8ɥvn B&�(7y|#!X ,sIC=KҥC~GC_xA@LmI/ȴ6EAa\KV`Vӯ$A CyL?OL y~O}8boာ6C{~u")\T _'~=/{"Hx`?‰Mp9L YlHLxxO#Mg+ɭH8cz$bi<V?04>+$ bXG{'LiÒql~;7r>oWSra .&~ȗTyx6 \a37Ɯy975;{`hc_0p{qd\9t&kʽƽ}>:fqc~GGckW?w0}WzB( ^�{ݻZ ' -pFZ$:K CwCA7!S($Gɿtæ ޶%SSja mD<El tb�xNzbEuw?6,|dg6Gy5v"o _|s G9Y ,3I�|R}IY6_ !"X,|-u]sBqIw6/lz5l/{E'+1<LՃ>5%1iu<Wy)pA+fao,gfS1I?pq8,ʂO?g2C9s_7WbY\mw}Bs=-NahL;c:0[?a~2aro6?ȑ|h0G0ut/:y Ͻwz%Dwz㱛|8ss$?cLáwU-FQww) B89�7U7W Rظ"#Ȕ;)AM>Sߊ}Ddd@2< և>C|OԮ>n bHF6#>;N:$[Đ`K6AG 臘F}u\s)!2lC¢ Y r} _aIz?KI�~b1N젿Ǔh1S)Ip# w&?`Ko-(ۡtϿ;c~`sj4;эV?OI1I:'n dWS?O_'Wb_p'59A\G%j)zC}NyA1cGo;|4W) _!8ioѫ>w'VJ/fg~ nqF87>Wr-cm77eo3?6|5'JȢ|% #lIhj^`}9N }3n?f̝n}[̱Xn!ȸ1_hüODAoxWvz&ƓK&H t#8|cPʇ'^W\pq-~iB[=}jG>{ɒ~S@!P[r.Z"K8)7X7lMMڍpﰋew8DN;aȂIG>܄ȹ܈`wS Hߴ msGxĄ_uX[bcW>9__]_n#!0"Quq>MdFX%~+uĎLtFF鯭dV]~OE??JKX@`?'L}z!tGqmy!>6~-iw DC񍒸Wc1C~1[tLlO?O\0#Sp]A?uGp&1Vb8GWz苟'�=muT܂ԸǭK2=c,}^/bn4>/k#ڰXt?\3oO3W~dO:)a/%xGh_Lxa5>"ܛ`mQI I?nAcKoҏ /iPTP>9 G2;gL:qqlK]7J܋̅b}߂IB( w=.@B$ܠ1cM {u $#7Su=j ]^"!n"57c$vݴ;׾3ȘhS"G]CZ^_ qO>ڰ>>E)QdvwdOirm/ԥkJV8 ;0>/YTSK6sJO+%0T4%o|~-ɯѓ58%|Ny?_{dX;dm:&{9c>X+/Оu~͸$ 3_x&"sns}%KmS[lKѮtk@!Pսr]޼D|S!@n" Ț-mԗy^RayO6JI tNK*RK<v%-Iw^H+ҟ>N?kSLܼFf눨8Wv'>Pi ུzOwJKݳa5_t} fO1wnlJjw\?EN8xMokk^Sڸ0*+6')|έyݕ}F9aa\d^y/6]ڳt-S}S>Gqߕ%( B#< 9jB:[jw^ۏ�m*v.$nԒɎ2!I;>MG^=&\8J|Ư跦;s1�#d#|Iݖ_[/aV}>97= /v~^qynΛ,66cxXb#ouac?.})BQ@!p2No�5~}"@~w7 w3>;(] u� "lB},^ɮC (ƒV B(*}WEϪˮB8X=�t@i�7;Kϵ$sa! �~$[( B(N7~f{~OKB8NI/1I]u#zTMNkG6XrS�a/_П]|(\_) B( !PaY B8nj,�wPT@!P@!P@!Pg�p>{>) B( B( B( 3@-�9A@!P@!P@!Po"P �I) B( B( B(_OaϏE?;~ϏQ@;8?GxMGY׏1zp~~j~=?(}Wg7_88b/>tɿ7 ^7Lz}\x ]󣙎R].Ӌ?a~i*S8gE\%?,j n|\hGlĹxsq/9ɷZ?3Oޥc!P@!p$?/4UWܹs5ҫ_z"o}[^'1ZS.'1OxKC~ȟŚ|3_@"=d_K^Kt8g?ے7M{/}Kno|w[-�1,旿e7}|}]???hA %JŒz%o{ڿ/2Oλ^�_Řg�KbqΫCl_|pWﷱwk'^,J?OS!P@!p>8 �d'?ޗF 77"+Cp%q$gm EzL';mC1]Hя~~DA`ង|Ɏ/I{-QL>}K/{k^sO#5<I"Aaa~-oy˜yq]\YP!Wߨܝ8/a:Q_ַk_ڶ0Oq}-Y(z^BQrT2pu@l?ؓ?Hj-l*W7t`/ad!.֒e%yP B( 3�! ͣ;A}O\r%m'S ko>v%}2ܓ{dyN?7ْJ Gpk+E;ttXQ$$y@IoωO6Ǟ??]�H<C%B?v]ҿņ~m_eE=wQˋޱg[? ')� mZ͕95Xϵ;{n`Aoeyx^ B( _�@B%"skA@R 9CXC;ozHdj֖Z=wUae'YIn oA�6WU;s$nړ)+էz+jcG�� bɂ|-Q-y,<)O}sE)a0Aνz<8Q@_n} A|+y:4&hS8O> (q% 7s_*OL{!-B[_Wd޺1+ B(.lk%p{aCWi)]8K>:;l~ʒL_p_W暵L֗LOxW0aQҞ2HuG' hA.W1~I(bD<>i+S8;KiWI $l5?/U) B( B$"pXvb%;Vv0v<\$nکE$ٵΗd F'#j>yYT|ЩBF:yC&+"Sa7 9+tAUǑ]$q`K2>I6l3fwZd.q".챣+6b/]/ǖ/[r~f`q{B' �IO:+b-lČB.J=-.ܝc:uwMHLK=J?O2xշsAlǾ%͘_Ť:by-6->>YS l{밊ւ ē~}l~і^H']|/k' CgtSoͼ;2cCMze~k Y| naK/})|%nc7�dܴQ`N XV .Wo:W㏮y\_5 xE=s`>/ƪv"/ȃH]M㋞ FrM_bxMޫv^_J?(3}:G/ ]:}q-[kM2,)�� �IDAT@!P�("=FՓ4 x.DlD wk/#2ȈJcWdA^q{o|</| z!#ذ\v0yJ?я I$%~{�b$*=nF7;O>d{,,d yOv..0 9N+o^_o&;( N ]ŋAw_]S5m<.첖3CyU _ a?9O@[n%w QygZ|"dI /}~+u]~032}om#|Hwp -AЇZyw4>O5]՟pr]|<-nf/y(~p:7%02a+ħb=7xck敌Y 8qŇ_sz; <}ӟn>F]~CzG�3ǚ_U|ń\؈csY oȗh4҅9~zͷ6)ӓ t$ޣ>O~$>O4ȅ9R4!$B6͛_V\;9߰YorHQo$`4sT?u-~q"65^#=X{n"qc6׹B( BBD`3< H Bh YvoGqGRGi !TpȮ>EԐs&] 'ҫoɇ$m;DUO)AՓAng")tN"L(9r:ȫ!^ 9p@_U<yIk <N'2yS|TI?\З`#>0My:+WtlJ +mh?.1rO@iCnĈXqNħïOc~{:ө {Whf=q>x.=w^fQX`Ow:Ht~ ;I#xt7~\$bVkʼY6<?1F/TOK8{U ;&㜃l1ĶskEҜ+qFo1[Db&@W܂M8/00,^dҍ}줇6S&+O?tU_lv?Ɔ pSH$ӏ Mg4Oy{8ؐy YX[@!PfxAX"dn IX\GRC$4!#gvlȱhAGA$l>Cb/F v !6B m?yAf2H}!77A\Éve*Y @z衇Zyz[x| Izb.v+!F>sF '$%qzIs!,d`fY&=% !E|\6]PqhxC,0O߉kcb).\ vŮ>ħ, 0ꪫZ|vMЯMs8_b<I,3 S"Mpb;c\7.]':ooOA#xDG,y_KeL5ywv2yԗ: $nb~bp^qt+03|z=i׋q89زl7[7' RMKƂymbN8O]g?t^$SHG'5O)Wvb 3N4,͓ xzJkqk.\O{➌yday]6Iקy6GK,k87&~O:_\lǓrb,ؒ W}Bf[ߙ[2g{FB( Bx!z�a@x$2$B#Ql"pv>LAXJ )2I \<2!ۉ?rz!ي.^]JҐNĊt!BןM$ % xI;;#H:#SK:5<>"zh8$1b;BH>cuq6Jp%b,+[v0ᄬcL6KD`j<, pXs+\L_G~A]%*m9ЯJ"7?}}!/nm Mc)kvfK<f<�;*/6줟5o�^q'eswK77 )+iӘC^/I9|y.%y7'X#Y:Xh\B9kE}dlёޒes*pg}+B85a^&KN?=y83W9?6Ĵxu1o kH{G{/׸ڦ_lx`1dT|MJr]r]M[->z- B( _!|V| A8@$ZHRhڅ@Ʀ @+ ڹA!l`I.,9ABԧٗ32$QF ɑ}r4W@W& ᦿψiECa9$ƫ~-QH!e3jWׯk-0]|gM ?bĮ b^NE6Dn]x8#/$|{Uğ8+%ȵ#9 ؒ-Jǩ2W!*Qb21OXJv %NlwƗxvH<qn_kLG?}]N/vG/|/ilaSU.c]*M;l* %GbTkž_r,V菟c#%|/M왿!~4vҁّ9KsC/׽cW:5m|34[% Uc/9b T{Wcϒ~}-,4d"K +-_ B( ^�@f((IRrD"Iۦ!LG !r!#,U'GԐ7,#H$ lAvD!S>Jz^Bw}]+;ȧdхl@\n!m͜S%c1.1D%Srv/wb/IdAqZ4v`+P$ ~޻y}J> 0'.-"y_dKŮ2W6'&cƢ8d^B7zwbm/~`;!1{83؈kk)^3m+OE֘^pĽĽWqlޤ?}3739pwtf^4s?*l~\alo1Ѽy6t8~z+`sot#XLQ0Λs]Sp4vUydkOt>/sp2lV@!Ps�7dA$$'9ٱ)QJ}dǎ5gg˫xrB/G^dyEJu >ȝᄏRqEZL Avؠ_zI�pI>"yc3Hy=YXqMҍt\Vv|w4OZd'1znϰ2i |/ ʇ+a,n,Nؙ^a'իs.)K; O ? pOƒXz.SEp|G'Nd !Su7זGCq갽߸/NFG1&F)q^s}l򛶱5oS"׼#6%|̋ާw ?]a!H< !_23vɫ+C09/:eVg"K}sDj\NK_x}J->`2g<�dw9;oEn@!Px>;< X.t!@$R8xXI"g!I${Ȣ~Pu\CT1ήBM 6!a}-�H"rX|<Jlpxݕ)y=v!dF06xgI}?/n߱zi>-X~ aِhkء58Ƀ#; C FcsiVSK0ɦ:6̫vl*lN{AtCCOz0O$VʞSd1#H tx-C![pA'6Ŭ~6*L^?'-KZ*~k|:I;|6'd\alϵ ΋g8ϙ1 >nuƲX[!#W/ĻcцtvV/z>XA!/G=A^+lܟő)`yˢ<[dZ"^ B( BW˄NjHND=dBxT:V$ڜ;?,?ȺO򾿿߾oA$wHJoa@cOsmA&KR_.Ώ!~]luǒ1{{EL1zQ\~�Bz3"DGmلi#q!c%׵aW~¯"|D?vYOm~24'O-I1À~Lxv]M`Lr:vIJ3ۯh'G^ϩs;dOO"~o|ٷHbFWdIcpXL&ӗ~ĔWWq(^[̌%]lW@9WI)IŊx/8lb98y?5d]ϋbC+S~oń`"vĖŮyaޑgɬ8U׸Y+bcpCBl{)c9[}( B8k� I$DBsJ ٹ|KC$$ȼDٕpJQN'Q#9tmXRuCAhD #>%y uTNt ^C-D V.\!xJX[~|ӷ/O#t]S=.DC<%%IR�-L{źvlIR3fd^}_‚Pe1d\?=>l#,Ƶ$?;ڰ>{Šskbkc~` \Ŕcaț{+t׏:;K3vł-~gh/kEJ߹yl87vY[xMIKq&V7 gy9ǘcŖ;kxe{E,\y ~v2s8o[mXR@!P#p� 7xɂG>7~rL jk+]] vvJ <H#t$uvH!!5H_s="A#!J(uDQ[?o;=zWK/ɓ:HGb婁9hsO$J%iD}]wH>rv. N#IWxY`_!Hb\\8ELMa:{/!$@ᠻ'k.0 ϸY^L6H,PIN c�C `Wq~k'a ؐfYS`K'>mGSʚ>.iM /a7 $'΋9v <>h*wBwW=3m Y\=u,�Y+p?ƹ]4Y`7Ƅ026y,1ކf-Iz]w][:kؖ=@!Pa8 �H�(9KVB I@d*A^x/P$;\Ȥ6?œCBjGSl')VW&pg hcw I"=@d) #_A.89<z $puHN]X&d{O߾&F[c_]'E%֖}#^K vxO'f& �Db+>RFؕJ&Nā^N]_Ôt@x$Wկb$1Nl+_/eDXƜz!1CCxI@5\y/Ă}XH攌 {ba-~Wm`o?\Rw.|}{+p4Du gKM[+{q++oƾgt/yIK:)ڊ7 [qN_KE'SW`2.-K[\]65/{+R7 lͼx C;83u`ow ƪz2OT7q:(S &o,6c{qk.3μE",بT) B( ">;.n)+^kDQ@. $A z$yP.AtX�@tC2$PaAq]]dO[}O"NuП]<}D5XiG_^"1?2 P@e5ؐ'% mc`—]IR> ~@~ KOØ 6zu^)ZR_Oyvx  [u0k 8RqņϏ!/8Yf?|�+ūbEl#}o-"[O7i=oOsLc~_?>dI'=E3Č pF]:pN= zt `Wb56$~wv7G$N +Pyuc}W2f[˼ct䷌79zНΒlK%>UZiȚy=dUE%wm0bq ^1/q&'<ټ'63[ԼidGbG*;2$ۗ~#Ucp;^^g96eN=S;M|™*@!PIB|3��(AĐv$hPU{r H{ +>]/ߐ@I>#9?$HXB|:>0 Tbv!cߵӆXs#ŵcqxgYS yq:VWtR_]:o*W%!aI47(dWvddƿ8]3EY\hm|?_Sz'~iTܓ;!eJ0ؤsƱyu>M:ֵB( B`;t�`x,0́9}1G ʶ"IJ.vDvpǹ9w!plяt2x$FħJw3&(ix<VM%ɞ`8kyn*G7eMz O֟C97~%>SvwhOxǿs{~+cKeJ-Gq7qV>Xi1|[geG!PA8&k&CZ!PG{�`Ac~kpC,/ B(�/Y!P@!p<I8e }J!P@!P�p!xl, BGc~Oc%R@!P@}BrX AsǻS@!P sjeh!P@!P(m�� �IDAT@!PD|/�sw!P@!P@!P@!pL1]@!P@!P@!PZ�8W߅@!P@!P@!P1!P>n;kG_k4v%ߓ/~_o?[[{'ges_%rz=[68Xbۏ͕2hぽ1%c٢yG _/~w6oMy/yK| ܧp^p3>cT) B( T"ý/| H^uUZ%PCb8 �|3^|{~ < �HWնdIV"oo{O?tǷm{|+~~oyk^ $GƈMZt^Lk,{=Ʌ$G{7;-�/);0~;bܹs-]'/y@!P �p=O~/}%o~O�҇}[jd;[~+_i^YSz>,槗-%[1 v6ɑ|+^W q//mB2$~q}J?[y Ƹ8eRm >ɇ-;3? ?°zb 5pyva ; ||(U B( B`;j`;ND+d 0;i �v~NkaC~nW#.ve6 q{ȸ,;ov^t# V%)q]kOC!dK[,{ӛtb�WU`ùZ*v<KS.o~œ'KqS8z0.,?7pC#OT) B( p;B=I˳XJA?璫,vH !$GTz"``!UN7u[�: C|q+bɂVKzmF?O+qs֋^Lv5/ = v-C.̕d wW%,X@`fPB( B`=�jQsEKb%ZyF_!^H {}U㵯}K_Җ֣.p^מ8 WK-aX@R@!P!p�;fv# ȕ;HΠvfL]3bG16># dkgvskW.BK_ mԳ3cW/@]zt_;#_OVTax(EdG>q??F<<GV?EA>$cIc[;vho*0ώ~A7=}BH/y?A:'zYd Cŋ4bO|~unN!|ěxO86?O?vq_=Ƨ6c7}0W,l* y]u2:lO|$^>;)c_zU(S8|"ԏz}a~lx ᓾ`..޳]ЍMk;'|)Σ}>Eۆ22,omcGͦc󒹉y ݵI܊'g8kÙGO2{܍#z'`?p+18gbbM\8J!P@!PGTAȐj�B|I9}ng;w+2.h.k?B2# D$d{~ۏv!~\x#IǥD馛Z_>hn#B H"ª/:m!/RE@o;&&`SO=ppN_A]t?cW\~P h~k_k>@_)9'~衇￿w<d3d.}o{'${gDȺkڮ$ ٻ>GH#}h[ߋo~1|SGy%VɜBoߧwbakq]aJH]zWYӋ~W?R7oxַ֎O>: ~e<-3oDÓu\GzJb_b7II_nf*c▘b9C[x=;Q1Ġtҗ~Mgqqk{1/ԡ&nԟ@w~2~藅'g1c2?noϦ? KfȟMҟ/sɇOrK�/qeLdn{p2v@\?[ܛ&-P\}-΍~y܂x+%'xŜ<7/čJ!P@!PGT/� E ""#@h$#h"I:D$ HHWH7/@:K;8!h$5d C7".1I]D<;rq?b<ABKnOAx%ѳH� #MlGN^a`Bu;vLҙKHL~Y�B$ա[{' Q"s~q#V<V_'<[/F#i Ka7`~k->7xWtsl,!z+6Z$Shv]LKƳqgi`@:`?LĄҟJBE=Ŭ8![8duuSH"iLI?bѫyMdNGMJtgS5~ccLzgW&6KsĺGOs2oqeŸΎ>Ýl2oUtTO'tY[N,bf_䵿V B( B`z B _IB"ۑB!IT| H0"j8DC[opߒl "K(z\~m.kg[2$1k�KBI IY;؉N{dw!d뉛"xH>ذ qS#pK\R'X ~{@Il:N?ђLKX<a'GB_'H<1%=XN>X'# ڒ7=$Jg1=!mΙQ̋ ~K3DO`ɯ=>⋟>~ׇ; zAv)xVB윅87nŢƹOȘy_D8W$_60gѕ}V?WbBo<kBlĐW?v]=_lƠBaI_ڈ[ 1Dw 'sV̉ӝ~tķMrscNiϛ%~$K,a 㝾&Ͻ(OF_$2߫ {b?\w;Sm%y{1]UWo΍s{w62v'q�C>R@!P!p�Ib<#Y!I4u dٙAx#F 1*Q@&sd9G:=!t@%")I/qb b&$3=$䑃L GYTtt=H G=oGOrL&RjK2G[%IbfHXM-� <Z<gxUZ6mv#om $­.ݐxHCc<:9r' !.f%Ē8"e8;lxi[oy[A"!>$ wɵ8h%iL޳N1vŷN'{ �rj|cO~0>-�tq%k#l~%l2F)$t1|{]~Nr|e bItf1"] s"Y&C7Op_=|`ӕdZt~>+|HV l;Kn̛=}g 2e^o0N#5WcGbW=|_[2w}8N1'x~~cE!f̭30CZ( B(Gq{%!H 2*B\GpA{BxX$C}CVr%-)ۉsN x <'Cեd+]=;A9\g'Oն!Zd. B %tB~ITlycJ$' g;Бo5M7F[zI$+ދO!}6nٍL[`Bŏ5 =%tO-c %Z0@lb.^$Hv좇|g{W+|`nWłJ8|V_:ąC zr'99Al?Ćߙ+S,bXPYNNbګ?+E~c\8&k^~f?n?HgqM?Y�0 2̣lGƀ1gbn<y~^KqF:!0}Ȣ؁00>|ǟɇO㵋't?,ǒqn[B( B-gb�qE;HBh;C^0 \ @H)KZ/9 YIk#Br,@'7$N,}Em|֗ b\R?l EF9{Ô~E dÂpOU {:y:+lwz"7dK?Sࡏ$Od[ (K~l Y'G|`,L!= ]^z,na8g)H7>词GX{;7j:ٛƧĮPea$'^Gz$2dlpBJ}?u@S[}\2%:\?1(>`x_nn3bYҏr;KZ[oL:ZL`gQeNpNЏa.xG}o>׷ K"<dqS@!P8 � (K|v ,I\CHy!ESGd{W;Gp[PD<F;2J/HD_J2=M.vӍ^,L&J|5~a K}_7]Y:W>)qMRigbwvj !hio>.WƟ^,xOˌ?I^Űn_8>Clvyq4ذ5xMu~|)f_Yׯy^WHrupQ%^_b/ۂ_}{/9(âNOpp\6A'}z|×3*lf{꾟۾-[(e _E7^{x7}}h+$}{>kX;%~|H0B( BfO9-"Rk;(vA(Cz.iȦv<NGA^#::|HN+AC5:I85]F?m-I DⳢ bhז>vؓC=5;H}e3ÚB$=iǮTq & p9ĔNӝ)lw_}<<+:a5L'i[^mMdwDMvn &>*+} K^OxX,7<Mb/96>9|s?F|Cؐ⽾\ϫkW41lq>}� cpSd3G/s=Y;r}cxde͸ ޮ_AD7B>,G<\C3C=9OԼ44mćذ%_JGcW`G"-K cV4Ia$V~z}KO֎ȩB( B 벱ylRICl=,9B\$#$ G 4Kw}?VE!]HB.c1KpMbďNv|N!gW&}-—D^_d9O|cw[G ۏ4Ivz o>c_'aHDbi^6kp5 ?eVo#0/&ȏC`Pg8Iঢ়lK< G=n?kNFbDIq_qGkG"AwI$c?3f C}O>ĜأH4I֎ă\@w)sEKKޓIG6;=>ϣKd<8ݟMqCo1'bę6l8ڤ_~#toƔ"$΋\angNusOt3<ťyZ0Ga렳6E<w;#|m2^9e bALbg8ƶjS@!Pl坨+d?H#d'SIΏ!AψRZ�@[$n ""D%ȥH"dK3u,dx =H ,RDDB>$yL2ovOF? ]苘F>HEG͢L#}ӻdKan_Z$ ] e@D圤lQ7CG |G7/~v'n )WL=!?]Ǧx&ԣ~}b^"dw6񥺻L/, ; NWu.x ;> i3OOx 'WqN޹8w ̓doS/i?D(m?э= dh~p~,9a*$g>={!dǶX?pѷ2[;iy .k1Ήgs\Ō`[[8Y4aab%Y,2=/!bn`b }:e^.c@!P@!3�`ʎ'҂"*8w/=΍lIf/wC#9�[@l*r?L;v{$HDɓY4@$J G>Qlhg}c!t|ILR`gB Ezh#IrݯInƦ/KȲG=;}NGv^wuWK)yrRzЛ-p'Rߚp?_ps=-fą~$K n!S2>ذCP,O|"o_ES~ N2~8ud'WB\0d O/IHw/roĜFJc4O "/ ""ۘDĘ||ֶ陞ٙy z:޷_~(kc3eD?_$ f$ |# {/3ԅQeccoOSl!! 4�� �IDAT~o4XѶ0L"mlDY (!>HH}~(wX€:O" fK{hʶFP'^{z3L/FYᨫ~wi]zk^'ѿ` $=uO=."z*W?#M^fy@}'<,0̑K;Ox$  ,.em�`0` 39 4eB2`A+6P0!AVgP@ = ^G8Ā E0qrJ7ADaC1@DCN93% Emq(T P4L|ĬA4cg#">CB(y({ E*.0F ? P/a�<gá|F?NDKgp @9�/ PVa_(Pm^8"]]?iSn[~r] > ϴGde> 2!QBFaye6#�jeLdʃH,`Cm# yG,4zB=$-@.~3p/k(튺EZ%.p$|5zܤys҂}'3ޔkVȄ,O@'LB>O|G_B„FhW~$y"]<(im//yMI9>Ky™ >nӔs'#hGzA8Misuy/gv?&ms >$  Hnw,W^m2pcĀYUYJ8I([(F 0 /P)<c�@& `dqI%A1f#J] ΈA'02*3䍂K<�;IFA"r\ 38%? ~! YFOJ >'D 4qP^rb�ʠ58'y_QO^<? ˥>Eq K3gN&ϐn 1F/02!/y'C83io=ҧ~‰AQǐm퓴{Ec/BSO/�D9j<IQ/i?䃸HߤP(0.spQ<CAY I6KJ?dƸ Kr KB i/iNapȇ+%ĵYiB?p}6G5%ԙ.|p@Ë(0 8B^r)EC)⢜77R.Á]_B:>F\;l;GҢRߨpI@3~+*O�gS HP>J*c* w~0IJ|AXq(86 yA :Ҍ A#i7r0x'-\e$  :IA'3xlS^s<'y!. J83 ]#A~ᅢJ^<"7G53RؒWR_~b!>H9…|`Ӕ<$k.PB>٫" v*!=f{jlFfGbe2OsWk0�0~P`L뼆}?ρN'Kʧ_*i$ u*Ȍ.52jkI'oΉ?/O?UGNMrߥ/4_$  L+i'%_II3t"30 )uQf 6,'9d@d?·ɑ@?&4(4Hp$_wsP1HP 7y$ylđx7~{,G~K}_ YO>G[9t)#$ߜ99! .[XP̎9+c\٘=GH2nV/ZHDkԴ'u^I4)~|'N|͹WOS&A[kl0wB~'~qȾ. qӅq;' H@UHA A\YrfȽ.Ķ4peEúk0 ? �&zŨ.CY9odW®u^w/8^l>84e'Ǡ<| 'J8 :JKY""PWr^Ɍjc4beiRibؔ0]N Ӷvf)(Ml/~qWFM4kö_$ ̔`8$��#�c86~I@`p"*�23wlڳYh6T{7�d/ (Xlʖ H薖�J?JR&`i%05 H@$09�zgP fCA葔$ Fm̦2"{l:{f6I? W0>Зsd7ysuu$  H` nt~`\M,A#: H`Fm2Nx_ao5K(x xtX!Aڃ^u,eS$  H@^�0I- H@$  H@3C`o>3EeF%  H@$  H@˗[vJ. H@$  H@QQ$  H@$| .idC2>Muԩرc\rI4 1yǻ#l,cz@|ؼ yĉ\)7>?Ft|:nP|mi-Aع<Կᅲx.ˆ 3^o7Fga=MG ԛ#G̗<'ÇKSzld8L{X)GR3|~kL%  H@�&X ?2gԡ$~WEA;�9 ?lçbi$2b�\{「|=]vEt �P/(qw}w{R׆E%㮻*~ᇢ,w|VsR�~mQQ04(>R +_4X>Ro+,6Z˵<Օ3i~峓m׃s H@7 �,?/:zhԳ�}63⟃KQ̲6O0K+*fd6- )oP<J9駥 W\QV},L4rݻB?EA_O>rFi +&h?(E*!'!J41"Ѿ(p7xcy'?gd@W\cP>S^|j 7~rJ?C=R30$  �d1d@9`rƗJO?T}k ܡ\1YV8}eՇzꪫWOيY>̜ù8~gLO>Yf)W/88o[oUdaf~۶m�LnYBX <GVIJ�Mʥ\`{:AzCP&C(rK)ӶЏ'bO8?N:3դj<$ @ sYHZ̰nڴiL,}2 �(xܹ,MpUb�3y矙% OY>f=ȡ}GKSʕpQP)oa^ȅ[oCύ>Cvp' X/`&EQVAhSYϒyM/}?S/e~ȧmMΑK)Sf)igI@+&Q?owY"Cy%@Qx%((OQ~@\sͿ= �8 w᯽ڢu1�$i?ӧ\wuEy&މ^Pf�ZYizTN$ &kv˞N:;i7q?\0QfĉJV0#|嗗A?ffN}fI3hv}-f֓Y9$p&mffL;{#Y#~~3{JXNՈ!,K?/'.<҅ < 'uYaseE0 ϙ#V'Af 27L|ȏlYigܔ `s^`=v> r'f d̨..~/G8'$)~S#KȘ䁴)3HʒtFy]@/‹4I?{'mY#?Ä89h#OyuEXfg~)z}V|P.Թ5p0BIfyPHO=H7^giON^Gq@ NpoH;7 P#oQ5aC;/D<ggR6%>:Cp"e!_~C0F!d ND?KfI@f�cH(}'Ƀ5͓д%  H@$  H@X0S $  H@$  H@&�4E$  H@$0&�h%  H@$  H@D@4H@$  H@D@$  H@$  H`h�P H@$  H@SF+ H@$  H@)k׮֯__[ +~YZ:sLuԩSɓ'?7U87D_$  H@$ oڴڼys9?:vX8qP5N H@$  H@Xߺuke˖|2ܹsG?jժ9ѣ\Y!{q�C$  H@f�Jm|fQqY\p<g ' !h�$  H@$  H` رߏ}߰a;92 gc 8qD =0`@ ,i߿8F=k�$  H@$  H`fΝ;K~%}ƍEGYGqs3�sp n߾}e3Y( # H@$  H@3C`Kٱşׯi�^/@�1p8^ 8p@叟a$  H@$  H`&DG)G}~V0%%=@:{ ׼ҏA!�~gQ�| H@$  H@ {{߽Os ȃ 8 �P3$  H@$  hlwy/&(Q裴GQΉg%]\@.4�t% H@$  H@XXn8>G99~^ %IȃCA �-p|$ H@$  H@G`۶m>l{]YR3}/_^?rD& @.M�ү$  H@$  h(ּgΒ{ǡ.~wϙ.?Gx\(?A977W>ȳAN B>$  H@$ !u֒,eś޼&@/sb�k֬)�c3@��R%  H@$  H` \gf0QsF!g?ߜ �י{Y{С̙3Y@_4>$  H@$ Y"=ke=F^KcS}/ſ2?F~5󛸑97nXV9r~k�oH@$  H@,ؼys6(Q£0k~~qs;J>z]2!'���=$  H@$  H`�fYp>Q3_?7Q{)+igG}:g�ȋus@I@$  H@LX~}'J5J6sx(̼s _^u]믿>s W?sϳ\kEGAM@H$  H@$  H`F [ԩSEGu={Yι;S$L09'l~\Ͻyq�}x[$  H@f�^}jC53fyveUv[k׮jǎ<*7]^9n^M�#'~#?~d/ H@$  H@3Ce8s{n@=|c@8zQqzk1՗_~Y{|KW_}ş�k2">`- H@$  H@C ̬ǡds=mpl�|Mz_ {V{oW?Cuﯮg_-e0ȂϙgMy#Oy: %  H@$  H`& .3M(ߙ}gGfsuf糟�~/VO?t1;=Sg}Vs& y# �mt|& H@$  H@C�; xK?|:tb>Z]s5վ}Q z+�j$  H@$Df&}!{77VJ_H|7ť$  H@$  H`&ty~!@x }yB˻-. �mt|& H@$  H@3Aq?*;wV{x瞫mqi�h3 H@$  H@ yo /K~'cǎU/R/`E6:>$  H@$ pɒu֍%=X}/Ik(a`ԕ7 ~H-$  H@$  H@+� ^[1?.xsgwU o-)lK�mt|& H@$  H@3Aw㏢LoڴiY9Q=�E)$  H@$  LDZKrp3r�\$  H@f�38f7l0yFT܃�0%  H@$  H`&9s:zhŻ]tTy{`J$  H@$ 077We˖5#.h��$  H@$0; �W.4M yu�%? H@$  H@ ,5lOCBG.N@Z$  H@$ OܹsK>/j֭Sg@!g -J@$  H@L8ydo߾W(ޓ|]BN@Wb$  H@$ U�f' iga?,Uot[3Ќ$  H@$ L`>|:rHFE �cāF�02:J@$  H@شiSsΒǏ#>lG",u?Bj� $  H@$ ֭vQ> _08q%TG6lPlR>nl7;͔5�4[$  H@$ЇUm۶Ϳp23Ϫ�fn?+ g~Xϧ/} �x_$  H@$Ї�93qeӧOWgΜX-zj͚5 `��}IDATkJV_\ܢ3 =K@$  H@:@g~1wV;v"h0`TS$  H@f�3 ~c `?�(`�jv�M%  H@$  H@S@3$  H@$  p5V H@$  H@T�0Uš0$  H@$ �0* H@$  H@*�8F$  H@$0�X%  H@$  H@SE@TH@$  H@C@x$  H@$  H`-[����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2012/�������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0020556�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2012/08/����������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0021005�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2012/08/IMAG0006-150x150.jpg��������������������������������0000664�0000000�0000000�00000016604�14502137606�0023462�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF�������;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90 �C�     �C   ���"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�tCRI:K z]H fE!kZZ~$_mYVH ap]l׈,_ @�&u4cԓM촒HuG>su}+6R+˧2qHRlwؕ6j1V4е̛pOJyqյ=5^NqGRp]:o 7L7_l%"0}OŦےv w+Md#)kO}v|Bqy{W<wczE KF@CqǐYFѲryxicPbcPs큏Ҽ?-j79,$*N[G^PQC| yO­wMrYyW1M|>!/N H?7ž;^jmPy)�w)Q?F4k9yc%p;g~^7exa~<EүxVf.-Mq j6Zfj#x%o6� ")#=Jsi4n]RWvN bkJ`\O ~ӞopgЮ@ � qJ-*LE1d`#hs'] xKk /krn㌏^z~?Io#}BP;?ׯ}?b/؏B+ľ<Q 0})VoiMNr|mĉ(O_ jҤf@K#FE}Z|`i<�sAYW*aJ9o<I<wo)ҡ=2>{իxHpj(h Ic,;`n1'_?%VV3K(|Mvfv ꉍpU<awxU&_8 u=OY*gfҔ8ub>Ie.#f<s?*gF٤[;K}z_|z%};O q>'05u4;ågΛ>jo*E ½2'\흻V{09=Jwn眸}9=ExRJݒ0]*YF5i8\8~&io5PF1kį\gK 3`m,K `鎟ίC,[-85xح7zu7z(nc`8*jlxuTDW6H).K"Ou?Zz}wb3ON+RȒHn=Cީޢd)1,N|dwIeZP_ga.)gI#,;0�\>5wqig1U@uE}kZ/"bafz1ۊꌮx!lv08[VBFEf6i=^{:v~x^\VflH?N*49 98cGn:vO,i*U�<Vf6!Ug]F |ԷN?1O2덼/Ԛ-_x7Gl g434X,9g'?r|G4+z}rLW�Ksqgo=FHUg*; Iԗ*< RR;!oR;xU<vȩt$%� ke wyTmSGN(dT=hi W'ki )6'ȧG]Bp+oNV s:vVlp;VݍVݑ+#Чfqqj+z؟{s%1GZqᦧjZlxRH\|vQ$H.#n\Ե{f# Xz\kV.,w$r}zκ" -#^�k X[=1_Y~3[Dinm-nwF ƾsM>w& u8ķ=n<a6A;@<VЕb)}o- Pn$|FuzF-!i˖9(f l%;H<rya]:s7[n#WNpiY؅lN�([e�Tgֶ>Dn +zr>!,XY]N i6V <!OZ: REj P/�+#ivPt+2XhJ?=28k*n7HK<YO59j}.SG4t-8:ajк'S,P=Oi[5.Z]ҴN�9G-OTUكbZ/ :Z#0krJ^G^s6�ž(5]. gKX!RH :"WLIvҜՒFr)Ӗ4ۡ=oYZBץgq,G+L8*xQҲ2 ^APx:w vD`GET֧ =�$ 7cڹ[8;[x.cF:.RX*O|�CQ&hI'\p#Abw8-EVP%6cA際&h<w~5-Մo繻X՘8ۊ/#׾ XٲIdBbqCd])&mi^.?o3TkWA<9-ET 6Hr5ŧ~_ʵۂ~o~iKn n:n䚣j?`id#FOҰ,4V7gAEF0dq{4{s;�hLŤ`0ܜg+TdS̸oAyOMV /[6^k Eݴ߁޼_v( }�h XӴYI$XI?+]+Ϩ:'( WҼُtpF)S٨.30U#zWXu}/M*NyW 5ӟIMxᙢVbm4o)n5KKvPTU5nZr.+TQnWIeU ;+BӦi^vB8z2v:$ӣV @v= X#V`7hM٘s ąN`zM<TdZʽxtysnpWZg>بE偸C,� 5/dGoo,ұ8U@P]#B/]&-k^":FF3Wu?yqS=\}\E<.ՆKi2:/ZDn#bTaG@;cW%(Ҫx?N]fQeb,{uYX{,ꋽ98qNGҽ?HңHGXP66ӨjŢ$�T�H\u7R~IOYF+xx;7ڊ®zr{�JLMͤSȌs^?j_ 1�hsr=\>+JK3n$'oGo3OvI޸htGDCdY7)o/hgT9A5( p<g?W`]E(ʰlwlvE_Y펫j!H?]Jr:W<cGX9'xgPz2}I\4*+PM}� <׵`~c$G$$W1Ꮘng̭ ߏcIElTg%=^Ǥ$8'ҁqީY\p[|S#ynrOVy *mRq˅&fm? QE$&fI_EQn潯ῃ_B yx<קr 榞$ԣd-H8@ϧ>5'kx1nf۟S|pG+֣ +s G,vFݎc"�#�6 GQF%ӯn;F9=xDžSK-Kx>lŹ<v폩㊩f09�kŽL-n٦Qtr::-(|3I{kk n2cԞx~(V>*2A3c#y?:{FvZmI ><;>‘@)#@I5)Ҕi4I06~q42~rިϥˢ]YXG]\AWٟ+S[a}n 6?C]^!i@H{� +1:n�cZ5/$-e 4ȭ$h4,{UWںՎmOFIp.b$QBt @�t42^ѫ ?:s^Ezݿ<8IO ~1GOZz7@` :spa8ls bkrFnǮ+}ʱ$`<8tY$2J'8kjo Zif6IWuǯsUCȯTa'w[[ukF2~{<9U58+;t'#uKMM3PU�!=8+l _:ơs+F7>\x'{WNc>WZ.?f+[(<ȭE!䉇F3S|=m܋lG<' q튟 {vG%ر.Ĩ>ߓ^h2/ :(8N?ZHH<;sEt-_*Cz*>,A;64y)WkOt mqr<Cxj3�փnj3^I;>ePR3KpǗn>^T#s:3CӗW8az{[z#AEm%BB ;󝎋{Hiw<.to�Ut+$7֙cלW̙, hOA|eo!kDITms;q�|9`Q^݁k/v:22`2kɵv݈v&D9-S)E=1U?ɣtMr[ikf hv9msJS^1x.ݎ>a#;`=zo^Si!V/ qߵsJcig\Qh+[\)?CڴrZ53WѻmF9\T3Ylo0n%J]Q9IJRֶm-$G FHE&Ok,TRq�FUo;qӚq珋͠hKrJjnHG ] LC)p==?yžm,j?2;1Y|V#'p9^8F;R}c|F̄/np8zTY+^!VFp:W-uVɍʪ�qsۅ"%xOAӧj HBBjdsMhl@F6UIa@J#�ZY3~ ɱP} &o:d>'bY< aN-ja)?u(Gop<j _2A_OY08fxUMVP|w=ıG6OMgFP6A;]f/P-y e Is3]\/\WXEE؊2�=�} ZJ|}x6DCEt C{5&Ncns^c޼& sJ|'s3[$*98=ɻra5yϴ7ǯyqi�8\*ή�*Lw'־D5GQ#m1�x{|Odb�t0?ɮ58-^^aNfiq8l+=BoFV.%힜gWcl,6ʽ1Z%sΓǗKL &�g'<c(RX)> ;֤nY]-LPH jvwB}@$eʑY$Ϙ aN=y$NnjͱNr~:r»b&bHҴH\ 7J3�UV"Llq*6ɀ>cJAPLS79rvO_`] :KAK ھO>,T �H8$LҼTγZ/,,lI0#q5 wWn5Jܯ*;xj�WVvls$p&ݠ�:SQV>4K(hmYFwqdmg IEYNg&�CǗο w۰zV/WM~[hz('5ކϔ^Ep1PWciˮƱbkx|QErŷNH㭮 6+EWc B<2<.{qERZ٢z#S|2+gi~Ҋ+j8qmj})Ea~z) ]yE#%#Kv4{ EZ9;2+GԖ)e<&b�8RxmCPk;YnDRU!|S[jHRv`"G߈:%ݺ4%wK\.C/czvQ^Hgkl o}ᧅitږٜ$]~pnoΊ(1N]ϡPc����������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2012/08/IMAG0006-300x199.jpg��������������������������������0000664�0000000�0000000�00000041557�14502137606�0023501�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF�������;CREATOR: gd-jpeg v1.0 (using IJG JPEG v62), quality = 90 �C�     �C   ��,"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�bbSZnˮlט}S[O%Y۳.=cT$^i${¶1Y6X�� ww+|[%}cIΏT<,/x(>S Aְ'յ( g0AgҴf5(I 2SYܷĿ';F$h:S+m>X9u6nM�9q3]6e* >l~MR-xNHݖP n9uO#K@r[ҥǦ"Bd<3q@ /V'=-N^O5y8�U[˟1 ;:]}5+ѼopV4�33ncBr{Wtۋ8ک>v}MKo]b+x̳תx#ÚvZF#rCRG�}2tf ^G-X]Lz Vs5:-Kv>.`Kr&Sg~5Ox ܎GIh89U :qkvG/7;GNڙx[єñF1漮n]N7cCֲG] lx#?)G,x<1Bi7*>aeHG5j;YhG{r3ׅkہdr;m2L,XU#*{;y7pN1r+4L�U_ƈ\xR,W} _ ~t7[c}Jd}Qt +PeTv{W7់m�i?f|J~cߠΣߵepxr<։n�9Rm,H?Vҿ^Д <7[;+]ïnkr;TlZwhm\ #@^]|^H!\'>?ϦΖ!y1ǚ$Qѿ Е`ʡ dWxMgRBō�dƴo xN-]iq]{}ֺ<ϭ|SiaڹҥI?{tq^3Hב,U; ɏ|>Դzמ(WY#dd7GC} �#PH$XwqV"y gh 椢w|g$PS†|M"o$\w?.[X xd#@e>VD:˅Kw9$-^@q9rJ�<>'ZK9b>Hƫg]�w7kѓj*LrĂ=;U᝷3q"⏃N:Ez<'˱ә=Lu}™r<iOF9wOѠ&hћXFr}FU twӚ4uEmt(wd2JŘat YbS>G?(=Kx.RrIׁګ_nM]Y56A$z ˚\<`U+WNsdMPzcj;W#R ɞ3v20֯@^pGJrq@}~ny̡p3ehWԣTKs)ƻIXc<ƍEU7du<>~ګ^܏3\C`OCU|Azps* 1¸=GŘD#ws|NR־(!bi=s[M,:lGYIDiӓt$^`ҍt漷W;w-@Hj~mC<##c^qu{u}0qsYZ\^er3,]-"<a:nU:w"[BF_�ZM7(^?^O?AZzz\9`qCkm{c#+~VNg|hrCab�j_ϳs&W5C[."2Iҧ[Yvǰ@r?Tj Y~aTG�5  �h/X9�u~ _R|U W+-L,Q8e]>|sмp#V:~d70a{zFEb*9^שGw4wRHt$k޾~rZؒhA ܟ.LjPqz\E*n]�mlmb QE*~#G`$Q_~g3G�[]*b?>sbο*j,wfkʢN0rN sZewo#"PK;c�QZ3=6ſ еM790́Gk<!EhKmb3ݞLDz~4=|C-M, }bxɬKn #sOk=VǵsQMo*M.t9V] ɯxumk?9d`JO~Bvg#|OZ\̎_ּ'1/Y1ܽJ=I_j4Ggi ?A&o'a6{z�nIT p'=+UN(rr&g1PKm =0}}s8K0OS FxW\ѕH2v^xUnzL '8?K A*d*pE@=MgM>\DZ]DfUv<pd\]2c�?ҧ_zHILuS dLwʄHQz?ư 1 {֥nNFIZ<E@py��Cl]?.inN1ˌzO[2FN3ԟ^b$qM{)u}b#%dp88F H$oV+M&h'įd�?V5sarNF9q[Eb��p }~!>-hZ|K4Űz�?μW#-'ֶ~;morXB|K�l'r2wv~wW��)E]jZ$PIs+Hǟ`LXp t?mGI)HN꧆kVw <.aE^=+laiw>�Y6R1~8M2XJW)Rز,Avt鑌UpG hC!3؟JN Ìk2eOzM-Y� ZpٺH3e펕qm^Ja ʚhn4ɒsGO*u6fQ6uq8Eb �9?sr@l_zVgV.:.1O;<=߸_ƓMJ\E%r7ʀoGQ*yģG"FzVӒ=bF~&wtJ3>ޝj"m"p֪Z"깯Q&j:T& �g_|.:u Fqۂ?]<jpg\_Z-Ŭnˍw=+4+GPuTGn9ӽcPuūipԏz9,QȨ##/8䯵okZ<%xR xϵ]Z]U 6O֭ݶnwh3v1y(}yMv3Z!GШ'=xnK'VWt򓱛^<}-ɘb2*_zV,%v%~MU;#4>3Qzfk,v7YI�_ ;$k<42ISWoq?6oC>h.?eP[ RݎDCڭ$5|%w( U%pgPKUB$tn;ޚkċ"I*F α.c<{EvNڹ{j1Cq",,0ˌ{rj[=5,ծC}P[dYd+CIpe֭\ѳ4~[0¯ez༄';I}b`q֪OpYpzۘ!`.N 9Aw( )'sy`�9= %;$eU�|>yr.Wf8R2:bpH6RƥAC(N?Ƥh $l#Me/10;Ҧ;�<? hxdZb]ZL1 bMe0pz{uYͽ_>8+4{;yr7''󪽉/\�x r}yuj4m2(cwڞյ2x r_8|{֦IS3J9Xkn0<O7w/<ydbԓֺFYKO�:}Eu>�cc\gҡHYtԈ�AҼP#G7--[Vv3ҡ]? +b Aޥ0$cZQ0�xZٌ{VY!rխpv95y,m%)mo~Vv9Upi^ܶ0tԚ"X N+m2Ewr#럯sڭP.xd;{[&pWWL}V6ڄcGQ?NkRC'[hJGMn71K ?1VyzZ]ǖt2y8{7i|Ϧ^<6rԁʼ$ANSlQI# *UZ wRĚ4�,ȇg?k?.㔍&57>ЧMY$񖚚+y{,Cv!Mw Vfj_mBCUtIJ#!\|2Hެo<c|U�fCdcdw=/P}U%.uբR@c�38KP1;UO5n-[7FkQ=Rk/O.)|)'4R4mKXr!֝>Ye,{F g#֬<A&|K-w[{v<ĂxAV~"oB&8�ϽE,_OvSss5i^Iln7)T;})4lHk*0W/iNIՖ4r=jPO…`3VrWG3q 5ۂ'$ŒV[XW'>翽Vf{,~2nAibIq1&ǦsֲFT9:l1:Va6ݖOqEgr{sK T(8wN ;pww:g g;f@cHС�OPc,<Mg8\n1"b8X^Hk>ٛOVhDE 9!suP:ǝ<Z%##_!x-{zeRL|ݔp?J6>/$"EH\la( Z\ӑsOeUQiŴzUM'Mh+PGc\gF9HO<cOWCCAU )nPzדZ|G ̚|'7Ҝ}j{IziМ`{,NJnR⹏XgҦ VAN: E7̋p85kn:ץCj,hɒ1ZFKgU8['hJHT2T `ա`3S}J"0k8I,ɞca=v%d w. 1}Vghe]cıReH�O>lifc N?:ǿ1$NXܣ] 4҉fT\aAj!!WwF&O'mJJ_?}|.&l�@2OMj϶~j3G]ٱ䘖2OI ^_m5%cT8υ]'ue!±nFb@-8~k5}+ѧuo*%(l? ka<2ex15? =6y/� nJ8{nL5/rQڶ㶊�<ҲZ͎'ecɧImJ2Aڠ؞20ǖzՉ4ϓ XjsH|+w<PPWh0x#M`L޴<ju3wu<R,}(״F=bzN;V#0ry=sYhqJÌ~J kPǚ*ITX(#VR~V/B8cckSO #9;tR9Lgޮ-j76HA/Yy$:v豢e³0_ʲ(MI!G#۟A~`8?OZ=cQH/_yV`�Db!Knq\g-`yl09uGJvzllf"&k1}_!LI1P?z9AGKOSMZlqX�Pv/RHnP]zTM#4ߦe$!Oa^Z9Wxu{=&S4*A\8\2gethzvؓSոSt4`r J[kNF *Aؚ+xb01һK4ꦢLnxn !+5$qb()E]N#gm%�rcïa^q01_�bգFQHN=vw&J\A z݉U['eX\)9<,K/sXӊ4- ڴ›w5N̍c޶c Inf"U@J󞵟:q߸' G2H_p+DɜOVo Eq$-*NW3< !0Cdl02+YYBDUmcstxﹹ$�O^?`LRݨ~;QkD)i!N'� /JVZWO9#Ŭ4ϣ~,~6^Z@_=xkH־!x(-KCܞ)o:kvm;r=zc+>cKMI62S^}|3ᶗ%UοeėRCHH8Xp ϯ%¢ օ{} !fUqKNhhcZȊY �w.[["@g_v t Vn,8TO5JL:De IWnAjγ�pi%OB}[1m%P㊦sf]YƓYvw knTh[ۡ]i<Ex-{jcHR1[21 ]^<*ņ9۸!'rOA}+N]*O3eU.O9~Wū\Wu }1]2H'wCW~`O׎hmar08+/S- fn*A?jem+c�A+o߂ḻ*HIs3*2oodn#U o%s5J�Fݵ@Ka(bp~`UmF$M*�Ȯcz:V\LX.yR1ֱ7)#OwORp�hsV'� 5Y*!9<]_Cv�8TiAt;k`pHW&һ܄vMr $¹vuʢ䒬d�F�ֳ-[wRi/-oCs5JMմ9,'|БI^"1mt\ܗby9 *-<^ƺMUq+pǖȱ[ygk$x)l HP U�LR#8:w5IH^1sERAqRrH$5ϖA @Q029$j/8䁞jf Z-�Ux'Y#tv0݁',mY!$tPKq8?4hZQypDh c&yv7ש5当w DWu(ݟ)Q]Oc>1v/n >=Vc,g(#xzQ]|ru:,mQH_;ǵz(M>PrzgsXްxdHzܲ٦c@kήiTEhʤ=YWڤ6)Fv+9glùYW*ғ? []T8&qMGCΆSymUf<b4"1p}OլEܶ8X̧(@vUI,esMTh1v;Jݛ(tXcx9�=1Y;Pۆp͎sɔ!$ [9ai4v<wop<w<:X}B9}8+YMa #a0>cZ]n.d*\uº1d"-ŀ8wM&ZЭ͝a8 7ɴ{cҶAA1sq>04pKݰ|S9P epq܊L*<1R(|{^#EپKTr"d�p+n?(/˕=~1x[㋴_2ɍXp~c!XI_G0OAִ 4n ~:C޲sZHbrSe%25üR+ҵѥKKcl}HYem+KSiwC$,zK빼I[#o|IgVȷ"B�m8<64 7ns #khE/ iH~hfNmqтALAX+FXrZKe^FxUy5VyS m?Q[C<[+<ķZ^XKG'I% s-x>$^-붷lՙd#{Ys= lZH%Cu9$ G x5#@�aONJAs=jr;V&|sdʗzذk@:p2{w+3]B7c<)|wSl6vs_LV:]c>E.ܜ}xY 5Y.爼cԀ;`T4 Ftd׫NSqi*F$j�m0rN+d <ÉWxt?rvYS̐dpœ�gtͧ*�7^`AvfX2ۜw5s&$:+ϵeHҭ#6&ǽP6RqU�=xjv)3TnCɮ[`ڡm?;1JƱg@=qUc*{Bʝ2zujƛ@$ V?JVC18aDq$ '8zVg!&잪=@jbƪrcӷYer@5e$201֪k7[ uR}*Y-ұt Om}rfb,ݔ~ HEs<-g_ A8sz>oT!vہUx Q)^4Q}yr*+@Vq5 QĪARz]Jn�R8�s?5[QbF%ȏo.ڽZo>�ڦxn�{dqzN9on+j2"{3@Sڼ Tʎj�x|פ+]Io,[VErw1+'isU?Pi:4$rgIcҢQp5iӬgC ڶ4\2G{c qdNs\*i<A>E~V4 jr k,7 ZGe`sڳu=\Bƣi= R~뚚ٕf~O=?Ahbc8?j%πx知dՑO'QK{ƙ!y:A>J JӞ (Q$W;S5{W:e8�׆|iu6yiײG5啶B<ɸ$GR7}k%YUiiX<ڕGM$+FsEI];]I6W<"0#~o xȂr'aWyb$ěVBrq3Yz@sz-Kr<rI7|urL.cFCr8lc[“b1ʞOU4[YM"ogB=s^_ i> d2aq'/�sOCj]^г%A?Cc3 qu (>zW|)ׯR=Q񭀟PXΘUi]qTt?whR6 O<{TVKas6H ({qZ81\:-b7vD.FZ1(>F+V";gt{HS"aIqz5oO4sH Anzg浊mIxk&_!rϻb t0߮+$$~<ag|],3-Ŝ4S/F"kO3ќgMq cUO_`nj1S?J>A;Ug 5N8Z@r rq? ͿHCBd+FKw;I~GSs6C(-L )FwӏҲV#'w皶,$vq[aư5IKi V63i 7(Ǧ95Ŗo ?lDA ڰX-ǖWpRJzIλ8䒢HzQW,@1H5xF<namNA2z1=)<i}:Xݨ *nppHBx|ˮ.rsI3V.oDvzOmJP1B[DD<{/R$>ƒcyvPg=:� ato* <cpGzZTèpA;U91եs7fqzibCP<99S00MG,߻n319^KO 2}N< c1H,"!$0zs֥%-no<-ͬF8tЂ z{W梷q?ܯ��'_¹%DVd!ն X#'_XJGsE(Tsh~J Pc+$$}5ݎ2+d =ǽuwZl+y-$EsGa1^ЋK,�zgtxڱ-_�e;2jtъRm,q\d2j]FA=랒QA~X\z1, +Ǧ+ATy9^nYjׂX]1QM@15(`K{@D Z;01Gs]W| W1 BQ4 h'?k,蠷";"N�]>c3gG -mn|14[ Ts>Z.~XGBӎ; q-8 �(`XU ~Yޠ97#[di1BN�v@b$<C<8z}q]!H >r=C嵌H\u\V!ə6vCA#w#j8ArOsTpQʜAݿčhdO-Y#ɯt�kAqb8q+*A}k۵O2yj@nߌ١M:UQkVv2*(ɻ]{}.)*&ۅ8W$�#KJ33aĂzqu'q~"Y &b HBǧ֫[ڮ%E%M-Υ$|:w^fq>Id8{5 u.)[LO2UN=:VwEKpl@犏)V}9>a :ZuAm#G={u#q;�]UF6 1r[U9J%-˜REg5Jm#F^׭B-5`k&!99$A^mi6&d򨐗;KЕccx(xsLV|SCJ˵ /|_zܹ4 ௱�W|CPfRȤ. iǩTKuSOf'\csCAeֽ^�X^[A 땳RI(Sx:+h# /< >VH滱22_AL8#,M4.$D>|2-HU19*u2[mp��:P|ZF8};<qZRPJqU)s xhϬ&a@%~p18>kĵi#ؿbfU+9ぞIVuwŒT㞣 ##3 U9#t'MGWowv2u46Ւc N9ELEť06h}3<-{eZEq&D؀ܟKxj9{w(2>I֛9g.闍h|qA9Y3Xg#'9,ntI,]a_#<WiZ޼S8MH Q#9*יO+-^R >:0 ^p%@}-$T;n*F#D =?ңث�mW+ eY]F~$ /<%qn2`&Ή$zj˟ C,0ef*�SpJŹX*TsZFGW:<wKުO$F/vTg~Gִ2MX޽ܡ] }Gcך)t(YaDE$ k)0;ַQgZɩ W@@u#'=99f eide")N�Ucan's{}(6є9cTOOkT+ApP_c#֢XBk(;aTmapcŘpMZ1m8H�?Ҵ0?1P:[x~XTW Ɓr0&1ҍn lAsN;W:g*kn~J6[v1?*_Iq7/<YL0Gf5ۙe8ml/^r9+iu9\.o�xN6eQY0ApZkq[Z@]E-(Rc9?=⽯3Iѡ|x߽!6gߟVxii%)PQ#J0 ~8䢻3iBu:DvflD e1fټ-˧aoлC? 4/zƕ≮5I V( $S �zw'uoxr&"e3NEQYōuSϭip?Z4!)ԆC9 |/Cxn- a}<W g,FF޼mnEznk[e؀T^%gT,ymnX$)cl{r4or?)iO{0=vm18NK޻/]GL3b66ޢL'[?*.T6]͒A$y$ɪ|F&"46lM<vUػRrHI}nONL2F}� rݰ^šn<5,)$aAmcn9XpdcķC'oκl-;5oǍ1=HMA)=Yk>!M~trZec  d5J~ 5V42q5憬^˙,iԼM}$rhI9=Ez7ne<0.=($7'20D$$f0\џ ȅf V%2y ^mZqYXC<*#HfolV'2+^࿋qgӜ{WM)$_C^Yf{9L do_ZKY 68:ueq4-˲.p}9hi8c='h\]дLazqhI�s<L 6p޿ή"*8�?J33Z9..X #G >g,zZ'&FFm,yCI?_M$W;d61( h.'8! !SKq6p ;�P\^ z@%bBY8�N7rR(G9dt5V[H 2yjC1ي=*c #}k8,cn͟Ƌc5=N6A v^Go7:sm w7k G1*@I3I#1j3GYbY0G|�{U2XKkHx"r L\v%C/<|ٮB0Ritm85<085o'o3] m*qW7@ ӥdC 2+2r@=My_MWC=T2inXJwb[ (Ÿi+#I1n9q_ ڈؒRWFA>ֽV-О<6ݻXrA\gq7ȪU,p=IZMdF-42�t6ks`[8R<7 sԗzF]ž}S!aJ8e跶W2087 cQ>-ty7;GkёL@|`:ga�:bit#kp6ƒn$w=kƖjֺ "P;H:cVKq۵vLJZbi(Пl%dQj4u:cjMu 1IT&�~(ԈuX%S$[{!nogld)8QE/aaץ[[A,R<;VyGڼIlYJ3f៨na*KTS>%Oz;`bQֻ RK=Ħy4QHc&kDoPH>TSM=+ g Â(j6tRzֺ<<�"CCaF4zgEgM/u6 XyDx$]q*%<2b+rii(%:cU_ nW,NG=(Z=$˫"fS Yy*zsߥzp`GJ(FEB7mL)  V,El/t167*'OSEYǩځ`@s*8ˁ\< v�( H>W;WN g[2hH$BW>'Ѵa09E=ڒNi3 D`<S^{}O*RA|\[ֿ/pS7 B{e+ө4Nҋݤwzցx;R.aU#da^eOM BLS&v;Ey۔j斦'om ZɤZh<6H<Vo: s1f`~V'/~8+Yg&a^ޝjwN^7%qw4LlA>M;R[Kg;$A8ap=GJ(Ԫ.ʅ*z4zχkxJ״{O0_r=0ÊQe phXF3] 5eR?�������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2012/08/IMAG0006.jpg����������������������������������������0000664�0000000�0000000�00000120122�14502137606�0022530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��H�H��CExif��MM�*��������HTC��������n��������������(��������������i�������%������l��jHTC Wildfire S A510e�����H������H���� '������������0220������������2����� ������F�����0100�������������������M������N����2012:08:20 09:59:56�2012:08:20 09:59:56���a���d������R98������0100����� �������������N���������������W��������������������������&�������.�������F�������N����� ��^�������4������6�����<���d��������������5���d����������������;������8���WGS-84��ASCII���NETWORK�2012:08:20����������������������(�������������������s�������H������H����JFIF�������C�    $.' ",#(7),01444'9=82<.342�C  2!!22222222222222222222222222222222222222222222222222���"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�[Gn[K`\w@e!zUk˅j÷lR 6b{$B=@╡12A�Zl4!yq *Jj"M4=N�O+,Z;}GEM*;=ē L%t+;#jdSZ]Jnhw82?iZX[AhsJqG'V吠`1CvF7)o0�SQu|v$= EL",>&:�w>TYo FּŷA"54nعI5eH׵cI#gʒ?ZѶuHE<⋏ٳۮBR=)" ?9Wi8ԔaqGHK)؏Ω2\Z&}nBHVX@pT}+/B̀#].<ߡ/{Y$A[)Ko"ȇZy#ɧiW%{YF}GjжKDZ; M!Inx1os bX%YS!qED*(Z<Y#p*LcZ:]#܋-ʟ)൷E>_{O3} D!% t? qZ`MEIppil7oj%@O*}4c� uϹ 2P2{{o"ܳ �*nŲo'�Q=μWzj!~lzʓՕ>}~8CoYS]jsytϯON[jyjym,0Jֆۀ͏«Z÷Zq:)U@PZ<#�zT㎵+BJ W6p] m-Wb7=] }>ۘC6q U@~Y 9Lzx/c 22r?U,d2TW^KEwm)mc{9Ρ&d 2 {;։撳,_2c($ʿT6 ,cXPʦ9 ڭy 1Dү۟7NkqOk/D mN3ip8$Wd-$v H#r#2AErxR+g4P ~Fv*/5A8䞸RAxOR D#aLȉ`P" ֪g)߯)?{˂*Ҋ0#;8{cU[sH8Wt1�bBy!<{;htb(Iik$0s^si_rI&�϶ #dШ�hC0&嘭#ATF2J@WqSslE~=7Gz})6ȫ^W)E#c֕,c,Pw�KgjgWSw~\N-Eij$jēڻ�ei?޹+D p[^zr~B�?AVzCԭ ;1Ac^?£.QL8QgK-QX `OlrŨgTV'} 'ɕ[M4vխB\#1NJ.OH7QLp"C  e9?2!!5mmm"[N ̢T �Rev2N`���SR5,צycBm `$[WK@aF['Ҥ Q2HU;�^#BAK-)1 bT6)1q>'s6a?Ux- lsM]K|VQV2gFmod^6Cq C�sWB*ͨ{yFCtҋX;hY$2�EY|=j/ogRo�.HKWRiW%2*-"j˞m' 9 [ersکF38LƤL12;zn?O{oH=95,lg#VfhmwBƽ1,}5ug_u2{^5tfFJ:a[/ ]]62}t <$ķ=ZSz�2FZ) {%%GL}ɫpf!,"48.VΥ3JE\?)>,IlUpsQJ ̣`8e@"�dP֦(=jC|Ҫ^a F9sR3TVlB\ K+]jӛm:i[E1Nr~2XRH#S#jQG5j:zcu�bvٕ8"`W\tzVM\SV02 �dnɆozs Շ=隳i[*�fBTpjv 7bayd#kf<yJ;b78P~$&YJx~jrhHfA LS#9XU"%bcݔ41;gK&X"(y]E;Hq3C>ԷR4V,i-f$sN 0s9 #5NR\U\T t4@e'yj'mj"Lyv+׭6Yq'�POR OLgSSa4VyITtGOAY،M:!3ɤHwy6F$ֵ9.;c!U ۭ1+Z}%ġB 5A2W+ģ!HշjVDO =iXG5sGmԖ&"c5n2qy"YDLѓ#"fhN̳33 uK 3N2GcZgNq֢T.㩅{$ӇYb8${BTm"t[a;ޮ}Sz W00K~-֩mIt, Axv1 J Eiw9*&U7Y؞\3NgZɦ.Ke9s[j��1+dqM b8 c2M`κ Cҵ<Ee~jK:w^ՕJI@&cդCeҚTU{{*ZA4 \(92؜z}+>c52Iݑʱ',q=jY%yfX"r;+z[wsTtk"~qSe*eB.Nȩkbɶn['W=bw!N`A2BdccکX=.w: FsUx#Pr7C֐Ф V돷[G muA {ʔRGwd7,kv-f\d`kmdZK~a2>.(Ҟ&:f5v6&cq˗ ۚ &+dn֥QB<㰩ckdxfxF{ ƌS IqSS hPQ Y6(Bw3MPJWȕ“Unc-!'ZF)uZMB)zx|!F{ک&nդY6և3$HzrqN 5dxSb$OT#c> D*ɐZLHaE,b[YkۿVZ2 =.@NC`^j 6Zaz~5~4bU 13<{Q^֑HE@(-]n�ҹ#{˴_%[,=Fzk�v Yz)fW,åI&T-5jm, 1Z2`]-ݖkү�|֢GV-Qh݄a=I V_,8/U/ʶ\:@˨L#JOޭH%3vA= 3�SQe[!iI]ǜ6-1`m̐˱~N'))'mJ1v,�E_;~^�֮gZ# pah8$ї `�T x^�"W^˒z X[ =WpŒ*[☮( x4N #)^�}L.5"Ff#Yp]Mj3�s=gUF a9<jm ]ۗ9r?*Y]wdgh?ͫUAYV7+y`*ފ?Է[Ms1ܨa}ZYҾ CO tMg 7T#Zwf; +M]FGҢ!\yWkO=̫�K)yb) E+[GEh$UUh2=ּ_EaQtQ(鄛I T'T\nGU]Ede/܏L�󢊂 >II,QZlNSA#o4Q@T7$y8!O4QLG]I5fcgr  9WD9C`̚P*N88ҳ4.øڊ+%?مUn*cl8<W1G,NQNUE@Ί(Gb�C�  !"$"$�C�M"����������������@��!1A"Qaq2#BR3br$SC%��������������*������!1AQ"qa2� ��?�M%EYcΨ>$5$#4cƛ` /3@Es \OyrhV# 2+U@wKsl)X(}lw8 �rwMe ;sw#U|APgҪfbhK[XW<}`#*$`oONBrr �;涮ڂ˨N0H5yUb-m�ӝ E%2Cgi#Q* ̬Xv5i ?sMYF j"Q)Rs/lݻXF‹rͩHT'p:q(($jME_|TDp4'iK˕XFwO~qg|DAY[AߋL~L(C#=ұq,.!1%tb[3C);ixKccX[,zUA\Gi(z* UX7d~dlNVLѸ]3(p7Ww@{:aQL%'}('N9d'+s^ 4+s�jeR.f%DU :B1 xs˧Z]4n](#<6B9~#!IߗԎmvf@ HzginG:+Htmt_izAДӨcYN#rx0%Nu4(8njCf! 5qٽElLC>nT_&ApTnY^bfB$\yzU<VexRV3{mfl( :0z=hv[Eĺ@-<=zS6׭u<,ԅ+"Ѻ2Z+.63L%5ҭU]PagHdEVbBcI�^D2 ɯ`IpHܲs5c04&NH-sD#)[tCj9e bXѤ( 1Ft;c8:3j9:O2oE1{/w#>\ϧZ2ƅd! jh<&ߞ)kYCE \�n<"Hn@x2N).'aHag(.#F҄$yp`@ӌ5EtRg`'U;}ln92&CE�#u X.F<$*n1L}&Y{swHE0t8A.ד/cێɎŤk<8pzV76^Bf5F#`ҝTy* �q9 CӸ}WV5$n2E W yrJdyC\ X4/.G.qY;zĄv@ȥ \7Grv4#E/^ s4D9#&[Ŏh8-jAd` zrW>\,fH'�cpE-/980lkiD. kEOW]Y=ncobaI ,On.R;%So֒(z߭T}5ĝմo#6U$k8Xs4Av"c$hVY_ח.8{ Y闷<qXdx=ŝIn8!A+Cܼ_j, Քx%[�T@H}XJ/ڶ>d~?<#OW @[ INotn#&Pğ Ҷ{ SfeoX99hot5 b�*iұ9.۩/Usmd(lpO]�LTZFuc<JY0)r9ffNU偞dg*2J lP٘&OCچleO)T3�= 0s&s`wD5ReY N3>TGVF@D�=z*,s4`31֪Hd1HF:IYwJ#u$'U&B{QW*|a5s;T/[ԣr@]6V(T![N䑜]I#RT>^U]*+G.T6HADAeI; _lJ:EͼnI#H69$T/A�>(ul:TeUrYw+x|8Yb9TXo9 S�Nf6[xB $9t$SVHʜhzV6U6ɑ['j{FF4۟<MՏ *6˷OZ= aǾp 6r3ڞ)di'7aGbIZ@yv(l87ETI>g[vskZvּNB(p\hA8S䧯mw�e�t?w�xA v*PLs~#Q>渌Mn.#1c5e Ia�m#k W#ǁ|Ijf |;뤜_#í!+,m;|Ѳer%| ϳg]=jNd=)ydtVe>_pۻr76)kyZ6Ϯ*ݝhBۍG|Ǒ `3F$IbfNbts<+Rf�J-dI ^}w l6$R T 䝏2hZ-!`oPȀbyӐD%i\@*G"Uy<�v\躶�P3V��"?SSU,1z^ e o'x[b N)#;`yV1Kĸ|7iu,ykEv{؈HuEh<`0y=<^p9a%n_J?g;Mx ޘdh[?}?]_n7Z5/62[}nGT+Go [<b�!k �G{9ێ/?+c}1YYۙ R:TݷTwsw1'5',9BQjT&;mX'K`F7)3O,Ve4dA3J7<|Qa= ܜ}+~8y�q^Hj5ˣπ <ix,jo0餏]j5c2E-AH9qAsr#ZQ*Jҹ?m, ?Aޭ+H,#]?~4Z؉5wi<nnN^[~jO4ACʑ oc8b'+7-+dF[h ܾ%g;û88Cy-8el.N| (7د,c7e.@�zV8-Hln̏lQ4Q1ed~1E3q$TF)YHxXtUW@#hH&pQpzQWDa:=Im)񈃗a7CDՔ�#C�{RL̹];Ivİ`FRrH64ffu3ڢ@jܷ1/aHK7&Ie$%O29b BF0C jQ-%؍G#}DsSdR7A^I*YNrA;T@Wc r UAG8,hD^8Yk8NiɗDL\%s`b6$(qc&wf.`D.Hz s&Yuw,dW+`3=:α 23VLr23I&4QWq(.Dqh]##=|I/.uc]f,m#yu| `td#"6.fZYKTi\SZbb3H@UYS-4* 6߼M>U5�)äinl=Qi--0E)*@f#?:sM}3&C(8ۯ2@Yq~6LW&3uf@Hw-{25#CMR#�m:) @*z ՘Ɂ?.E ),OHau*@�#t^0o)i1A`}Lە3Gj+8UH?QE9 GR$Qc] jYtK=O%_ \`>ѣ4TӇS7�S2qbGF^ 巋W}*5;(�4ݴ"8`sAXQwr@)<pDhB �mq\$ţIaCBG+½&þPapAokeg. pF%^S,N#SY7DvGZdKz;c{];A:=܃IGl�4ZW\Xeۧ{|LGld <'RsO\aCo#ҭq|{Vi&Rrot2(21#(*(mYX�/@R�A 8[��uyI VCZhL {VpA7ޕCUuG0d i@|cK?t; rz{I`7厵1q^q ,g uM{3q xWc ${b+xA(ݩnIK|H9H FWѵ{#WKDb}W�>Gp$|:Vbi Ͱ;!}Jh*_gk9$mDrO1T=7 zRBK9_[ѶHP�ŏAlE"0ztj#`+ vSQEj. ~It#Ebט<|qpaM&wI ̓j>g#ugmo�?EI4dX)(8tH dCȨyDss!09L[g 6jh[l#p#r:Pc-ғ*L<Y@VZN@=klI*J|D  ]M1p@ >,z{ft## JU bv$ZY뙘7*1;͂3gF\$Vv!ƒ; s0a <g1cH'bfS 3̤όl‡Ys0쇖T n}:K:$ASQD,F A#Rm=ѫI+):A>LzW #� \zjٖ"Y]Y{PD}چz$ί,r.JHdNqv϶ 0[H먂v8X=-yt-4ಒI xsҶ-c%%swexX 3$@U! +`B/(*w!xU�v`GAV JI\GjҌrsD)6V+%Xҵ#$`A.I:_ŀQs֟wP3d89 Ei$SU//1r! }W<BvX˖�}iM3jѓT6;}ndp@| #hБ5x3#({UZI6Ÿ|WJ#LՔr Ωq|좱UDFZ$=9? bōM0HAp:gp9f "ʏڳk L tX �$g)9`UCg38d'tَ�w܍cΨAJ4-kXBP`d69nA qlHPzt!YbOm~ts Tcҟ0)*@POS5B4;ջ*? &%8׀3W\`r1n!bAu#!hdh%E[y%#M8&xJ3Ք?�!^`Ns;/sncƳV;<M)"E,q lK}ڥlnH{20EBۉ@gű 稡{C[G6? psmN1:c{k-)IU_z�O/LWm"G|faoOhR4CaOsrnrŜ!ܭ>aGE,6܏*mf#�cg˭_Zq+ZDD0S:.hJ]8�dn*F;|1Y4 Fҫ'jS;AcZX8q֜6>g1 H4 hgh@Q~� w(7A_Np0 YēH~XК G!gsoDsi\kݘf:s$b<Y'5Oť1+A@uxdg cҷ~"BoA,�&QP<Zm{3x< �K1+E߇Jؽ XڙGWV\ZOqt4vy; ψqyC$֓ >WARXX'm_Gz-8gH&VzRG:F uK=VdM>=<,xhc-jNMCgQ$dN[bʬd18wF\/sYXdS 1et2@w > 3:B~)ťԧI_ޏ= m5/B:2#$32{УtI⼒! 1�8�sFz"qis> ~4{]VOOJtu>l|ktBVHTAimdOx/az|㗥Mbm(;g;YppåzvՌz¬ b3Ұ Ip#19Q^ҼɩTbC3ڍmo$hҸΕ܏_ځ[$ҺF3,H2<`p|W@-5| DrIl6`!rg&]`2B;tFǺS* -*N4tcHHRg?ZmE]WN0&21DV;hH1jH\U)S l>P#xgΑ&A UB@c꧈ʊA$02~IܫcZ_[N\2Dql>]G-'�]w*[6ڪ`,փ2H=in`j  x_l X6ZX ե?*G2e-McS4D, Xf i:9YXqX�?@=o6,?J6 >:N`ΕxaQzç�gRmNEޑ0H,l��m#h<'̚Ǖ%RsE #w L4tڧj 5!nˍǯ:PI!Y\18w99U#LJi]K/ z *!Lg nNBA-|9RΓg599 *$-dÎy/JH:ɕfV4$aYm#ncި$F>կI؏*eуq׭2fxzY7Qn~)5K c[Z+|x'^~ #Џb <J4;۝--"M}ɏ>7 I]TIn\GLmy8X\!#C'8͝w7Vr*Ί8ذ *pG,Q_Q"$Vm'Y}U8f[f;^#3^[0V<J=+lvV\K�p˄(d8Em 0,#֞򣧵J3wS֘wW87)Oxd0%{eTP|sWKTZ7|QC6C]R,1BL'm:JPalLV|&ܿ~r6`|ƮZYAcgw,ߙq砢[L'9+S<tYHN h�/7 wn2p>p=bX52)ä4ce&1ʮrW׼Ng<bqνN1}Ydhtd:;gk%I2!hv1[Lh ;qI7I(g tߕF;)WnY*CizZX_L1km*xxP6tt `mg[aԉ]JsOJM`^43E P]o{ M{N5wk5\j _ի&1ev�"λ*Љ" 0)[AeP9"`tgq:R7z tzL1By!?z3F]3gԟa }0z[IwEA;})xאM Dq^`QGo0/;Uw~#)9N)d<?=D1>}j8-/NHY$- 3t :xD źAhcTNBz  Hng 7@9jDeפ(Ps?޽n#+#�*guGU @~gˤ6 sTݼb'nz Oҷ|;rpvfNeqrNʆP΢ a (6N|I*Y41ӥAwIW\�0q~qQƸw o\g5S{ *Mq\c.8M?gn\mj: 1Tm񁝩e-0ZZA OATZG3IeA>ȣr2i\ Nj0<'M.sʛ.X$F 3#ZHN "@|cΑH 22I(3}kʻֵJmcoZ:DW֡]8:jehX&]tc54CL^ $J eP`ЛǕXt?8e �֬dB<+1,ce)%2a*gƒW b7ӾA?jtF 8wƀQ?L<r>W 7\;阩`?^@I(H1Jmsn@G=蓍85[$Nw|D�ǻEٔ6њ!E`@n�5^ hV{kRCF:K[M!2\:*9<WFq{m!>&5< mOg6XD|P\~֛z1ƾ=+f{dhú`@' xs!E[`N9RG>(*AFvo}{Q+np":֯{FEz|d?f2fԌ{}v5b>U g[ܚٸJ%ԸH׹Vo*2eQ@3$ Nπo$aURY\H|, ؿ�DżU0n})=* sҩp1N5gz4r@P|ꏇ^&T X6V9Sa+0|^(-L u`Q)ICHWB PZ^3v.r҃}4C@e9g.=MQդW}L:צ3AԔXAbϧ8_S)緒 %e`w8sAiHbgا.3t >_ފx:4ܶQ< h<˸ jI:#wFF i9Jl7>CT2yJHKȻjcV$2qS8CwE�ܰ/dHc߻ C{Qn2c'F1ҳti $}jĝ̮Ib˝3&2ePcahvf#^Sa[u,'ݣ8[OY0&zyqBQ;̣�Ѯ8?(#ұe "/?\T]CJ0B9yL'sF:# d}>{.\J (~m:⊰$HqF)n[a |Q�cRY.nnHb>Iَs}lB_ !ٻh8lQ#S1<HC80qk6dzCnH`چ0vUwݻ"dwF�yU Tc1Tq+H[ Υ&Ws? >68M=;Z Sv^'源):@Ppv}u%0Xٮ[W'g�oVE:ԑamu9"E<wZF�@A()؆uVb~QSG7m1XxH9oyRC(WP`S11փlwUQ9?f 1yJO/Y rDޔbf8@tl6<EPmژXAޅH ԩ+}h|E0 戫|hSF Po 0 n)ϥbd0eln1NQan[٤.BwXY"FbRjt{2ry}WWiI�hWoӣ!Xim , Y� K#$Qv<�jX^HLj|ֳq; (qL [E@T3?zRV1@v;m!�*RUKk'fTCMwA8g Nx9R^6D|TagX|,4s ?ՉW\f&Nq_2!$>%2#E;GRsce#օPeԹ.݆u b'PsN?Ux}|#. 5mVJItK�Ox|Om0!$<>ݡp[zҖk2,MN i04 [�5YǸĢKY]2VtU_w p<ZYvMy#aGK{ q':cԯK(c?ZV%u<S3Qr7V7hI ˹m �}(JQWbHlG!˥-)ʜD6BR^�SXeNd?,wǑ5;K%H+zF׌V82ćq(& 1_M8%3ށM#{{B'Ql>fK|D <*eJB7QPM`0vR=k8-m�V\D>9B>(Dd�i>2K@N?Z^V\5i٪ۅX]96,mO3� iv~JWV}JJ xXP$LeASM:F#\;k#$lx| q Ndx0}>nHō-7f+{ `ҳm 4]og4fVVmL: ]ǀ9FRYT%8gkIdFL ?ޣ1FujP :ˡp,53c.C3iT]gV]9C0ӃRbOGj?1)$bIqU`MyĠajr챁Wr2?P; \{Ր "Qļχ*LCqntpGݮI>z unHdMض3RN$R>4"sڶكH0Tm+~+ysgvO?ڣ&ux$bQΙQ�(W#3 dR3ތF,ڭU9 R%S<MQŦ*p7'wnDD ^BazaǞh]^^L[S<ǝ^UAڵ.?e%Ⱦ%]OxOVYeśThN�",@/g#@C*QNԊ^ʴB:bbeAԜi5b*6J+8g ttޢ> Q'g5;T-gڠz$"Bj8Qe'7-MQ*dNygڅ$x;>'Ld *7z .7D[*MzkbPd >M6$fN:�jtY"9aځ=M#in%:|y֙]SPFIXl%EfHUr5!'L &KpZuH̙!뷙ک.4" ̧3iKR7@rTPwϙکdd3` ZB|\i8cY#mȞT9cQGNOMfO N'5퟇FFf<B>YLve:$x3j$+2~8+xl5<(Mpo -\'qt\9$]Ӹ\)f%2NAG7j8<n ppkY|2[6�p> $㸩*>l=sEX.bs.=ڮ)SVεD{#y1UR܂]*?ၸXQ6 'l�)ujgȁ 7*ytv+eʐT7\a6Ab7q'V']M`8vf}$d1j7чڽX7'56hZM!^8ғ`V2۠y;uu jIu?J D Dc[wkk�T̃'I8�1M[vZƓ&0ARr"X|BHF{jH# {7,\:7�ly昞e|� \mNS-q1X|~5)Q@}!tIix΅"'*$oOzF'Qs* nYedn|+>̉@Gx2gN&&Gw H]Dm0R2v : j/rtwn"7 D07%9DY^X:`Jt?׵*,Qr>T gL $1 F9 :ZB΀O]DtL%:qە9 /.3B$qpy`Y>1  dfuab3)s +�c;j)5,F=}ki_ NÔA+g^cfeP]y}pC $[` z RĐAQ}谪&L(FJGКX1{ɒOO 0G\j R7'GKs<$d�O=�jgUCR:՝f-z2RgB8�5 J1L$g~P yӅDޟ+IGBNeto\Ib9%\NF/~ApǖCaN㔠l{|vj eOWwූvvZ߈q\CD'?Կ)$jAV4cZAHP-~Rhf5w *d(u`7#Rb9V-�Nt )Zm0@RAj_JsNUVgj*HҲ3 ;sJI<3Z;mN^+aޫ.mYn@/7Sz;Ι?)a^ZI\?ZHh9ԭ3VQL7ܣC> f-!cv-;ӣQtVEaq *-$׸WwYcmw77!Xg@LjY/@cIj_²Hnj"o0NXۀHuV݆^v7MJ$2N2�?J`I鳵NK. t �GAIZIŸ$9|(jq8TcHMh罹kX�9\ں&l{YlXtFUo苚}"[g.FNY}uk+KV$EU-p(T ∁ãUfϐ;Kl|^�$Ep+_cιyx6E'csβ1V M#�wcثQE, )lki�=uv]#z07!ؖr>`#H2MV>+.wH];�뼷drɢA(Or\  | 6:yJR׾ =6? F!s.ʲI@4 D2s=qK^=SArVV񼑬0to΁ʨIafBܴ%ɺrh9)8Z^GY[Xrc#0IN37,HIJx3jfex�V#9;�}6f;G6Ǖ"Ih&C"ARTGyp` p-=v@-g`� �c>#TFɫVfnBÞyoL<b8\#&Rnsޒ ӒT�9SS\ck%Yٔ+ ڱyĸX*sv&(5#rs:{߆+: u'?Ojܣ"lt<b1JIV3pYRtQT�s C E$؟<T"e5p\z2FPu09]38%!U-twjD g;Qrc|sVe F*.w*r=yڢb]iV;Cjv<U�U Kd�j٣'o*]0h'J69.:`/cH 9m'ەRitc8? c];**}ű"I8\uW6 o *JM9HbL<%q8 MybOҋ5iF*Gdv'7!vߥ,Rӱ1(1 4.Ic@P13�HE�_�rD�?.<* S: .C�*F0 7a_PzT$'Nli#2e 2=UrZ }jjgU;<N%eUɛ{rdr4;%L3|ΣZ<r9 m#|*5 qOګg4-r(ʸ9}6�? ^׈R|޵q  An˫ȍ#'Olͨ3񻖰wDA?jo8ykl>U8k&Gd\JǏ2kde{-IJv, ~^B֑=݅? SH6MtvRP|Rf[^K4FQ}Y0$8Ry*2g�dnh%d# 0}y$` BFw*XbN|ՃdסbN6XYB#c sRB"MHځwvr9ugq+Jl7ڽZשãVt]H1]:%hgTm Ӷ1̊L JnjFTuk+/M7A4򲁇NA8un5:ybFصնDIO#aF9 mzbdiuHXsMU� s?JV2<ƍunD}Ju*d*wлOIFq͇*^L8&A֕&3EC�d~8H͹8d6wHUu o 6~áNiHӧo3G%\N~_ iU|*ʘ0T+'Jb)'ZβJ컖rǐ u) J^fr,sڰ񛄊C29l7y IKB$}Gikr<Q5G#7[gcAЁsC s+"0|j$#)eF}|q0R<΁L'bKx>&o))xbVVQ9ӈU\@N9/|� J ԌNSe rY&Y]yہZ*�oV}_-gD  s_皋>L�r2F N35D5.=lNƛcG2wەGN�lS֟Dqj oGR3t&8`1{w'InF~fQ^Tif0s֊%>-lfDrt įc� <P;k'9eV6:sNNN4dNY@+ȏJECX+cgO/@4v@y ctw]?g#at2pqEdI_: 4 |X+ϥLȠ֢1cr2 Vf26J3lp=1OJd+@;2lGΝV@\]qN4VPڕ΀bTcMT.>9U v$l~$r4Zvz^hlJl�Z"[,IBp8Yy1Vƭ?*Fl%JgW \w#}T h!GQ-(v:u03](A$u $iXى? @gh]O/_SAl0#ӧL+c%�39R*¥I>+0#dJqD]1K9 'b}$dI.a.i)'ФqTyQܝLiYjۖ7L.21W,l 9"mzVIh1ǐ۫B'f2FߧΔud%2I�ϕFT\Cpc$ơ)"cfЋ!:G^ӡr )2#,l{ ;wcMdMoVt{j}XR_ $!;q^DHn6>BYH| JmjVU$# ť-% ĸ>4, %aJ_6b!-7iZ5\ >Z>H|2GpIcז+̑yCs&MF;Fd֩8n[+s8V<F繈?�ٚh?c?Xݏp,wqȍ$ypNruoQVa)#ctupK6R҃:!:ŒLm0V9& Bڂj҇Nj*$|8.<R)tdjL2yC aF;Pυ5~&ދ^ I*& PGSgN |9ʀfvUUNڝ27H@z;3ԜQc`q0*V 6bʣvN5o#E-LIވ,y5�£#rFv$tMNY;Z'<w"dU',76cVzٶ]\FIFo,2;3[G|? c.q.bj% djԐRmj%v蚓3e 7t!SaJ= @ [^ PyjA[$2T8޽ϗ*L|' בEj/?@2بH�a܅T eؒNhn梎>UD>bF cPrB+`⽄,.XF=zPBsև"`NNT\i>Y<` ?sV3Aީx'OUY]R0ĸV`62s 0HH  xEHe~3x|=k+0pCtN5t44ȇfYz; @(U#W)ۗڣl,K9LJu�s4#X`$<Q1 qm {4~h*Xjy"(|&N)#=8HI]$D(7-nN1Z.(lח ŝ-Wb5 oIֱMeÒF( 1zҎ 6 &%~1o`ǧɩLyjf$ajrrkԬf7SrDyh<bLdj9?jf#^U$x˨ 9'ֳ"׽h`9 \ idVYf*6MX.ۏ4f觔9P% lv'%u31AJJ2XT3J23 <$Dd^7f$tD`ŘG[v_Z`dC>FN6heZ~feRryT4:]\?ⱑ �<HY4rCLcX:wǥT#bIi#*_f)"$J!|XCcw ` jSZZkܤqX̚حi5.00$ h<N :_ {<J6 3`66&0Wr, �n&,d$�z DqAB$�iBYL0̣ćҔtWTe93\sb vL1|cgQ#:kBBzWS^Ku;W,ğ?O r:6#H[)3OCϡF< `NV~&[#PهQʤV %F[85+F!}y$HMFa4Z敢m 9j tIdv:X` 3L�P@6+HeenT?oֺ�W~Aun X ?%6A:S1G5GZ ;B䓁EKEp`Ѣ1*7,7Nt;; 5屯4El6O^ M,p3!KZMi1e)`hȖ+,x<9%A-ҺJt&n:: ڢ�4}:#҈A*rH+Θr;yTd5Vy"H@% Y$ ^Wd3$1/+-AS9װʃ}D':BR*6ځٮ`$*\QYu�>{nIvv#B6'ZEU-0u\��lu*D٣;ѝݺs=w>28Ԭ"@@ƭ{(2=!Bq Ԩ�:ʱػ5ub >1#",yI łO<:oU p5 0~ l6 F<bn$l9v*"?ß-AgA[^M9Vr8$4zk*Cs$}\tFq3Jy,H89haTb >u%zTth"̜6&\M4&_PP)қh$T5L/Z4x1$}wA5d&keYoނm$bqF2 **99`z,N#$�ǭb+":¢jc{SBFqUo]U2Nd 1蕣1sRAb_7 nTu>3\E# bF.NXd D29\=+H$ˏ1-d `*aZN|OTRǿ'# >{t/iAHwkrq)&em1_h1K(uI_^@!6ht>�_.(� ъ eDtʶ(FIJ2w�79 o@s׮ԭ BeS8:ض!@5@i+Rƅ,71G}'ϧJHPFso3BKν'!p)bh XҴ>E-%d$fb\Ǝ9 x8%=]?琦T[=M#wcI 9zPDž DG^T:tƠs".qIVzꆢY�fX1VRK΅*KyZ5e ʶI!MHp嚡D1x>q"h~Cj YT2<'$*h|)YR: oa3pŤ)n3LXѯu+i'(PmNbAѲ9ÓEWT'*ω%v$Ve*"N-:e8szԎc|B6Db30b:sΙE1kkvr~pT㐮5/h6]᧻71U3λ1ϵj<!Y/wwוرκ0EMrheF׌7l3z~Ǿ|.,ri|'zݘK} cz(=t?,ѹS4gֆ#|PJPڢNTƘ $b'ˁ1^lzyQ7r2hm~u#NJrhTɕRjv{JQԓS6.ŗ\au<6u>wv|.& '<ɨR:t<XcZ~%+<\d<nylYW g>^2b8H]@'Kwx' L홼y{,rd*N}=(6lH܏3U7EeHܙ 1؂=ίދXx}䑱̆?x DÙV]Q`<P3z8VDm ޖMim(�7;zd}LN z"}SCC xNP##3&#$89ǝ4@33v;>W^9m�70ҾK~LBCy欭 SZQ5?($bFXDb�ʍFXdCRSay$NPdӍhv1lQULҽQf $}+)F&R#q(uc2HJ$ ˢ`*O2Ɓ'9#O!X�pX<o8Q n&u0\ǥhf# 2ws"0#*iPK䢒0z jsIeW`ҲΕsz1r;ƮNʧm*@ ɬŠD|s�5+M+ԷUw9s^'~k* �s9jn R/*p*8?5|(wر$g+�:g5lr Z_ PIޖ�aY"lJ�*9(L2ڷڔtcDpY0p~hɉhJR\Hqj*%fyh)$)y*ďL�D�mA�9@9JY!;88i;+=E-LH w'!29WqKu.5!a6uË{gۑݍE.w8SGJR[Bkxoڍ<#Q92倥ԑQ]DGf�k\\5~"EP5�yQF@v*kh❻E i~2R 29P"#`E;֣4M`Ս7 P3* v*R&HxE&M[:SQIF@UJźط 1ȫlH KyLRgm<9+[5hQŲȺXXi|EH wdᑟMp$#С@=)UAiBs{wKHvڞmʖiʁ+GTc;4N\BEe<J2ymD "9M[0>ٯv"BTs&O@kn4 oc.p(ڨڦyƠEl}Iv4R5]a!$6rq֒ML8AT G}t֛^3)I!Li69H 8׷ 2N6=ji$n+(49]@n|-Ĭ/Z&T9~=yw�C'{pQrT=J$<ܹ>AuYEu F8#>ƪ~-iKFcasesm{Ҏ y*t =ӆp%7 ;J$xusҩ`Ŕ]=1m yqynrۂ|/ſIMQϿԑj{=f(XI(dW,PXsR\2K*e$^~ߟ1|A6`?Zj/h�kb]t4 "I /0琬bK`.Nߟ殬-;б rP=1-g�=br䓿ZY `(i5 H߼FZm/%ݘMZA#l2~{6Aܰ�sN\E(ٸvѯZOm5?8L_K۴f̠ Iϖ+\l/8tlTjGB(ZMmŒې@�5Ausk˓֡C*W|V3Jd荒p6:"L1}P$@ϾA ; OW)L@`]t3T~$$+%bRL5X|E* b ߗ <WO@Pc7M(DgClb1FF&w`g$B<K K~ Lw& >Җ�Ϋx,,YNOq,!~XU=IpX_5ɂ 0G]�<hwph _{GC[յ3HV  )K $B�Hb~lΰģ@3˸KeC:ªjѥ6'?z4!T2-u$p7S=9Y {aUl~7 SeSCgZxdQq+źh":GYzJ]Cv|@cӨ(\ �F);A4qXalES؇rI`�+j,n$X-ZCP$ Ǩ`!Ի/^^>n. aW@j;=wk6I)WSpXygyI{#.H#YI\J]ڑ'PsBX`wWw.nLF8h1 lz`ћ^7xYxWL�cd]vs}%9sP($Hi>uf,ۺ2,NAQc�#oMdžc5ɠ�#M9ڃVh9N̻Fy! +hfrw9N_zV1w!Te>\f镶Ls8C-dЎ1MBudc4zdf]Rc0]@D \-�YlF{Nևц1-apAZ\KX]qҗUA;yѕ:m/3ZMۮM?ʝU� p*O*U f0z^Qlvj"Rͱ`1(-єl61#N~L0qӐZ4rjg}J,8N:FJʠ:%B1[]M 7 Z>k{SԱ<hQ<)lKNIXA&8{4y8#)6}dy"`eHm? mpWS o=o ${u8]* 4N٠C[Kpd&d.l ǁ<? VFUvB܀w+:VtG,HN9J%îP��ga˕= f-o?"҃*|8>٣ Iґ u# O #Uryƫ:K9sw}1H A=(м++f0iv8[HB`[vަ GƧ'LT[F#\;JdV@ 1gʛhϭ5n6cu!aXyQccQ@&+,WP z"�nNk8 [IfTHA#ls"6Z7{J .}9�oy4Ou 8 ##UkJ-d.˂]HYʎpCwis$7Nh#mExF'ODOs]f(x{U]+ X\ Ѣ{cS*L #=eqjcl1s9,Fs[Z]\;ۮ :5�-n6βڱIѹ6'΍xv+um 1cyg֠GEdx:fBV5{FEf26*jsB!՜oK*FY坍GBNxU#WIa!C)!qmׯ|w*L_ cz7A fA#bFƇ4#bppT ֩8lw€lO-K,1q�6ɭ q]\ZH7FMGS]xaDz8 [4%DaN;J?wxRRW}d O8ƒ}pNqJVV(*R%fT#<AhT^rbZHlx_sNL=M}dSr=?Ŀ]<rIjBtΪB~_%}46p&8�d ֯d*1E¼R׈[jtz~cmg we±Q |%jrt99ޮxj8jrfF>Bߩ[Gz;X QF<cΤ8$p"c&K@5w}{yn(fſ3[텥^CqUH ~ҳ n|=zDJʹ8i[ԋ)ү鎃<n"9 cu:o@?Dzj{5.-e",ݍm<#lcՏ +=m͑G?;}XiƆ>Z|˦9V, xb:0ڷ99`y΀CWpˡ-/d٢ciamK+i.z 'XK<641]Иds>>м їF'=<bIÓ&?ew?rwpJC |pF@ϭ+{-k4IbJDH\#F<@d9dr8' V= Y\ ,g MIz=l?uf`Aژ{YqkBPκCeF>ZdR6!&FiV mDB͇f�۩ ZV$ <P8]`mǐ-5懢2D8P}jnAǽcsђ"07;By]F?o*n8Q<N<i\Ĭ _3LglmCS5,ǐj~Bײ|]>&* CnU phFm@1$FICy=@xe�P +*zn9ZLU}X�6+YdI}</ђUy ^HUɅ G.])W9γ1▱֑,FK;=s^VXY9fm~tR[֢my�d) FU45:y;x$ԮgEuDG>7$zmDWx2e�՝[[-^!"܂Mb7BG޲�xK@*G/^@f8!kH+tS,Fge p[<{Wm 3w֘27#»=lNWHc55Hpk9q!:@ccDGV`9cSKgI:!w$`42p:ߊ<K\zC/l파g,J2_W�4y@ƍd2No~bD[Nt޸k h YTx�w.[ ^FyOriz傊sa*r Atw0\&3@}<5_: )56pvlH~$A]Tw(_[  PGYpnehY`d*צ tU J:kn8Om..0,p#;=Gsnp8'HY£ 1`I'SckckrZʗ{�(0uxm^-Juo[>ϊY)(<Ƞڃz740S5#g0:T[CNDHU*,rq,QAs!}3j\@dgT5j܍JJk^5e-)73MOqrO %쭸m.#5q'IqV@q[Dї{ԣoJ/Gjhh-�l D^DaaF�R-q&Ybӓb1Jk<ٰ>N gyGtc87­}j 5=1["« ,/Np-R8ƒֳ4Duxwy{R<R04tTHb2kS0]�zmkXjǦ+q[8}&iRWBX&}4v ._G1c2Kv=j~k"হ%ׇ(C\?Z8#N^f"B`zj l1V o#@= MClrZdU Pd$z?E㷶,jWW\^׈ &57yW)\5uo*Ǥwy}:b]QӤfЉDy7,F9U\]{݀G39W]HymY0T \ +&QH3TY'8POJV>t,2LHr^aqѪE:A}kzfi Jj0NGPQX̤F -`6ޣoy'p&pEH%!|i 2nD֓<0G$QE}]c:[*hU/Dt%֛8`W]{YىwF“+\Y^wrEK<*Ӈ^B<$=Ru囯ڷ81y:팰+@ A{vښRyWBY 98v6F@%Is�aCL\Kq٫0Ұ#9V@U}\�\ۙlF 0}+90qzG<r%`;M%hĉޅ#8>xwȫ2'vA$H;{PݧDHUcwF?J+^|*.FΑ*˂]MҲ&I{2C UG |Y?�1[ 0Ů=*|`˰܂�ƙRm2#PWcG2})Nierfx-ȸYcO͝䪓�_!c,cF �GVbH1!}y8l\I7$eٜ 1\ c8^y bFex9$gRqc'\Z{v[Iej rm8d\I,,4=|DI"ƨ@Κr5!;E@]�>|ۘIt�dBH呥D.!Ԓ38$rBV-\'sSHyP":ҺzI%S#,RrB�v;f.-YvhQ*#`ȐoT{ ?M?jumiB dT3zYHr P6�I !瞙HD_8Tܪ;*zsñ.#=(6рrEzIKNxV2⎂Nw\#<s"ȍkg<@]q%Ff }JsK8@dکQ}�K�ʉgop�צ}=Oַ. 5 0x?i|-mgd 2榻7f8qUhN=z⽚<c<^$qk*TƵű;gvƅT#=@h" s;cG?ֵ�q \~ߵ*X %VRY;k[}%X?\ nP 8�불8 T<po4-ojYR~mypA?)CSm\$�F[O EܑG_:,Ap t5,dcz_d? 9,�#JyT Niț_՜, !cz HoֽEYdi 0�"zd5dg >2c#o%v .Y+*x[0ʏ\sU\JBaHd_1V׉@H mZۭ#1} ,P29cP얋Sruʺf@I `!ӹjܦ5Hj$ȭ-ԧt‘ɩBlii]/u;﷥2(frt8.Ib]-hTw:Ae*6Z-]ܴ<MPS*8 iNH;:=1Fr}2צ9t_ٗ"m</4`H,rJ(@"3ƜΨxލ%f5Ow$d~KsxIPVK*;琥Hun,J;ԃVHus8` zԤS~L_L}π:Y�!? 5 BDiՆ^-�#D[#Z-,;85Lrmgot>q_t/e=OCZ.��ln#<TY]/mXVdA;%aha$=kI$Ԭ�.c垕VDf5o֚jKB2x$na �'ywP+!5cmS]9EVSXB +&4 ~}1s֛dBlX1ej+Fw:Jn9$.pA-V_BE vVS^P+<+,0Ჿ.I'v!)*(I0PDlAJVYP,q �"gq 3?Č�+è$EDx@n_e㶝hhmk'|"=pk0Njz)[!i9 B^BGANDݹajfc-eE+P96%HyY$}Kݣ9} 4UP] qGvXB32Nƽ <('<[>Mm_$G!er$fC#ߘڥuq ;k$عU^Xld`�Ms%{z^@;ODǔrnNFs boQy'V ׌YjPqr1X+Kaȁʱ#5m4@zF(ĀBcW4$'H8-8V6jCQp>[φiwFKT9M'41+m\@GWuM| HdJnZ>/4nd H>pK幇hg$c<�j8;fʛ#G3Xl.-xoh k  <<3s1 HY+OWd-wC:N*~%NRe=1[$&e'*]]DSa|C#'�UA�veev<afR#�o-qYgprK `W(u(I]ˈd HdUYs'rEqׯ%R*?<s4 r3,9:~5mbVn,B@Y4# }X퍥\�kM٫F3 +0ItiCRocq #hZЭJ`=9V"Cl$qˑGjF�aMsI6*+.1Ifqm- =ǝ;l.u ߵu`Fw 2k_8'43,sq^۠שQD~a#D 0lt<fI^늺ef#.JHĥd`ORe6\8Mⴻho`�IU3ʏsŢG[[52�t[ lSRѮϳ֍g^Eq%U%R?i<UpLƺ{c�t*$Hlf*`qr �|ǝq8(c7V NkL86'8vʠ9 : ;|TYѡ:�_W] WC?J^jTzzl}(gj5Add6TS(#NAHT#Sx� d5cZLn'!;z�o=YŜhⷶ tc`UWc8b])>(926GJGI𽲓kXv ◆ } O-J0i,jY~-4/ƥѩ�Ħrڭ#H҃QNb/Bn#M*e":6KiIW3BjoV)BO.yKVDn⪼Il/Ih f/cZc+\Gzt�mnK#_|h[ m+A*)y~o9&g󒱰d՝I<s5CgZi:z`#){y欚g/ŋ y2,2鞵cl}N;H ؍ntݹ+-2-؊ZZhdf AH>TݠI/jmǮ<0$&<dfBKcoZbd`�1=B0;w<Q$ٶ|7 EbK} pq�Vt4"Œ`󢂨y*ȩ݀Jfjw5 CQۖG,1WUp5oLCYpN ($]c9_/ 2ϦiUuc])X,2>MfmՈ;�Hh%Ϊ[ПZ,.)N4 u8 T1 _E*j<cVaM `@ `j^ R;NON 'ir.$�rpqYұ-[jCUf,$Ue8/.&8eY50@ʴz>Qlst8TL\q_I7|^U!��pɿ8{W68i[x\Zo[�eSUH ҽm�'""@K.bG,qMg7zݯç6UAfϭj<oX]\%Q|089% d>pH9c|(.va"xO{2qQ+r+RR:׳ x0Em]FgUd~ÈbX7~ ʵ .ͤ71Bf Bb c,V \#ޚpwde'S po'�asV�ÝQP c޵jZ=̉!HQJv F]X!`IIKqDŽbqOCo֯GI\0~["pDAG369{<-sqY#n6-d`!U7 3pԋV!-,>6gʧ(m \x/!VN/!F%quW{Sd#;ذ,;ǥSg o5hl׾P;V5X t\:?vFy5#`mӄw䕭 [Ȏ3W<V<2hlt޷Eb1H*dⵎ? [[93k]V3bYGP9SZ*嗺e�8UL;+DU�8RR0)Pv ҶAS׸[IlcsNZaoxM En41;Q:F Lf ` p4v @b@Pxh"7+7"p:r>@4٭mB.?j.+S*7{ܲ�i9w,#vUq2,Fw:gV98Wfw*8A4eAҩ{mٛN l֒F1k?]M^^OKVo<B:ipl֧q}< č:g5e9n#W8;C͆B!m862qZiJ4fak弓'J^\08L"X|ؠl{H z)H0M.?Wn}#TIZe4ӽWR|,lzҼbm 4IwrC|U7m1 }-rz:"cl MPDUݔ mWձCU^T@Vc:}'ޘN!xD71ԈϮ*1h3q p}QBIAo櫬ofS8&vR5Qe#nf7rFַRA]sp"GL5,꿇wKiE`Euz?-:y~ȉTHN['b~wQuJdžXেWtĦ<`n7tCVfGR#$=q_O�\XJv2(Mg4Q;A `sAlWJpuTBV 0KsAOY:VpT`﹥FS8#`H=OQfd[4uU@pu�yQs c yo�|܁64'UILhzgNfEf#&Skaְ; ]J%:t<E# #w7zD1hJ2GsRT3;y =}2a>VENJ($鏼VfIrqLLK8f0ڡ$!.4a³GXT2X\Ίleaegt@̚?lSVgWslzĒ*<IYS5Vl4gIW!+zf$opySE y'OJFNFz;}xK,1"G:I;ӕFx}r)E+U3k�&q(dM�Yo9mu4r s=ݻر54s^wxzoK-b _`MK;~!B zzv\HQ%Ca@W.J!}x^|tt01$Xß.d/mVk)EPZMGWL3\5̒HT(, �� $DRlEKeu:4Noی_؇%Y!c6Ac-ƆܱT<6۴}! &&C�Jy'?w9oe83ij¹DV=G5~cҺw JX c51ptӎ] DlBK{Ǖjы=g"h&x@qIol,_$_#?Uh<?˕_βx20Fcn% rGȃEjw\^ d#H\'>CYQm߉cm>|K!_�1$lY5Ѯ,ȡ|zU שi����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2014/�������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0020560�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2014/05/����������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0021004�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2014/05/14098345978_c15d12f19a_z-150x150.jpg����������������0000664�0000000�0000000�00000014472�14502137606�0025621�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 90 �C�     �C   ���"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�Pd�5pp 1^iX^ik7jcN_RǁbL"|}WxgVP[ �"H{'י .DQ0MMkDCܧ~5 G; G#k/3ג隮kpͨI2ǰҵ5Һ(8Ƿ ik!vnuٯvPd�\n.R_2?0*Nj \ӚMɩ>0dm dRɚ՞5W?} FRXѣU\g늛_<8v$< ~`O$Z/;y2*:jPL>`<.W.jq醄=ҹ3O%v[//!8�?Zz0)� }>pO-]7hlcs �ox~UZ+F8$9V>cnJF;WHŗyR=GIVK<\&puڦC珥u~k|8�OZh/v2,.1j## .yZp.q٨a_o3YD7�d�֢Kr^xl,2@֍o6`UYyc0j W"+h Bטh5d.2F�Hsߚ:k-Q{ Wg&\ ;Qdi<dUYLo` S5TߎFr,F)a^:.جUpCvZd}.AjƧl##p?{u|ׁ�ZعQuT뚟Wy.$KD2X/L�*Ɇ]0`#b9 p[Y ;L�ZɻqFr72Lyʋձa8Pľ��ZWWՍ$*0ǯjuV~BɢZ۰7:~j?AlN RRΑɹ`kWJbI,܀3ړ$OadڜYuo֣ӮdZˏ69\JׁF"29g-?֟a9R\QC,ilaY3 rzWmP3u 9l}AuFOim?mlpa>ne+6N *Gҷ1KF|ஏj.�QnAEqVc@sӧQ^qN־#?Psw\Bbb~ =U':ո-5F-ŬJ!}nk\u 1iGq) (ݤ!@*ǯˁi{I-m{Ca꬇>Y$iT+-cd"@w_ y|;Pé۟ʣk9w=t3^8MI&@>"xsLqsba  z /ir|K6 e,ҸI[=cFЗIp w5 }xQ4kڸb'+ߜ�j,򻞌~Q2Щ;GֽUҼ *㡯0V2taH#Ѧ8 -${WoEyo "G(g{_E|/Tha |۶QzX\ Gcld_0SKql\h,zu|g?]4&Q6A܁~"3T0 q&;:| 5Ŷmf78FUD!~DT by篥v\Ficƭ ֊+ �h6n5FW?FN1HZG[8�ÜE h$w5;(ڲ3~%R:eA}_�#gPQr9Ve5ihYbr0�Nd `|˟\‹Nk ]KhTt!?CJ 0S^ 'JxF1rg+%vTSbpI'ӮMreLc0Z>Mf n$O-5x95+ٮ%vX^y`Ԯܴ> 4%s<sɈL¯iYQ WiP#9>Ջukcc۞<CĒܨ9&?HI^ wυd>fh$] کI<<i#ƉHbPpHUɓ3Ųvu'ֳa+fmOF10Z)pQ.k(+98#k#7Y>XXY} "U?҇P1֊W x~)*e8� /�xk6y?zחaֲ4o]&G0o_»pgGC1WrqW-=m5XY2<FN}8W>*QiSZtT2W qQ[l&xgJFEnܬq;_*㏜1�z Y,C 1ס51u dЌt ?t30)vұUJ2gۨkh"d' fԿR B�[x)0h\zi|Q'Ğyx{ܧݫKd ; TŽյ,>q`c >TZx)r#m]rb>*(4$s'#)+#՜&?OO»[  nztcxz2+}Eb]xlAt>=9,}*Ւg,>b $ټDJU>*D"56;Fv WDYՋ0Iq�*|WR$qMś$+xjq>Yǥpe ;WjZAucןkVMg{ &!hv� [O r>� oE{6> zQ^)>*;9pLyN{Zo."ˀxR?pV22!7dMX=ޒXgGؐ;[wQI$$_hSO e=g kYEl97.H08w^N3vٺcm܏ճ۹=(E$,1q~*vV Vmqһ@Ukv9cXu[jrh<Q�I߂@O˷hBhnx5.b:ޠ%{piid_LUoɋ#c氞#3. <)n=kyeK߉ɭv:N4bԮ9SѷR@<MmEH1v8g٩+i8WI #\WLY3&NWcp*dfT9PIzcO[x 7eMg$r$ܖ =yKm!#ӮQUʹz%sqmr8#uZ"(YI~UhB7qaQ#ν#D}P>|R )kG<Eyj͸zW Q.aH3s\913敏ʨ*t�βRsT^RÚ}-8Hmýr w63�dzf_Hɬ5(Ia*7k4W[+в7;kni<=—? ? x*v(SX5HB=$p+D,''$wmPf#`pďjDOlƟ|[Ȫ؎X$iXˑHg[opO NQMԪmlW^3, �N-̃5J] $D�xp_5L*`X=׌-<z6.7W/\oj3Ϟ.sԼKa$}h Yg֨Ǜ,T[Ԃ8.A#^{O'nv|#| v)z�y+[⯍e烼'(vsǭt9is! #*'Qyes9-u[8Mtv=bH#U= kǨ}I#mu$^4)x)3)[+@xs)$z $޼}T"dGA}k$plG$g2=х`4{-އZk1ā2=)ڏ-ٞPZMn-l�|:/}i*]ΙbhoxƷ^"KK� 5tǷsw`s+~Rr]5k;"ǰmݴQYLGpuŒ<N>fGJRvGOlW[{BT rQ$c߽z.KGҘ{C#6XWt0mxs8>[>e-* drp?ZaV.kq Rk�${q۶?"�bpi'9? tJJaӚ<UYsXxHUAâOlWkZm"W.vp⾕]*�$@eAs5*FzuEl3'7J0ŧ[P@ďz+@%Ǯ:+_g`6~@'09 �:gY]b%yV2Ėleu= wwQ֩6YWk[*rc_@G,IlY6z [IXNEz|S$Y{UsYAr]g7(\[:H &h.$`�9k!T1֨X; 3j͞ǵdIeXרkfJaw5/T O6aW$`X{'>ZZ!� QZ„S {nxn.#ҽ__oBE{gWx{6 qo h�}+7A?3�pkҥkSp+|VK!9 ,�i?0q$`sp[urkEG c0xB4&u=[+hF"�(T¦{=1UEI3cj΢K8b�l?Pè7N=+k$ck[4l (8N&}8uO :S$Fx8^<}(%vkB$P#>(T}x*h|m7O�rheX;o=0:%yo"ݥߞUOûݹ9SN5H�(Z(z+qH cE lrN(6O זlOz4n �袪W59[sӼ4qYWo@tPh2H(J C̛mEI&5V�!(?(sYQi$rԞg_3;PN1EBFu8γʎ?ϽYIdD;�3ӵU�UtR* }FܨhCqE"G#6�'1ZmJJn3("XQW9QE6B?������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2014/05/14098345978_c15d12f19a_z-300x200.jpg����������������0000664�0000000�0000000�00000034502�14502137606�0025606�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 90 �C�     �C   ��,"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?� &;EW>`zjߋ�bM&zw\i anJ7\vY`vx&G)`Fϥ} 89u>Tog\"'$8~4OQ?Ҹ=JK`N`AV'|;?ih.SԤchd )OJHug}5oGt(ZQ~5Tv=4Uٯ#&{ҹMN �֤X@>SjP0[MsZCm Տ m6tq}hK-IdJt+bdf �,S}y?:,+$X^hKYY`C]ys x/ V{ yL 1pz+oxtu3 �zu~')QDŲæhWbYxQpZ/1 c$J i].ʩT%s)~YW^%R6y+ <Z`_yYj;[%˚Fe'/FJ.}?_S.w*9UHO Ȼ#g5x7 ' յZ �bIWI;|@8a!;VMQ|], 6Z�Nu\LJyY6屭_EciǷּURYSm <dg@OY] ,EUMYnC,bQQ !#DǓ OLp=�dž (j8|i�EtGq;4<9Թ6yyrASAEc Qm085:n% 6x\?K;T`T[sC.nl*՟.^h�vOĪrp{FXr Jsh(> zoYӃc[4SyqI¸x(h[x[6 Zn�?J视Jn'sXF �G�>àB$I?Ƴ|7!mmp!ou#Ch )&WO4 B%LsA)ۆx3o1O U`lvUw©1CYآ ynTaGdvģf�=L~rhyh*mE")nc\Ioo<+8u4x/]8_0;7ʛNyksN!sn,ER.ei`rv=1Z"=>dz AwGqU<RDG*q^}K8x ΓV?{@,I99m-}RKcڿ'U]* b?ƺo Yڂ@&sܴH)%5*0=^s|AyLc@ésWUo?w]Yw fY@=?SK $jc3�LJ?c9՜Զ[Z(0>v`t5/q֭-eat ǔ?oƻzB n2NEnV*HCumB0mnp+l^.O9 z"l-N³麣[3c`7U"k9?WҎb\/Fm CfaPO(ۊs2KkAHe {Cw[v<' UV]""TB~S۔?ҳo|9`?4ĊQN b!Ie4~kh@@wƫEg}7[Ȉ=q KV_"H `Q^"RrGe?(?Ҷ d8'#O˩)elj4sK IB df5n7C g� 9q~5Ҿmn;n�/8_Vp? h9Mw,2F3tl"4`G~|I o�=_M}p+8ڭ3JxY;_xmOPtuxfk.RkvYz`桶e֦,O8F*gs/=CLdkBܧK&E؅7QA]֖8 u6[s_n^jz:ɈDmsӥ>X=bΞYGsiw1&�\pPk>XéY{ָ|X07O-ۯJ?ӤD?l PFzWU)ΞS^'2iQwU$w�=4O6t!ڻm ?5ìXk[})QKƞÛ͊l]F o=MUV+.֗VxH6?YZ'yXc}T6r[,66xCoUUcgwQ�]v\F3#8QJ3R 1Xyчg`>⌁sj J1w8ϵdu<ŮI(:ֽ.N /=:]{iB7 Xw#nj`s+rpX#i$,ǖcPM,v"gasִU>N|c܊̓A y,7-P39"@ޠ=n|-j1֒>a1z|Ǚz kvYY˷eiL[eOkum>n,# xxq�:mk{-rO*\K}yCk$F>yxBftOK yݎm�(p{56.@FsqR6YHa޸ 3Ʒ�eEyGZo (DZ4e6٬v#;%? EQ j+ {NLn{�YGQ.AR2 ,ei8>\kR0.m_Zoqrz Bm $Jk6z(KaG ;?Ƶgԥ bsjWMs_�kgGQ'U)ml, e p:t/5 w)8[_݁MNG!p*ØCvz ^njӌSZ +mht<IMf~ ^D^EjC4{+U^>%v0݅W5CM%vͬ"$E�'Ӛ nzY LvAz�Z2H&5M&wF'*uk𞮺2G1[s3Jw0:NR7-z{Ԛ߁5NmT08+|"=@~wq *;gnM>_Kş1x"Kvv9_c�Z_Xҿ5Lt` _(� {co4{1O/iҽJHt|}kBSs՗χ/wFvɐ95xcS3Ac@oQk4ʻe)g#�q\FP?+1a<^{B5da9}9ZW~,uq2#Ub5v�>E$1lPŝor9K9=1C\ݬ&ɭnC�HHa31�xW@ p9jxnn�z%e ͟rΧDDОYM# ?AYAg `uP{%UFGRǩjk{T 0I +dҹ[mF|7/~GgWS:E%P )$mȏn@)B=*<dAVȄ:Hz *Pvn�G;[1Ǚk>-*3*pWY&)_.٢qaOv?J[G{5ZN]_CH LRkH Gm}b;DYب@8ܬ|<" k8[0ʊN(Md|hw1] q*Q3٥-c# _~ZkwK!CH0zTI\櫄u$;! 5Ff }jyMgRaS�<5B@WEHe %]{^$n?*/4HWkv/ lb8>y}%:ʛFx#PI;F 94\8g:\JW1w7wk+T %d+�:ә!B<H�b8ȭ+|Dl0S;FruڶuЫ ɏ^ơ\#M&P4wtΏO;Ǩ[n�].�2v'LcRG`~cή娭/z;:r^-ne T}ץd#SXIX6K�qx]8Mwf,71ݰX<Tڬ;U}m4m 4*>}yq#l 1:۳,N�,N}}ԣx$MUWdH8"q� jdo|_ɐ956e@Vt9Tn>b9ݾW<C[:gkye7FK,0=ڻ]k �cZW` 1t<{V36󝼒^zP һ[Md\Ux,=}+;ƚ[n?*';氣kuڒvD";9F9{Rsm8 vJg6>y;w5ԓ[tk8~W yQiqɪ7%x#IkesfQ?feFl1VQ}7�c:>永(F8RXvSdp ^(M w5/`ch.Cjd7" d3'X(ϙ�]ÎE>;O8h0߯Zqkfi!ndSg{u·' 0[A P!OI' mD*X\Ǵ, P cxFv 4U#G*b>Nt>*͢\h\z q^5 w-0I©s$$m$-݉aɷ#5L'yYIsI%n59-䕆Õf]siDdAtW@V8XIk:}?Oʌuў00VgWMm/u]xvr}ވL2i]=g8�@'s9&)2= Gc4ӱ(9;ytA s^lVmAG>nvWϡׇët h[B(H"մ ׍Éř1\ ɒ>u'.--]dF7GU:ABKo;3HY1Ƚ?ҹ_kYɎ41ֶ뷦xv䏩TOG4tNǐizGXJ9~+Qs:J_ȩ#^/\F#P157Ftv n?<OII]Ky2;tC>4tGYՖ5Qr;yմ/炌X{WhdfU߂G;z;׵s&utVWf� <[MR͹C1dvUIfuAps+M"TX9=;ڷG+9][C7;@RIc);*cwmk16LcSS{iA/<?]PwYSžVG#?$NڍZ]<dG_z }NԺv>u>c$G dkNM$G'6fe+܊N麣GOv+</L�E<c5JT.#̧q�9:>2?ukI k&V k+ͦ?:]b�QZNIYA<' Udwb-Vv*m6xU&z_jP؆0~1XnUX\ƈ4wc#XEXw."8r 7BB;8PX3X?BˏjQi9$±- m�`<V`R21ߊܳZb;ϒʡlK+*lfLm=+nU$o.tlbYOOU|z%G,Τ$j0ֶ4+Ī2˜^oB;@G(k"z 7.w-4rMrv{! ʖFje&ާti(j{gڦm$ ۼZcYOW(zḌ>Եt&`kV0PNӃTy"ں,FE^I@b+%c5,ɕqȬ{}|M) ێ|UyjF('[SJ^aGd U;U 8ɭ$U:bD(r{VF [B�ֈMƱpۗ!`}Ml*OszO<(Eq'o '=ᏋmlB2`_BMH嶦:Fq/Fsm#x$){W]1GL�iIR;+ȱ[gRy5Š*}GLg�^1C,r WSe$+3<ƎJUa9$CZSsoyddOe؟y?=v H_ ݉2Y? (. YP(V Tڼ$*#9\o4]G 5JVz4qlq{Y>dI1;nsF&gA"r2~5ysoCI&uE 32l sLgqL�S]\7'1o0E==Z:co0\oY =>hvGrKc  #hݐv D1  ,{yB`H2p6� SK2%M^<2t: G16ʂ9r|w(?Z;nb $Y` T_mFt%U;>SUfvފѹQp]a*m$㊆4g.C4[RkjYˬU]5"VS9BR2zoĖwbS;qYIjeV\G7M T"`S\k :&BrI>Q^iJ<4hQuYZf5 8Qk6ۭ l1n%5R!͕=s̐BFKv''5iF�0zK5 v/qȶ~]Nѐ m 0(Gwnv)׈M sPaӸ{=E+_5F>V7M.S}&BEF{kA`ĩ)pMkq>dPEY"" ~Q[\2l–FVp$k#Zgk}nk?J[02:+|oJ,kF|eB*3WD7TrSߏJmv4JCH #NF#`OQk0'Ⓦ?RVl&:}sX*A8]*-00T�qZ.sֺQ9 F/"&kvNM�v95Np\G|We}s=ؘ*�E-qtfF*PO+J΢>B;SoMJ7[ZizImGV4-2qO+'%eDpx�OkaF޼k:*EN2VhNoYor2$Bd 9�alL 1<0Cx\y2h)UK|Br㯷zdݖ6fUn2NU~vc<ԞS ڳ{3$,R_dKΜ%c.IP=k@HUt<OY~V�+6,OjV-eУBǧyGV+qt'sY. >d3FO<כ~>*¸~KrNzZ  3vc^T6ئ ֤d)9F 1Xtw8cDy.n' JEHMp..JV'c#@П._vbl�msV4- 7Enz\EG$v);"TԶFڈ~GԮ.%2sR+rU]Ϩ8qƺ}~gpE*;%OJ^otkF Ӛz4gޮǕd8'] *؇<mjEN*pq[&rJG$ i�h3xc+2`zG~UբOJ<]9&:sIr;4'WӞS\PA (ismuĺc6x-2w-eQz1zڽ-[h|>}E{vQ.X#=;W%Z9�ԃWt ` ӵ#%ײϘz-,I<WSK4nmRp]ݻriWZ97)�TXvh8 %{dRR(nx=kWᧉ$ˌ~N:+ Wx[̵{k_oa5u8 mxR</=Њk�{?OOz<ya荶24sFբb 1:zSRrۻ=wDEnݓȮ�zIdQ+<*׶Qȳ -E=Tץhzx�1}M�Tɷ}kKD,dJ=7K=c kǂi+,,펙f[ŝH$㸪TOĈcg su0dA3LNvܺ?C[j;lRU�RCXk�`]:=kqcL߈ a9(+ GTQG;j-}=*4`$C ~J)~\9J`}XdTŠ Zq$v)0I?c_4S=*\>Q\u,3 T%wcLpe؆'<k*rmKJN'3Wc[WTW(t чjR4ݘi{wԊʿo]S5~ GM~Sԍni: YdPBE E[�(?gA\KGC;StZ& QkWz'Ww|#%ZWh QfI odYrΕNgs׆UFּkB;>e|Cuq9*M#$"n�0<A|>fc~4|~y|FYsSˬDG,1_<[He槗4Z3UsuKye`. $^⼲SHjxS׫#JC H$pw$NZQ_C)  _xQfqa&x^៎Z?jSr̕G̨!GA{T妧ՂFnn:e-JU>*[xSJ?zKVBrW'jǴ~۽ds�J @A|yW]7,\*1ُNzJR6Cnuh"OZ^U)?w O'\^3&9rT${k굎\ԣJ&zqWn 7'O -=MPe̲b%3cYN#XĥN@9 u0j{"!_n�׃O <)8t<סiUTbLa2ZzŎ8R>BF3]0c 8㪂+ƴ; &H^};uW gs"7'J}s]7-N-ڄHN8'�zW e9-,{d,Ū wǩ.y ͌+BT/sڼz7�=iv 䰮* `ZҴDnLؚm$n['ofmVT%–I܉jn\-V&ͮh6YBs]UJkhF85E qS=YCXX<7+m5q&Y;�]$51NijiB9}+uzzitډDK1>(Za@TvҴ<z.{#.ye<%/[Q�0*ڐ. 3xy%mF_uc\ޯȷ=j�q?wd-P:SUAxJRTtaZeck[e۰±.v޺ #a6苉$*|3Ӛ$ !SKM6�M}]:&lj!ˌƻд#1C]:�W5ШX՟i?}RVHle x W_| Jc$|''vZFuo\$pFAsu �k' IX88'Һ#HvGzGggCj'G5'F G�߸q" Qpg}_t*g,6|/8I cɼ֭@_F8Ղ.G�Ƽp�8�t屛鷷VS%;n k돪SNu<K+Yc܀׊>10xJ w!0=#֐h?)/ԷR�jZN9*-qjx! ~&IԱBBķJs{T\gdI'5Ri'&nUc5ѰABo5{-MVF!+M)H[W,lאMz>ؒ@+�ѓW^!9q^Oaoз1/RƟn!!Exx{[G2 +u+턾XczW5Hjzj BڞkA5pxc\ ڋc4\1+GQIq%G.<t ث[A-y }2~GNVI&h>R4Cԭ@-߻szUOEn y4-;, "[b*ft,J٦zYwY:@|9rvn$m{f"\J(7u X41~,2HIOWnMI`'һ ֭ۙl_sҷ.lzKVshJ5q+H5ΤHY#�<:/-ANc$9W/N=]dRԯ<FGݴc5�խR[YՅ;v9zQB!l?|A#]vC,J`Rʤá�>C9lx΍{N-Ɍ,6=}�H|e)1RA;PC*Aw,bimd*9L%DKNG] G'8]?*›2@@ND"dLҺXLo_;v.)htZ$bٛY6Lu9RpF *q.NގUcb8qU9 gdoȠ|=K:@E붰ZyXe&q�ZΓT;,Cs)#!{sWc8k*\8+r>nW>wy`Nr ْF,1G0-kZVn*G\M1+v1]1gx 1j~vo2]X,I Frõ6)]p+u܁,pf \hG)iOSac8Mp[AR^fǦk7KB͓YӾsW&�Eg?JHH|SE)j3j:=/]P^k|5KAW-ftdszVJ.UlQbx >@kq]VY&zk5M+ F2O$זxkWkmv{Wi#G+\n6gѯtkxvGȻG�b_  dX`\f.E.aR5NKrl9"/D5y 6Sp$z?ZG$apVYT7g wQ-EɮŸ /MHy�26?Z xA hw$mc8=znyUs =h_,-,mP²1|8=:m3{B�P\ִVP0xVrJ!{^iy)nŰ!&eaB_Ӵt=�x8~m9S^\#*LfFŸ[q lI2=*f+yc(Yxp{gO5QCTb2��.ǚ 2X jy,H!rHUg:g�fUscI8'׊}bhe,p89? b;=xfm9Ͻ(d+\shA2]) \Ք+ssgzΜ nYi'6G׎#B9dRA FUY*I 9fLK�n X67s)`9ج?Jճ[&[-cKaO&3..㌍6§{KH`m<�q4 �G lI1bd@$P\筴3,1%}Jk_0ssV)o &?|\ʋ0'%W0shKX¿zGXw^ Ei@ʼfɅ3uodD!a*AδHksyTj(ggIU ("uT$Cqw#"1Ɗ*̺nAej껩ڽ:E-${y>eeoin[w7QI䖇]:PoH^iV|CʁRL mΊ+ZqWJoFv� jl#$l + tiZxU[t2K4Q]19jYEusIzgӊՋRn78’Z+dESk֍\@@Qqh /VY59f'+xn3'E, QSaҪɆWjJ(KeI6dpzVvڢ pAtU!22U?3n6 A8SBz3F~bm*xo(4QV3-ق#0۫g(c[\.loj uN_xnXsT$9fܝ{QZ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2014/05/14098345978_c15d12f19a_z.jpg������������������������0000664�0000000�0000000�00000462022�14502137606�0024676�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��H�H���C�   �C������������ � �B��!1AQ"aq2#B3RbCr$S%4Dc�������������?��!1AQaq"2#BRb3r$4C� ��?�,8g(cOJW%HaL}jlA<^^W5꬐o:�E�3jo5~MđpaRy-m%o;yDhϸ˝&MjeklA9UuGdYjT}2Kv;�» 8s0QH$αsH/ Up>e(͊bX r:%Bm;a}j7g-<s xAcϘg8ZX',!:K]lCw+isZ fl#*yG ǿI\و% FZ� 6Srlgtev6�ϪI8[NQF-A5�_*pM�LSne iN+Տ3 Yf"f.$kǖx&z c)0˴`5VrWys<WLH3C*Ndq9@>(׌* Y gUo a \N]e'9xpsՁO+bakh"n4ӟSi+UZyTHHN*ߌCE")W]ͽoq a.u$Zn6 ]ch5cFRHE~3 y!~4F;�,=}[?TD9H5+L$>9%Ce?^m$F�Pȉ-ߕẔD=lin`,u jD apy{UY-T1"n|s>/q�ěiqt=H^Zi 7>],Gڦ@bw^'^sφ�g \CP,Zك)"|(Sck} l5+Z:L*s&YQu{V�oBMo֓t@T DQ\ (6�;o8 ZN/rSS ?:|C@Qn$HsۯlAe䪺08;ԘYҵj߱ OZ,n9ՕS'ڥ/=1qL(� <grxuEBsYI%R2<c)P곁%Ȝ%\Xb]h 1moPR�F7 �L#3q7,JKrc0~iZt@?+sԋRē{\5Y£p  UKVV :c)YJ2Te5Ʋ)Sju0| WJ0OOMlM}.( -y#D/E]w]3_W>Sv@Mf&A|8n:q[)'*b륢Hlк%!Xb ƥRvns*X(6"`=LLv*Tq(<RӻKZ`HD`D^o�$n&0~u*xj]Ƞ+r{FLsKZ9Ir"WNq�]}REP@ ;laDSʾS E` �Yyۙ*Iymk ls$ _RY󽼽~ k\P੩1e%cu`wz?|M�ҖmLcf@%H*}P*�=>\<Ρ.Nt=a׽Y>]_n:C XXu&!e E\t~ְ7N} a;N%$4J: {[}zc@Ug8 W&poxZ*:($NctoZߴɁ0c*-Ȫ$Ϋ!4ma* 5 1Sq3(ܙڲex<RI˕8ɨ[_¶N#Δ%ER@3c yraѴ=52z,A_,OL̬M%4SLt$|+*3j$wit=~8+@qvYVm UC,pUK(mo"Qqqt7܃tYJ"K�줍K}vSMrS>T.ї^8yÓgYu4ZT*h_fN4 XMգYM!;/UV6r�:sčv#(Af[tkn/p~9)ݘ %Β �&|b8ZbKEY-�q:؝m\C z+ #m;"x!f+%<^܅a}W4V}7mӟ�Eje?\YӫD 6ӭOH씾ۜ ;bȦ]g?U`/tVf*!-Szm0갈{ A}B+H]@m9[sۮ5lHSStIS@Jَ0;wfHQc he@X_rLAEbnN[c$Z ĩ,,ur'll�EL4I2cbXxԒGQ0H"f[Vanfux+'Ԭ%ZVXI!�4>+SJ%mdVf]be([@ ia˕}0QC}eP5hM,4HVyTrI!o *@tC$I/1@9&(2ڬ645]ĎA߸:`t3wgY\s8tixq3<!! Q rӳ H MIg_Kj{2r%z,҉'t,nv-7Gx.pc!T-73yjBͨـnk1*i$qK_xz(,�06c +v�X-V�IǴ�E\ߟ<%=\4̙J~Sϯ<:%zL6 ɧAITk2\w~(q4v؆qѕJ4 jhLe9Ol1F.%utea$DEψ3: Hjʉz�*l !Ę^հW Ih1Vm-G ft4*yA`6o5X�>%Q|kRpG%OMO3],Mg|=tY-X^R~`*nn52*ܖ.0yYK1XvS;7<3"@kcW5AxI!4L[YDmvؖM a4[ ln8@ooK3UNce\KIf+KIM,tI4^0x{UV0ɨblۛ ˍf7�3rF9�/K- m64Q3*PIc"�h8 o8O`]$Yhv qjY\[ kl'=S/K|zyu]QS}ZEC5<L:�kl8T}U9~ QP}a:{E�U;BLW=vyGO}$>T|1v0I^,kCu y+7̪QvenmiOod= (l�ߧřQJm#L\ǧ2QQį;oncr�8dT]t Imk~cGWkKv:H$)$d nE0LBe'&#.> fCY]ozl; MqW4#>U{QO2MHN} %$~`XoJhꄒ/>cX=L4W�ɚ$"oq ˁWWbPm߿. k?pz=JS+$.WI)L=Wf6��k%|4̸wUaH٭H?G3bTu0 �'3M_RӚ"2,{l~obi8qT*;X�r䚪 ZXXߥb|0FJ0كhbXԵ>Fʼn{b1gC.4Yd<jzfOkcn�Rq{@-GL<@w=Hz:8ZIMēxj"ӸP�_&~% 2Sf]*GV&(CGAjʈARB~xqQJ/$V4t1D!@>�"A˕zS"ge2đb *Miff&7 x,{- CA)#FE5-JHz`cDG64<%�3;Ee,00a> AxtB2u"-pۮ$�ǃw_Yhܡ>iHqԃ�Nhiita.5Pr4<$j{k;̜K8o? ˅,Fy `)&ꑩͺ\Zqid[u5 #= cOMU9ߠ(ePM%ťm1'v[c-MG'�|WQiun@k/?wc,s-;baR\Ki<Xo,tUu.(|E\4R9K- }uaA\]80-!IQZfMUOQ-[ˤxDn!Ԕ[0<Yvfcp�UǵNeY r8VL, ֩q^(SG58w%`4O).)SG* >0$2\{�}P㸢Vfq < )C-QZB&�*_k?W덪K(6�HҪjh$<apnX�mn@8)ptb'Jc.2bu-3:2)#eΦ�K^^cF$ϴ #]F|S~OepqGCKHnGQM ȳsITYȀ�?9]#4pEʲ 4LK[u;_ŭč(O2Y d),�Uǚs&\*?Š&-0/!˅$1:ةV,m�LKF!'rLT LC坆q1:emnث)乎!D.pr%M\Hd,ܪo4^{潟5E�3N{5{t$ a#R"o d55, 1}F':<_Ia8τ4RVeURPV+v(܃Xt퇅p|Qckiąjx9Iw6{ Zys&�6Ĺ U@x"ܭ'>w9j_ #.Xb7ת ތ83 u fwLi͈�#r/qcܰVvRҚv&{(I+)QwRA wgfZHjU^0&zg-9XT/k�xZطT}kMBI;a?j*y1m>{_ L\fx3(9<礫ӂ󜣊3j5r![7EpfrGҼ!ԇ,k(5)B0qش)$} 8B<*ji񢀢q�i8x瞛ҭ8譜*!HET.Y6�ăk{s{ܧϕoV-T@-6 vPOU0$A69mXVJD6]'L#ms[lKH@և9GD(Z/(PRCg4z);n{|7BQx}֓24EIx\S ۟,oB)�7":VU:%,OB -h⺁ �#EJP6Q; ܜ<1[ L͍@>t_jfY'S$rw  56E} \K}�<&⦪M >hI<CZC{U-$?TZl,[9ѩ2*rmGE_Ǚ<Թ,؃`nz_k[E<5�{nsWkڞa�$p3HٷY^-,iiF`m= 12hUͨmN.w^bB8vG+&녀{"[)b>A gŹfgCTQ)h&P{uf�W;]�&SNqR/)(>)0`ƍַA|):)O-c,$p<Y*pQQR.|HFֱ 6Yf(p0/+iAؙgMa_yme%M,rZ DE. ȪD -1ߵ�g@CNV"l쪥g̲(So_*4S!٭ uuH$Gwq^|&W eySskA$[_icgm#56v~Z} dBZcmYzrͺ3cUZllӜߤ1.ҫ1JD�ok^ݱVf.zH{͉Ķ D|̯Jm:{sL:nSӸCe"U^Z9-CDdnyO&Ͳ@k ?RG[W$_^^TZ.[[b(dC/{n#yKW:$.;q Ӧ yu  +_I7o9Pa@}@y '9ImTpበE3!i3:>�i\[mC> dCEDhs6þzFQMYzHbPP9m\z `Gey져X̓qZ@/7u;IZ9!cI:lH؟uSN4Kee7#rI~>f2 GτsJ(Ʀ/)k;m[*ᝑ.Bf;K^X�7%Z1přƉβÉC<ĭlqٝ|P�5ӌ^ۮ{ #n,jV9C|m p&w[ZP(#KXa~QŠϚ n u=jM<q2Q�z|LVwbyLMGK"\Zmfs_DtMfʜyi娊[(nlAs(ZǑezc08L+SA?!餢YN}VH+c4B5̤qIaK$VoSrx m!k� CKZ�qQ&IGLK.a1YjJ߾v07T8mS2#CONK0`u [h~9* "�_.nAB UA 0$Cj*KG,6[\r|pi䥗M<24ym;n<I:)9lETo*IuE⮫6mlA<F .k*0kbnI{ s`4n{ YH5Yfv2}{z᥶GTF-J^"l7|VhsT!T!cs8\ʹ"B,):_k8uCɌ%ELedi߳5sss@#ԠrxS `O,S"]&L[ӳ]HަiT/ϕU͇@*-6w+<G)"RRܷ^|⮂Ѧa*1|Ȏ)52F#Mi_ ׇ=qcIW?֘W>;C 2Gw>j}*f毬sjsI|K+ �-el+Z&L=-}WMOJ&VO :F۾Xߒ_QKg3s;zZs9%ūY,,yL,͌^0T$AAMr<%#3� [bm}3⾾CSi:,?ds ]Zoߦ,*wd\O A�LceOջC P@j*s�fNCv$<Zu�c'|NX'8 TdY3p0He'Pؒmӌ Q 1+8s:gj:iXV8ϧ-M)HUash#pVEȱF"ZQg/$ I9hQl<ͳ&Fg,Lvn, -Ҡ }w*.h+zQsS-{VZH[II&^1\1MUUWQaeN}!n<F}N.s�b5M\j`" !pe튵n)BXD -s< >pD:L"n, ݱH&:_*8@8LNVΜS L"%[~mSÚ ;F̈́Qm<F_9KUVaGL�_H#v/7*o$K�sCpf2[nY؎vNnMk\48>zIԸW>`A-7]`aa;"FGIMYO UT,qKiR n׵<=lpA{A<~F8sQޮ*fcu@H#Så]?\j�M Ɩ&J~0KO5a+g2Ӭ<�ŠA(S}`@1vVSkZLM`BH%MTȉ^g5;_ 3>8a,RS-+j#T$[�ŋp2 pCǦ k<32 JH i쁄*Ynd_|s� U`vP$:WXLh\z5`! 0y> -Z`YPJprĞM0IH#˙߸!AUICY49Sbc�H+~$:皥R5xr_]~XKÆw(d$[Jtr^JIA2+$,!~c,2g fniP.oVU:UӺ@ =6lXүR1Vdt#!#fTjQ$ �%%Ga!k3�OPCJfŚ�O M1mIEfo) tۑϙ]d82(g?KFGcX 02 g�۽mp=n�!>M/vq5"ɢ/Ȳ|d0@Hڜ[q0it13O^x$~#e >qg4h5U4(ǰ8ˣ(hQ dϬ/2ǞYI'1běs$턶1v&֐{.$�"xs&�s7)fV*t/<u FKhM,eD~@n0'&L54MPWIZb 5Z$bO睅? s*R:}oJ*T#޵ T06&fo엳+|u32ZXIҏr׷^xȨ@*NDTyI&W�E@ nb- Yu*?m}&N!|[ `M#  v{b@�Yî㿊9 砶Hcgq4Lklz↘ mܢi,.ICu $I[1m#],i0ɒ`~):RHUM ,U4p9s:M$Ѐ*2fz AJH cmyo)K?̩L^!Ջq}~醨ld[z$m-m2 o}l"Uic-bG$傕vЊ7]7叫}7fm*t_SV:F�DœEQGQd4 M^Z,#Ak1G2vîxgN\3]ЭxU\2>E|]Aqq&U&$"%V$7cܓEEFCpY^WI4|3FLw�ؐnAۿ :8)N$WU8LmbH]36b;Dm=\=6eGP']$'-m,}F D%uk�<Z?03J6h׿? Qo~Ikʵ A?ߠܮ?H*WF/$[kS#4I/hUu͡3 �lv]B gNSK}sz)�ܒmoBPymЦӟ}$pfA sRDS8`{`u,0fB=G00N(O(!jXX ߩ$ͦfBIΆ^� KPY+ATz͇/eq"ߤm~p;#d@%Y$DG,-yYV6mVx_TA兪8�N9,>[G˼fM�O�uf\ \ -1Ⱦj+|"u|'.�l;\^JwG,%]XӸ mD#X-88VJXE9U[ӪB q :>RGL,nW0.w톅 �Nb^&ghjc$hJS+/ԑ�H~?i^G 0(3qN #kdFLIhkyz5?qE` d..һ,s|ZA$xNsS~$Ei%'[ cnWݾ_<'RNZH7r 'b7B:H@?&lv :t 70A<mi3lۊ'z.!s)2^Cb? X_|MJD|g.k6RNӼ}@K+t m끝< 1#"@"x9NgT&Z֖i݁<VIq'�F/􃨈G-\O؊gNZScQ/}K5TBRȒ bl 6Q4ϰ^3�W�$gzs$gH'-f�� 8r9<=}l CMSZO>Fћ9sv< !7!mÅI:= ωz>*sfx:Km`-M/~t67XcMTs ˪k&\͖Hʮr2±䀃̷L^6,vUā2xZ=)TRO2er '᭼q`�-SkA[{2~JN"J(Dr)0m~[.g{_C9H�'\·^y=*L @ <8,].gӺ;+Gq= EWIBxJk rĶ@EwDǺf=EOROU�ov42~+{魆ii>! Ntu,I'{aߚ�OkiYNTQ+!!QO#};-2fT颥e&c?jc �$5z7082ph 9lu)meʞ`n4Lemix'G,rCMMtpd0dd8&lMBaj|x, " zDb7?G!;k ?ӞJ !ERIְ6xmBe?#U{IS4s0TJ"Gfn@2܂lc9Z$ W2Ogr-䪥cSxf5*F|0#BslB=xFFIs:t9zP>CGJ Q5Uk[FY%WZT~.x *DyrS}6[Qj39F yfiRTF0޸Zi>Z,}վAm@�隿Ow �SWKJ$-`�`Mͅ}.`:[Ih[i IG}q]5^OSTSͮS^ �p ysL�n"2MJuYygQQWXF FnG?$y i36TJNiSCXjQ8Q}U1<5SG5c_塙 KNmomK#3%kjӭ$OOMI -V̺/\.^Gh0{K:t+k䙨*IF!c&U�n#HQs(g+ jC $#~Icic矜jj: sYVB !py5i]ӐA6ʪ5Zy;m{ۮ$3@\=@f5 Bus5Immg<Gdu,RHݴIՏK~XQ}!oXW;g'6#(O$G8>*gXsHG>(.9:o}Cj)%+oԅ*yiϢ8⯌rj:6D2FA+b�q7x9)A+1ʥΪKLn6grgC rj*y16/mEokMךTKJ؝Zi{p5Wi\ߧ{a&&{էY .5H[┐t\\~2~e1>*\UB3߁-v,E Lo˜'�:Xߵ}&~z[Wc;Mň'_ 2P))hRC5͍Xm}Rc$/}4/'0IyE1̡�L܀9:mlצ7"x\{ޤIEFe#0ߞ~ ZečGDuO%ysޫ$hP&We 1˂5]H?촋 *[lH @si\vkOz!mrHRiia_Ol^98Gմ=d'"ăv�۾/Cg >s+ZZ7tQ젶}, C~XhlnClw31<.TVs*8)j!RghBwfq6h} # X;O&7q;L~ ꗀ<i󜮸(%h<$\ lzT(A`7OVPL"[VCDSvBഖuPHV2�ymoޢe9ӽD/tR+<\g}2 QQ4ϞB21:1Q'=0R١ףaC4(pE׮9հKTbdniQ/e2NF\%Tp>H8w5okZ6,U�7X %Cgpbd?0�ͩU ^�bAvNT:Uf@}G_  u@"sVANXF�5L"A|L]e=-@$7 CBe<,]A~ "*gkސf2fE^(;X{s`15NJg9r#"/~6C 嵹]`+(2�z`/ߘZH$ }`68׆J�29iEGC .Z qL6]#fkfzmu;;jAЉj_("]T�f_hlUiָ'[(yT  p|QԶt3TЗ)Tܖ'{ݳS�l8<ɞU.e \#$snXTжH,_2P+٦*Ƈ5륌iUH%U�'?wllP[i;\AHұӛ(7#kJL`$)J=:ڑlw~I3Җ�|߹yx%Oghsq�({,KY 25(97ZЕ#͹�&vITiV; pT\+*$^5mwkc bn>p*5ÉvD+2_ő ["T (yzc<iiw f�:۲?:E֌~�HnL%Y�g$o1!G^V1f]%w<2ؙkD*$ < cȈ68GmyK|E.U h|N / ]Lq9昸W`j3g�ϙi [Y-cDa6 !fGI6gsgpkz_\X2tYHx|Up g\Ҍl5XEƇ/rnL`y3 \+mx[;| Doׄ)͟Vfs kbua b,lxdEn(u)i>b�⮞:y,}\ sŃ$_TtGJ(xJxhla"YLmA?b_+j=8٢�ݬǭԬ-,X%0)n NX-7Mc2 sgt\3f_t5TUG'i,jT1{c_b؋bpe6_CO֟.Y3 5s9 X:o|zm?;[;3d6dEy)ʏ7T*Ȟ�::9w8Rl�^Z>I<SQRӬk;\ jWQ{>Vl8H YzVkAh�Be4WGC?zyt3*$DyAb.sbiڣǬ'hʣr\Xa6ܷL4]3cnLV]Y%/t?QH@X@AEJpq; -*ŠI* ,T.@ tnG E8 *SnlN-Ѫ8?$J̺3h*%U?p!aaϏ,=%׾89 61騲oUBFLasLz?@06n[id;$4 U|*;A,T8/KHM $K3MPIcl[Cj6]shgz@Y}ekᨺ7S4t=-0dmTtu2GAe؞ɴ^oHT >QSa,QQ0$ j #v`Uم< d%q[-| J&hFgQji)@x0.o;,A `fM�??R8:qGxo D.eYh%!N/!v:uD Y^5k}7goZ-/!T�nIl61v팴 e혥oy'|ʶ8颍,(K$;Nۂ.;74cꜯX;t ϚȢ㈉ �Qs &É w,STTɔR=Lq5Dm~jM� V!&DlGHNudQ^>3K/y`.nh<斷z�M.Y:7 X$3|Yl=wTqaÆg1r~i;R,!�EڒQb #7:?h'{"/sc 2"^~\BpG 3"̖ ©3Zr-,[@-qӞ/M1iSM|rC*hji0G�Gu-3Mvby߄Ue#-;B:i\KSI׶ uE0$fDVaGčWW LAI vFRqo{ ٢Dz5 fc9jt\MYŜ5pA)шrHM6*wanVc1j -5 7x[ω!BR $Y/-Df-Z>y뼷PsKT<-ŕRGAAfEA+�v1c#]x Ӵ a;71,8)ш&jWEmDwԘKbM'73ݪOPRDY]m`\6~Vi�&I�<ȁÎHQ4\/f fT--OS\lR/l4H^at3wh)-\ WR:^H>WBH pwD[4]2=U?QT�҆{}~ TKI*i}|4"O�%z߈٭1NMS)ˢU[|5>AHMjD Xbx_]z):3]A?Nx~ԛy}K-sEp_~/TK j|2B: Gs~?r{g�іwߖk8=D05&1k_\1Otq#\M67M:8I&z:og鵲5ch2"7n'�RS& oa ÌN2H"گiJ$-*1;cϗ\ 8CT/$[ Y}^s\*$vzyBv  Na,6nXTpM/VAHYQX@:!7wpg<y.cCM<L" X` 0uNYf#/0"ZW7A/lq'1 Z[1x-?o1R\{[oڍq!yyJ$V?�*4DJZo쑳v,:yF_\ !Lo33aD7?_jw887Mt-c1cPr᪊_Uiq1I(y~90I2"H9g?.*YТ@ߝ~x]n|i{NV=trB!n.+@QtNCE<oY}^o=Z)i᚛F!:dݳ09$u ᇣtz&9R6_b I:'y�w}]qE",GSE{᩿m`t٢H6p6(䞹4԰~&S%NV\efoS6g6Wan9 TqcL{ܒͩ~c+fYvk_$C1VW2EjepPAf3ᶺ'QGC_!g�ȏnZ0-�I9*.mKHXFT:y[_]"LI-tu:WU YGm[}y!kˊ-ggt83bi%_3} 5 ,.͚2Ly[F�Sl]A%=UZd=n'hduZnE`j-cܜ|Ӷ[$JH8v vӮ-ua).lBUҩsC1bsݹz]t/}]}m�UI�Zڸbu$"×( ,,n•pSTfӉ',A6 r~bXFSe wG}em;Ʀ:XnRˠ&܁8'H"s-<ke.JX@.I'صλ-RGAMU¼ 2Xf^K FV�Ņ7;{ooI0+zaTU$ҳytƱImE:�oAYNjFyb_`)yg;!چà)Eu0G"rU `J& J*>nVI&7i NMfٴpO=LH]YsawƆL2-+C`\4L,:\A͚f$fhDqeB's:cR zm`MFSu:F,Q-G"no9$k~V˰XngI4rU <kClZ;Ām}yu'olo3.cS23#<AVYȹ<dǪ`N"<gN:cdUP;eTD)$`jskys�0K V<S1yG9?Cq<ׅBRjU*x|]s#|dUKjAF~KcP#V[]UKѬi\6 <?crbMit曲l4a3X34tG,}0ͧ*f i'V|C9:DJƤYDu0nGO3ihmu9fĒEU9W$w0>cy)?ܫB4? 21ENgeB::,VT�|}|;Gm4 }Пn?cOloK 3I2+5/ } F'i\+s%]Ӱ~ 8& ᚙ5 T\C9RԨ;h<u(q�F�>U+, L8 $]GLJ\)Iih]wiM4t ɚ)$e]_x<.!NّEfPR"s-i{.%ʳ\#of$Yf[`a�fsQGhac?�v]k|}I+g5S\濅}by_kk.kk{Zih=TP-HxLt&y`l-�hZA%=Lbk%3 -)}tIw%kRni0E]?)<2:jR֨X ywebSԌ,\"_~FWŖ@q�2ȑ"0uId<c�XZl,sТT_huuRTP< XAN-S9h+T,# v�@sԪ`riM;O8PÌ׈eocۨr%LL9\{w䟆\[QRխUnmMSTCITܑO*�b$,ƦAPm*{X_ oW4]54]4=,px>A,EʹÈ{r_O6)?n`bsU GgU &0u=�tzh~^!+СJ˺�'S� _=ˍ^KaB| `{lF[֦E@؇dsFC?(dp02j%҉*<3vm?[牯}ʁG]~huk9wBs̃+*:/̢eB(R/7fwccg؋eS^ +[07U7M]5BgU92SRU--`yl#1&ȥZn +3H<zXtmm6x@g[[Ak@g*0cW'hfm-®�p -NmG1^-F ǁ~pD&}ĕeEHݕ�{-Ӯ,%,40r3Z GP|>"E,=~�H67| Fz%,>u1U" m{q)4S0zp73i1�x3V947dponێ1f7jmG\qF#J$I|�n+mA"3#^\7ǐ(#rÑmPG#rhaIA|k^_Z@f&7{5`g5%a:FjVf]l-{{bk<HZ#2m71ujVqSiv+^w~%�=7ۃrxii$kl^q}}j�<[q04X~_>!NhC~ C+]m*Sz$pO?9n!I#Fvbo~[u@;0=ƖJV w;ۗ?&}BV1$#k[[,*Am湸 1uRMn}+_a$YLNKKՍ�LN]FX8Ž!L #/4I.A} >I7늽2YмKRQc$튰r3c%L6&xȎ+5 mϧg$fS E$6&j@[Iy,Um&XI*�p9^F\ԊXUbX�L8/ĀټӚrh5{ԟSL+m2�G4L]i6/ݱ8�iwfTŹm�8{ m igrS ^n eJ j,PܯM7d`[ˈHL�/W9S.5HUC{C$ 1<Lqo Rx>JMVȑLZ^%5Ye4iRee'nw*S$榣`r65*㄀`O-D*K?T_f9}Mg�HQji"DJ[N׹O{bX AKm 0rj5/SU7X:mpQX(漿?(k`]-XGRS {尾e6Ϫ֮A-SP)2 0e)~űT.%)Sfhh}w�~6cg2A+ dS$�-ίN18/胳-L>.;Q((-#Zw -I$Lfws ]׮ySMR:+"0BVFA8X꭬|pF v.-9 oRKR23`gpjhfR#:n.7=:  7Ps ؈xzWŋOT=escmN(f|#3 :2^Re2l]%$J*7nx�q}#U~}MMYX161nGC> ŌFGYnoMMt|(Pj 9E׷o(xōįl<w�<Pʹny[ƵׁOcLL-͸^DZA @T.\(l[csa`ҩ72-0́]It&tm@_`vڰX$b-깴 n\IܦdYRMaH{� m\Mb˚bDwED* mIԐ�~巶 sLm,mX��῍G EXjGABdeBl,7aƴ�K!88;1,Y:SC @pşS1_r-�MM3|%ԶǸ˝3h O"f*ɲHBVa}cleV׏Q=8H4ifRfvi_*ymScboNF7˳3IKOKT*aZEisZf@nǫv|5Z_-MQ uJ$N^"X*V<sP�3YtPTOJj&, 7 |9Kh9#0ۻ\U jL9:#.b#lln_Qc{cwb\ׂ6*4b NB?!ȡ xkcU!IUB(Twm߭6=As~,SsLb9A(pvL&d|iJXUAJ SD<>{ȿuY0{3ǻx[$@H&.}j?̴QdE‡qv.چ�,f#2GLuS۾V|@\ѳ&Y] P616݅sӠ@�dM#)S q$ܒ \e؅+Y9p$�Oߔ1[V++2"D, 4mFsľ m}z u%Ic52$v?+0ǩP=U fIj)3􊊦YUv|n/ ?87ͨ\ 23ǦWOSS!AN)"U$�Q;bZ%uUكLoΡ/C3Bd,Zko{o5F.?H1"fSpkUd5mG�O)F�<pQI^SZy3ȣg|SjR QuKKoc SEeKgp4;Џ4.ogAQP6ʥ~w=90a\L�>79){ tR$Q.Ĥ@7p3+_f)�uT<MD#*$ m~X 8!j8FE[Mrȹm;[�a5t{} 0YA*2;{rZ{&vQD5;;' j!d b�oa:|ikì m3QfH0�=EKu?0 "C< K}?ŽQ +*?gj02w`7 ψpG4h"77z0*>dB/ҵ_AZbAaNo`XDDX 718(r3(OB]»%u�m隯ۤꢡnZj?6n3Jg#I}|.?/Wf�鷗vޖ�(˦jL+$KL�w쭸|>(ZE/dk }ȑ?|ΆZ$ #�[COϳ ?wzsqNM92Dw�[6w< [L;lxFHUAY ,JXCJM8snsm]aKdٖ]Xk%72_#߇+�&[Ysnpq{E%t*Aԇ\FWILp7QYX 0 \@>Nщ!cf=O^7H:3(ptt�^Iݸv(S#`{ŅLHd(+c-^<6y|jU@Y"5,K8_IX Z!Fp؃n;~]�lvoo7-"^An9�cc( kd|G$NHeۗ D^(:~ؖxeIA"[ pk/1�i9ZJZpu+9b7#)p^7ZYABt�uqp U4"ccvQ�kuJE[!Iʂ븽x" z4CLg̑,Yz/}@͕StXn꫎ VibfIw�t nGRfɘt,#Ucq}b`J;]%A;PA:8w+qLlbU�7<|"Xef0l8"QE�}qκkN"zqIy%%43o{m!뉜C # p"VuF"-LwJ./ö 6 zv5e')>Ĕ4$9F*"5u7,^b`qYvT7tVf]ʄӽVkXٌ[Th %-#mLL6@mo4�.`2X)iP�[ Mcs۶ڎ^22e;ɧ2ɫ[Zԛ/b"؈ gkZqu:mmaU�5oEFtĆ�_J.V 5o�m)#+.)rRʟcCB(܃RXCd�RSUd5Q:%aiA!nDl9#%jK[=w- :NJ OrN-q,ڱi䦃@9Hl*P&q&yMK`^fyʶ縇?/C8H{4†F؍˝=�~6/-!pU774qDUʲLZz([]z[|ES]䭳l8y}c8`SL5�m`ExI #kl�Osj9@5\|VLJCH{_.Ciܽzd 6p<=A"A9m�;Ƒfr=ngams&Z?*z`TIPE-X]̟^|V "ι�ibYi0ͨ�= WU1gi+MoMYup,-J4R[S E,vqKۉ>W9,bX!X$7�mkytk}H0ƽT 6K%t/5.__O91lo{b�aUQw6V;+&[C,Q>Y$Ѩ ~bHsrN|$mwֿ<[qlFY1 [k 0 �xmÈ&/$*0<W =sH:9h b`G5}g V>@˙<Ԋ,Q5c T7}&GP#-\T`}L<6 ~56YmAm3Tj1@C*#x` 1$VKp!n`xO0ȳ3êY5N5MѨ U_D�61@N=?e5 ֺ[ub:ߐco. 84 "n!hbΑEx)Rߟ-#ab 6D\mWJz%&iWU9D .1[mfJ}qydL}M<2LP)iA?> zMq4?X-] RG0G]Fg8abtm+tƕ-:qr^v/ijK <,n%,M\�ǩ]c3g50+[UK%I@*!/ `d ruJa1UM9.$a1#Pud 8,Ɔ<7eĥؕa<F+4DƓ|#ةBd# :w y02W ZHTTG3m- $@hl$24i>T2U'"[Z;@44蛪`aѿ>^|#9ǃHd˄,hM$dH"' 6At-J|§:1sUіd!""lZ#OQq:ozzz\l*iH9 )qN'h! U"H\H54f^x&'2Xy~.KT@>7?=hhA&Z7/0uxaX@8CoMa7ߧ$:HO"O;sw擫MsX&$̥Z \6Din ƚI.Td仇V7HYHC5#1Pc'k^޷>p.i%Iˠ05 d}w<�w¢Kr5;#D<瞜8(rQ%,|psp֘pLmy-&cR̎m@`m7BFz<:҅XUآrG0.]6a O n9{߮LOΪ)l/11tp$K(Iepۗ/LQ( ԗ k L~Tv@#kr quG}@ _P)w(WMq.ΟUZ5-�+f* HmFbeĠ:L(uBØѬUE0oks8%\$�׀o˹$9PJCo[{o1J4dNH5\׺:iβJ̮VZ9^G@v9B�:K ]gT+r2MJw(ؒ7>l�Ps|_<F^eN /nI 2q$xv3wn3g/@{Vc@&AN1q &ecv7T<#ߙ8+T1~p@Gn6p8*vplMz|b hTjAbze\9DR@pӔs3_j묂=ZE.3TL}5G]jp9z a<5LI2*`q?eZ`xET`I@-kS~+ۼ`y(΄,׽HOq c*)V^K_JdXPnzr~0]A:Es35KGflEv8�!bgg:J>>�Dfd|=,cvO ~_0N)nU#nl5b�a1Ny_[Y\eK -9C�ׅqE?F :gJ}3U&'>͈Ewh "^-9<q锹2Zm*AȟY�cs.$0UU끾p"A7NSďUZP[TrSϧ,H`Hߧ:np$#٘sU{s0I]Uo\ OKKIAI!hCpo͈vߞ,O��si t>Ғ/T@5vYy�?)8!^H.oD&*!fȲʬDk\Dt7wCO1 \,,˦ً*ͬ!aANďߢ|cH:,}Fp2znEsLG')V"@Okͬx,qv?2 dhӍ !Tl493ʱ%Ә9z$rV'DCf2lÞ{spu1o?RVTzQG5e#rGӐ1b!׼Ƽ$+JZe]IVÝs9jCs(UVwKu$TO':ov9z*[~)Kk"TէIې^|jle${-z{95L6 !R.!SOXݜ݀n68Ȁ7yj6o8ꨜǎkQ*02\C:lӶP& ]7/4F%QnX| Õ Oh2<mgoxEN}:rӹiuN^/EȉזF} jKG~KcqG<(?7h;o.^۸\hh! oI㨪g-hr l ߐ?LK`xL<i^`IWYM|4 ]M'1cG>,9O_ %;b-ɭkaY b)ܤg79W*ZܶHSI:5f�\ۧa)Ն-V{s7Iu7Qwyfaxء_QKQ@HNw'=#5YZZfd[|sn[|$=饏,m|\ˈx; ;SPb�oo~}0ڟQKfmyv(ɫ iL. &6߯Pborv؛< >E3y4 ,Ă+c~\qJ_4L;Mr˂c}䕹V_W 2J0A?Qô(R% %C{!M<oK8YV$$bG`o&P-OI'{qbq^(hz#(X|b'(6 9|P3ϗw@aۗ ś=dyeU]w<5dUӥ$l6xMP`\p:G SmKOMϒ�?ttQ)cִ@7۞A7ڨNS+ꖞ|_wA<OC˜fZx]rԘHmϷM>L%eZL8ך\Uu\*$ J-5еE;c5ƥ6ÛG.\qٛ$9<z0PX16 �~25FQ"tV(Jzij p$ME#qzǛ ߠL)גs ے*gJ[§psS_|4 +_lן kfL"7qu<�Yp*i"2,<'0jCfy $;R@# F2XEGAJC\a*XA1{ZHE2 i+j*cQEKKCM5jU?qk۞ NDalD_v{\0j#bodk.Aw*-EY: 99#斨$6@떤[`}ח]Z"@vYQ DXI+|T)x, ǗTD'ѤxjnnNw=ė##qRٱ3(E:w;'f)#w&?~9qQI]ѝkx#(tqC=-kXu Ew�ivYRsGx€J1&HL n.\r(rR~u6?e@nˉ8l-Zڹ^7] [0X+sqhÃ.!;r=Vʪ2XREHVacvQ#HT˱;E88]PA'N#o�^G}m 7[0/�m\$̊1+*I]+T y)6DL4"Un'Ÿy8\=04v5YVc˾Ǻ<"!VZ1R>3< EV(�{Dqϖ P 2N4_óҒҵlioѿ ,uw? ئ`g#e4dǠ<Ѝ/QŀK[ .�2 yOPi�?y7Ž]Nk|-Y|=Y%v[UB}CZH2s0S{`oL 1\# -qnV=?{%i{ImÇAgEUL!-mWnm8Dx[$@Imowt9z c$wn WkԴ8DH-I'~}8R%˰C Oq }ꡅ^y�rRֱ\_p%(>ʠ eK.>x8sQà=$Kr2SQAQR`r{2ЫM$}R'K oTp!b .dМ)qdDE`!ͶQdW_ )fE rщ�Dމ#8PT s_b{[׮#iU`ic~k2ͧVQSIH�`+R"/%N�ک 24ڔ4{=mIB4%kZ^"AӺiK$(ve~]pjp]5h7Нrj*L=Z:iߟ ?c[��!4XU}B'zoh~P%SX %y, vI_7_;"YӒa`hA qsmיM)VX;^|l@á#箨'h<YIR9s+KDvM0\śx9:h|?WT> =<I摬 vsBëS | `Bc)2MIL%1T"%ۑ` $QmB؃xĿeHኝU-N$@ݜ{{chEn$8=DZ"!)hXTdKn}/ϗ 0\GN'+f i &NX O5(51*inI߭>- ᔩy; s3UU/oST1P71{bfHlO?^[J,o3sxL4XT*}銾4"sOoP<INYX ;;�xܶ6 }7+ 5ͼk)(YjgTi?yE@qb" T4fӻWH,y@ ۶ 憺]pw3S'%:XIyC_^$iuGcq&t\f%tO zc64]/exӔƓ»nʩL)!@-:j77?\ghq'É ԷLvV]9ӢMX["`a~Nm&hxzTRm.m?,7:qH޴_95:2;+TRUn@ؗ-f>H4-s; {#d}1X22#36*qR3Exj6[rMR YwlƲ3'U51KSKO6:aָ0GW~ �^ ן (ϸEoEL*K|0ݡ$*藺^/MjR=H*lNT�B4~<p?1噶ѨK*UĆ#�2F@9^6*Ix}a $_ՒPХzQbH"0Ze�6Vpd[&^d0AR@�ܛ4]`aeV)t=&mi$'N,9=e $ZP yoͶÙ"ӃÚ}מ!EiiMSB'D+"ZAqo<oN&I6C �@sY$"%tHR_{ ҨZ㔎#-#t迄V<4iV8R%V7o ) }Gc{:G2l\A{09I᧒']6rnd5Ei<^ cH+N.WaqzK,Hd3FVۑm0Mǧ|Qbh?9^{|rDs)323 Jhr1jI\Cv-ӽfע漐<V,DyTǩ_kIb#=\ˇ$'ߢ_za=6o 0i|Xmpaaou�pJD%<`T2\djGE#e h+z#I+Y#_kr7Ǘ6RIn@#?PL5cx3V�@[L ˁh4Zz|/_Y6*Iu(ru}ǭW-x쫶߲)k֓8ϡ?LIPkFl9n-cNY&dG ׄޯ6ZU/Ѝ%^ap/˞/'  ZeѾWmm3k z+饵~E.*i@~t]搣/y.~+u`<b 08:<=H;'9hm$x9feZ*7 ?r ߽<1C{~OOl/p&ZTlC$AeUҝJ*c{_끹̎jX涡s Uz`+bH6CZc"MnA呑BV\߯߆8Vȸ.Cs34'SH@�,Y U湸f1 S 4WC"͹/| \xoZALe.~'uMr{0,NHի_n7 q==’0\pk5^i-3&" `u 7~!�TLuvHШ :2ܻ]Q0�9nqWJbZGe�YH b� z$#[$i?1$+:hrvCqȎt4dڍhnܒ9] ]} gb4aRg_SRKq IhlkȏZҪi1k\+s~Xrb1UB fP�`O+tDž pmx}i(̲uyVR,YAm;^ +?i~CY5`_鶇{a'G-G<X;SER-)$5q^#֤+3l!4ϻC4"g]-M\�NXy>�8 8XթPqdtT%Dj;~8ap$ �i^h)"ZX8un)xl%9hY@R{27 *C|X08z �ߕ~سA5NDoCLU7+Xea]/K/#%`U_ qل9J=,Y5&gӄoJ]~h3)p�܀;]TҨ0|}5]b:u8j ɥ[uUnq``($G> `.dG+U~R7ğ+*┪:n�NnPEtT=D-FbIw("G/q +uEҜ/t<?S5tLHm��\�|�KX̭L ìN~g銱 e+�3 /3ˇ.6WOgӚ¡ F:\ D@Jas'8Ω8 ҁSk@` BӉDoy7(Wᷨ$TKK/ 5pÉX_ᚈXiݒ2 So-l1EnR6T#|'IU2 ȱoӕ0,=lDߏ))]IuKZ[6?\U"U>*{xTgH阍DJ�7"]qj0֛e䨎$IjwI,@zy녙>,'^KF#P\O(eP+|"Y>`j%w'Q<Yq\y%d#f`ߵ2 r+Xm5p\;oSI<*̈AkLF,&5=!obIOrNcJTr �NΩ\H7^†�$nN<^R"U,lTm!|fT.G+ON@E�>Jܩ$۷K8fLp& p_ mϭ*KA#>kfL`Weó$rO/Ei v_4O}N| "U:sϧhel98kIDtH4QlLj].|J3 6Z=RX3jT�T%vm/,?Mi}:%~?oDj Yؖ/1ߟ,!,wlM%q ^<G8)[gk<kdC$qczJB&p_p7-\rSTSYd2`݈7Wp"9,ݧe\ft+# $2Fi"8QG݁}  ?F;;ezy]5]qQCIJ&W4E)#QN@0K]ñgdlYI,.ҡTka}! \-׊Z..̲,Y֏.ib,<F6@O؄-<Fy5SEdTdT6˷;cPh{ߚ¯@4't[<%]Oeen:VClJɉ=g`)R,p/L�l89TG8C$(97+ ¿Yyw~(Nn,&}? UO ɣ*=@w;˹HϢ^,A[A*u²�V穳\aAg_ECP5EU>x+.O4_ #Wv`\5@=l`ܓrC2myo:U-eS4)$]1dT/cDc{!Rl0 LJp 0|ڡ2"NfWkI[mcf@=tp鍜,-]fUd-e4̫<H5wVi؞ÆZ;!k9 %jjʦ#BX\.-PD3Grzi`M6l, iA#?܄7=ufG[OѠ)* $ۣ"Nw豥=qK epU-ED_1Lcbtua7<w\I&(vՀ M Җ4ggq�ireJRTvr6X]Rl$:d7�}겄M ]L}1(eϾJ0w"ђ�6~k`r Y+j=rU'e>6H햹~xh<p7敖H66<,bKOuߖz ٱc68? WN_rmSD51ſOf -ss)1 EH 3/xV B~X- ȏZESl'ˎiUƪ @[ݯbl*SnQOa꬚n�##.׸|Q5WSwtUCN"$<,p2MA.8x|Sv-倿gn,]po>yϢk\YeSm/׮$S5ZMsكצVy--:"�˛=;g ;~MXD9D.e8`0L=?kc[$Y-AӭNm0DpWPI#+s>_ 2\/\΀,ёܿ :HDFzjdǀ�L Zb"dyf=lfYv_4:eW"yMP[3>j 66|�oQ'Yͮz׸<G.78[j%<w 8^ JܯQX`�s�3oa@Fzg|!U6k" Kdw%I޸St\KDfَ)#3 8Jk�p逼=B}P|[F�Dה|X(⪞`lOLKwrL@Ɯ$mdR%Cpާ We>|998cT+F%m Z08 G9CeZcr �v� <zw41cHIE7Vk@ x'+}7[~2%<]&ImGkʻ@L�jͳ";=Bܒ:m`aa*�1ЗmGkX]LEg=HY5Q{XcAh*pGQL4>^-mIwO8P_)jjxjOƷf7[pFy,&<]'%7p;g:!‹[\*sLVv" /}c?Mg+W#TI+>`1ob ޘ mZ_M%L)Cxe`40Ӊ4c7H�/~Ja r I,"Ϳެj桤$Gc��(�|V̫.$9]CMM@J*bI1%v�_qco%M.5ʟa|EJBIG^frs3_py][C["g3um_ŝxd $sDVǩ$iB[qa9gfZ dXn7$Z&?sL xV"f?s?ȰCy6QΣhQ{^' dXۤnH|E!%G0K+wqB֗D*5^\N]5Jg\EUZ,Ht;- LG5B@X�=tJiM`[U :#f98pE4un=Mb~W7o:dm;W e-][MbݾIAezdQsxg,&Hv%T-r?LܶV湰+xk“{Xz`5:e?|ץL{p[R-c1cm;o*N.Z__U0f�m담5bB WDhVk:vqj8|"rEiZV&E⍯}gܣQLLj-PʍkN'/js,r3VǨ L 3@;7o }UN20> ۗ!`\Y>Wf㗒g4n$=9`Cb d"vmIeDVmt[PZу; 蜕 z!ҡmun=!\}u}}>񜞺1 dN\9:#X:lf'39Β#(Q<@,% c[Ll񭥅;6Oyois ՂFUP߮4ppp7Y<!ߟ=!ZϨ).[(5DF0kuL-/DjsE�CrB3!25A9M4 .`EĪKlX؎y5hrD js&Á:/%;-L:$05!&�pK{=`�L$\Z� =/IMkP$?s[mG�E6ŷ&7ZH F_(]]3F#`R(VV^r~D;rj?9B9-!jo!Bˡm~HCh qy{#~1D1$5174bU"þ5\QI3#5Um-UyN2GI~oU\7YUv:8 ,Zʵu BI7[;0I;[S \u|'�J/[-6ȁAV܂.G#06N :By@-5c;靬/\PUH*N\U܉ȿ|dmMb sR:˲u�<erkm}H4Tn֌dj5qeյuSA:) XTBw׾遆⟠F:>S t|eZKns-o!'`Z5X^Ù & L^r~y)U V ~e_` U,U}&sL 9VUێTiqenZs.V5 RY�3.|�"Uf@?>zdɣ˪'x#�C^,9t<\etpZq=ørd+fGV+ikAȯ)(Z8KZsGR3:Ӧc+'T%3;Iӵ{bo4袻C0Nw1[ k Q|xjDe 7?+t8,7C|sOU 7%aU ɕԩ xt&N�ٯdUAF-*E|}qgTyiT>ݍfaRH*dL]7}WÄ䦃ߦ>P) f(۱|(NG`"g㌜d[E7;*Uo$Uq\5;UJ5+)& lbRp>Srn:ɲyZiRxʛ�o\]v�)YMSG:>~Wji6*k]M`w*pNcQE+#dՍ"`w Q` i4��QU+$Ju]y )mӭLHeM>G%>P:N<3 09=(맚TV?�gUi4i9,weY!2\om5"W84\w8+GeϛJdYUiޖvߖ)R] o̻|ۏK8+2|83?oS%R@#EiZFv&P|9L*f6"%%nWuݍ ?j l@ oy"1j|֒[{A2Q8#�q6kJLs 5)P-񁱴˥MbM qe: H�?,�?mhH'WQ4t<ߞ޸'1F; d4 O?#qFJM �Ii5CXO&oPf:Ef6 q:2!ZxY-fI/ m{n@~*ȟE JSTO,@qry�LO{�oxM9T̨R"0&ǯtHu28ϮXPUq | <YaHXRA;ŲBWs';9eRAS5<WXZ@wcF-W�._C�UUٍI(Jcr6P:D܁xLOujpKUOD6zJuIћ5urǥbe3eq^87uDa%T+TAiҡ�rVQsu&R$\kF+\SJL.F7nlGrHmś >I좣/{y-208b-z u*шz 7ˑ'6Y! K\M?iqtl\liIX~maJ~ :rCSK͈s5CLh~mn|kS3kB |Mȹ׾+RҜcqB&۳6 ʀP-MUkE>SMgAIv)ԫ#1ϐ[_ 5G]m#>hx cR7-b-Z, )4u+ɨ .۶ CNmnGܻGzD+d"○c/i�ql̅pv|:sO!{rzb2u[~zq r…/̞\AIݕՕ@C uc�#$*mqazg$$t@7q郱%sT`As­,UGIPFTS<A̍uH&Z7_ ;sSn׎H.˙|_$7e>?RxN#sih yhL4WޮeU'̲2kqi:c_54ou<mts_4¦3FH 1L]T`K:~9@Fl4)w~ݰ"#6MyIw5 82B?H*v?+(Vԕ�07x%ft%W?^!,>7ٖbJ8O 0w_�)6Y%I s sԜ !xݪ`yPoEpd9t5Mt~*W_ cVXmaϗ;zOT�͆bl8B?(*֢\X";hTBNmuqmQn�w7X9zhcvR{^?<^#<g 1\_ ]6wR}|hW]LVhRMf�;O:p#q ̫1 ]Eh-b yYE{o+^mK-N]YMY=nf2PMh�{`1q6;rKElu(ht3;4Ȑmq09ߠK:d_Eɟ>5M*<XQ�.$sb\H۲6]&5|P WұJ Wi�/?RKE7e HIxĦUyEܒ] u6[l!;90d ⡗˸W�E6}`MF۰i.5qqlDpE;f+5mUIe"}y82=Li)i)G#Eۯr?)MYz&繜 V-97m@s{EWA1F }:m.VI1{8cM?5h= oqkLŗ4C@,Z40Ua>H=Jau|eRh$m'sͧ&ٕG98 0Hʖ9oLTA~{d 6A _!U 7ό A:^~qc!Fd'1ݐ38Ӹ==w遺/tBLF·�_bGҤYL"h)M؃oh0P2!Sⵡ4ecGK2 V܋ �NW\MweGTWRԒnKjCnC6C5p0 ׿+/cY^ȒQT)Y^_�b+z%Jq 4d˟<gVAkbz&(Thav(N," 3Eymb#&`Û}=\3- , 7Uuca G0ـ�͓Z.c0X�7>%s[DLJLޖ_X#BI.X_dQ)#+-uTsN"4ԓȟfUn:^b^Q'pѯqܽ9 U78kvf$J)?NQn[BUA$Dhfı xTVy˖^Rtm)P:FN8cz-*"&'{Ofs`TGkGc4)ְl{~AK/$ZX&E/+Xq4�y WPIVS�JI}DzzL@ 7)̲ _.OI0G/.%>Ay\%ff'Ǫ"x\m% Kb�lu{tqbQ|;ZFA8FQ!\> ݄wBH'ȳl'upxkǝҦ0FߒI\D!eqn]\? hߒ[J:uD*PT~DZ8IO+[KS {X"> oH\Hrw؄nv1lE$א 8,}LsK"Z"H ^'ϠNZ ]I]]'``}V*/˪b"U?խ3x7$ �{aJ7=� ȏty\WbH0y.MM]5`ؓnXy^7椈40+H+skpNA5$d~R-3m碮sWh)>QZN"]o|/[$[v[Z /e]h8*:9H(%݁uQa*Xd_wz�Lyl27zbF"Q;$•~\؏I+�95n辙g, 3\l0C}afbYkя*Mtaq5k85nBJTvo+N}'7}wEiB˗勍@vЧw7A8~V;ˑ`Mr4ָRkbg,^R$:0s6 @9q|M7Ν,UQnNO[-\[ЉO\sϮU>M - bLaqls 9q:]MXm6^V~,9߻z/U6!1^\|oW٨�S7ܻ7:]r"p�| \5_/7G,mA0SVF'yrMu''J1(;t�l4$LPmy,!bvv asgriB T DQ;GNJܛ7#YzwӽƤ<�&۲ 㑾]QLƊl _{ !Ӯ^%9/Uhd3ҙRTGY~}uZ=�4ᮉ5Z9i)ad!Wf .0CGkx3:5dUe&+6ޟ=vQZwnB.tÕy~S,Sмңy_?<U‘c8G!m![Mշ �|ca,&~auY6OC$ڃB.\-eR$a GdjS3T50S;}"H5|Wv8A<BY9Fh#}kz+=3x0# 5<M_:^E.5}um..r0.(k"f NAʬn˱OEǤD/�Ks]�K(UY.}%}>,Gy(wgO/ 1w5}NdHa{77Y;j *Aʞ[|9nde9uK N$ r/<}qRfnϙ:"LFJ%J۾06|vG>Ƨ]r A Υ_kx "ہ=| B8)vX[]]Q(@fpI4F9nq`[✏}G"4 ?eY]m`j2gu# `ch%Ŀ~fRCIK&f&vVo @q>-9i4_$9�Kj_"hF%t*`рH|4\-hqJmq% 3-萫iD+ܟgWe,[n^n<_xc]/|Uk|>9%ƮbTQa'mVn8U;> &1yz.lC,TRGGR "Gkm'EMfN}wrv1ha嵅-qQ,ۨusR`بo~1:JD1efK0ngqt@˹@qh:oQ)bXdPH<n_beia$n1uSgp{>R! ْ 'ă%ơpcc쬊9PI=~?M?s ])\itn <N~E\h0c{m}p'Uv5R@�;pEQ*3b L"kXo.q/e<7H<j@Da{gV ns}I$K-$i 3?D0mxi /[iE`.-DϠC{ ݄gBrp`ɸiyaͳ\vqpIITt+QE!Whv F?iJg!V,E=SD 0TB:шh))khQb�}H9TecR)d̫Yܪ-*^8ӂKv=xqR$ȵPUST(,+gŚ$,pm޳4,&bJ: ϧL `]apxDFqVSЪ:]QIusz&jة_3%",ǏF+7G;jߦ#"uDȓ]M[F&^XpLBL�&/ &}v4AO}u倵րhP]unw 2èJY@ṑZKLS% N*S1QXI6*A[1y LKh oȾsO~]Tٟ5ez3٭]*&=Qq5*um)*˧OO銾>U!p~nDsLψkG:Ūpnl{XaaOQ{�F}=jeQ$iӵG�seQ Àsqn-*Z_hl5R{,jU՝$8E#9gEM�ZĽol-ݛs~q$RR�$j6k{wǶ*6V R|S5,Q#.#\\dqQ؜MG^Z1%u\0T A'ߐ }_3H@ܝJѳ|!A6[;2N<^w遹.xo9I|3{EuM:f2WWƥ&uVVmVu'+Gնvɶ,f!~?sff'Vt3 Z{ �5OC9k&c7 RZdiZk|֬JcmX\_h�N5\m7K4p U`,kq~^c~Kl |e (̄�S~|b)}(nG>+<f V`߷6e.9- Zg3o+tK SB4mĵ8~gT/@v oa\ߕŃ_�-o)"7VB0t6<s'8<>;*~`b"3DidkRloӟaC(56p&`b琿;˯�{Hكe#(VC׮,ڌa $.sAKѣv4ab-$u~U`�Z �{az ɲZ܏ wG6ܪ X^zwuHw[<uC#G yFzۦN)jl'|™ǜ1U.Zٝ( Ԏƀ{M{i͖c5&/f6oL4yogj*Yv"Ͽ~p)LVH;=7qM^"( l]2#vu4Į}uY9,XԬ`s 7 ou54�̧遇SZZwE[#XcBBnD/N_PGP*Q[sQ$� +e8ggGU\ U}%vwn2x*)2H=q꾍S&iO]ʥ~fQp8`ة:c]d=JZ{dGFK6Q u[Ey=WQX*6]op6eK[Q⠸Dؒ\9c7 `欌\K9ZyԸ[L*U{sgb-+�VqxHV<56X3\Ɔ@67�AמpS9eyMU]Q[YY^4, xYTscYmpI-FmSѣQ$Ii͂w5m7i!L9|ROeYCŹ4#`RG^~hHb8{,{ET1FFk^W|^A9+8[0"+ij{lqF1Vkgttԫe9^v�sf׳ gÖi.XXs+*0HeϙX#T:ƿ Y⪉`@4Z5A$y8oO&Hw%.+ȩteV:Z?B-lp!)G0_K"8*2C�c$$P e BCKA 9 x8;ʠ(ZJAW�% R7A +ʓMĞz{ ue/#ee܀CHjx@] Z)_D%\ɾ{wǟڶH=cbT$JU܍`1 q{$hxjYP2QoEn2,Es5ER*F:Mo-h=i҆V`HM}Ņ,EF<%Kwdz1<YO;``4+ FגVmoϾ.}ٶ<)]&9'}G9*`Üi)|Euk}{@6L=]XP˧=lIWYdM=EWX۟›eF nP8`ꃋ=ق<]) =O_-wvDLd|%$DrS@n�BŎ^ٮC*CT-Mqml Ł)WdfMU(�*o�8R 5�SQ+[4̊AM85no>7jy`ji&an~YoF :O7ECS4;it}Z4ETWT #\)]\\*31`oϠ1.a.`^䬉&Tz\Rs}x-AQI%muUHZױf'>'rb&TL(a?3OU`_"0KUx3qR%˳U6uF$URC{vؘ=|bhn/㦼Ls-Eu{og/�Dͤ/I)W@+c?da#T4SBzy[7Pđuv0Nyⶾ_4�CODEuwvC|n>襭ymmMPJR  .$؟E}aW�qTa0Ԟ^_oT%=>X#Y�h zYgMCKV5%=t}lgN~U[ zs8>]S e? ٤TI'bv=0 UZL`|ա1 zZ5<9DثK<f4 J:F8\jҤv,{`ͭ# 2] gC ɳm@)]TLѿ׃&м--TKO•2d:PoakX9SF o>V4FCqsgJ! Z#D,tƪw23)ۿ Oylx2Nf B(�HXslem]uXOҟ LÕФ#o kd'wa9ē+�_ة Lu:V>YJU /ȝ�V�oFSZ^Gf" J4}F!?C?ERk.iv@7A}5Q%t)cs �TH;tvoKSKXMVXoT$ VymJP Չ]ta2[ iKx7Å_ Bc�d^ 7rEmP$*S$Xo碮 7#b7}QO퓈^ZO mMXg  SaJUk_EXo#F t]6 h0^_2[UT0_ K[Ĝˁ)AeON_-$@W a|�e/"# @{~nj;䯇'2/~WSMS<j-~`I qrQ QY4)=Jha9s`@ˉjae9̅t**E#0#f[ t!%j03623!+73a ױULpD3lslQ"7鿦-TL8a9^YQ#q>d+qߵFG8tWed5 ç[`Z?yqI6udI|ŁJ |qiYd33AR<BHcPƯ*�͍�QN^~[Qgu^Q;L<#%jY;Ă3/ ItgYZu9U '] GRod[b6ARj׫PlY+9?bϪcj΢TN VXc~x 2x*ajM:[ן]gwDRq,y D]okbDH#jsGZ:B\Ѭ^X5W!Dt_},,HLSb]#�JJI~OQ_O )Y>˹^剶blOl]͐�M|'Qd5"zV:Ydv!o4jà'|F/�I0Q C3"2ٱyic[Rrr/q펦%+lHpr\QUN&:2 .(*v>a鷦=+�qacT.cn,-ŗK|30GA]x0d,/-ň[7 Vt:2S-dOf˻ e;LgTp>b٤,(yeu(1bڝHIN)7@LLrO[8g+S/1 ΩWRŔ};{aOt3Kfj qq Ϟ9is;h&H0%Ud,k ǩevIYuXA$<GhL^J)Tj8�̠:-+22 r?Z _r|%FsljjҜj5cP|ys(Txr^k[e|󷸋 iy TUWE^b $LvdoZϭ"7?(Ul8xrPd̾[6{ztf D~7ʝ#U@lv2nt*c[g3䳤#0 ?.~p0ӒMsvNUQ[H=UE7A+�|w*.mɉ$Nr�KQƙVEUIL _h{e-q^sg 9%߾IufV{p6lɳ(顒Z:! ^ ?Oe$_)f!줃`az`_p�Fʭ{8fAMCXobz( $HA&[q3ʚ*Re`tO'l8ݟ S<Y,sA Zyr͊qL.CW8˛7#GęsqG1mnDkgfB|8MǕE>QkiR4'qsFH8ĸ <x(qY5jh)gˬ\Zj3ߢ -y.keWOeeUa&P{@,<CZ�}5e*L ⻋mˋY!SZƙ6ÑSWӤ]K|k}�c�07`׀N|߾k^Cf"0T,Sir T'A*DG,[#2vs.X{.k 2+OCATgv@$s` d4T-’iy ̏OlEc@V0-;YI஄cև{hL`skl�X֜F:}yb"؈#h̍2rZ3L~<9@ %i*Qjy^cMߞ0LӨ 0wÜ 5^GPm  <3r3nOJdž"dċ@!ppdC^]ksM|U)K/M*?iI/b=ZfnQa= =C' n M6qOIB< o{}|M:M@x-cE6g~K͏c3~6*Tqr 9 M#^ u=z4%ͪUIɊ$׊x!ps>jOR:KsN0loOФkV8w -O.H;�zCˉ:QٚH'>~b �#k| t5˒٣a Oy+50 MVߦ:%γD�ixՕX8<M:g'$Vl]TaEnQNn"2<#$ކ"$U_ Sr}Acj'1؉T/dpTu�>p$nֶCMߺ޹~j-{FaL^y{@nʧ3%lB;k-lp/c$f+P�yvF/, j+ 67%Wdde=<$x/E8Ŗ#ܰ:N&3كQWQՈG.Ueϰ5Fvì r;aol wu`R@Z)2ADSBh _pDr1}\)+-esUhaJXTTQa{mTI~}V`-3:zj cӶ.haw˗e]%,(!u*�y`h BKp.>Ɋ 7�9b cl 2l.3(EI}=1 ,9GkY82eFXѮsk)E f7$qBDA�otbT=5G ~ 4 Ҫ@k5E>BV9EeY&k , !jp&"Ƕa/pݪGzI%:(2WF˟& /:`h@ZiL |'rԱ)�~]ӭH�+CQfS1thA�I0k67$2]%{TPӢT2҉HȚ|ҵD3kvx+pEdOO]54}m"AAbߤngznM'[H|V˧3dt\OQeab 7"Ob2ܕ/.7$r-)ak36zgAdن߶.Ǚ{!Հّ"={JȏQTٌJ.W gQ`L tâ< _�-CW;�#@f%K_CgT]wTFWISf5U$ʱ2a]͈kXq_H=It8Zq]A•UG1Frm7"fIG x-YK5A8Ј~v`2)(ԑ%DIF#5Hu 2܏(|!%Xg ;ܸi$tC J8c7|mϷ67BF2l Ag.+'z FՊCB^]hsz,浡o]C{OVUXjwOҠ+Bc{sl;5:-3�bH59D8d)[aq7́Wf<b$N~k&ھv@IS{F!V16M2=@7Hp$6s܂UCHGKXWq0'B#~\f=2Ju:e aߵRv8_/X1Mk߮)I䢛*cy3n0eB\yI NZgu#]YٜooFѹuV11 ;+/[�SK~ūt/|Uxѕ(&s •^,{'t_|�5S9'$ho!M68t9Do)]V̔AQ{yX6,\>ɯ)f9\Qwv|r3Xa7kX3Z~A!U+�v{ޯR  [8c&샥LR6BayTj2�US<@�# q&@&Yδ4ȔAO-jӿ*>j{d#9D)f�^SuC]x'<OE3L'I{4Hߥ3! tZ,^?ڞ(*)eu?\".GOBirZu03I0$^/0Lf׋DdTe.JjZdpoF}Q4fB!˳y)~5yT#Rqn7aԋ!q-=:3X(V{8kplsJ2v+eO3_7dƐۜPk8y5g@=|ϒ ﰒCMWSH FCcﶣ[1pF}PC۟4R+x#eR1؏l\rC|ל]�mPQ2E WC_v(I~Z[^IsSR(5[bVH.aFUֵD)*H܋l\6.`0ʷ2:[)v;[b^&'ۈ\ey Nu4 fyXwvEv?$+ƿ/|٭ <nC$ Nhu68vˈ^�� ~!f5BMA7ݔg`l۩yWu�#[91y))f\ƕsp^C9^y),)$oX XBC63mqSy,լ[ cum< lŀ47nhFbē͍#aPm%n62b,O²Ԧm03yX<&y|2k|`m4LznWm YNT <cEdzMq~JTJAΪ#T{\їHΫd9#�0Nho"A$w uR@؟o8d?K9sGRY&r{OLs'y8?岬؜�_=zῼXZLZ�>cHv Qdhb7 ܆qJOw`&+6QcCWV4,ǀ7 %E+TJr|āo/Po$q3[W5;2"mT&ۖIz l1KzwŅ!H1pw^|0zhF컭Om gFK$"4q $)�Ti-r驪!Fqo4H4CLm<i/ TH 6ب>d?l-Q%O 69- f"@F}I)7Zj`KFs2?lG.r=3oDА݈劊iߺ!�$^ED p9MIK%0nEl}ŶNT٢=m.+Ѽ6V ҬZ$ZVNB.%Jx<b`52}-W`'5>D]\<9y>]wg3S?vR\r=Z%oLH$ %0*sshi2֧{cGf bm v2L0V )4~[R L973w|ez8-]tٮkֲsAF$W:d4w0zb3"XWvQ5WU_/Uٮ:@'7EP꓊>J55.U>yR̾6JxMmwe2^65,ihRT)zE?{|DVW OAQTflBl-h.lfET_f)_}V�JH,"\1PH@UJev/˂b&l2̚ՠmu7#VVmU] Az-SJi]:-lZmD|C*3euMq7`XM)5KR J/=O նo!EoSq -&a#SL<""l}=]O7 sXyIἫLj<*i+#waVu`y]mla4ا3-C$JONI7&_H]cHS^sM uU7O,e˨5Tө#oTbp˴9~࠾}8qϟ::1VQx2@SǨUru\yK9WAK=}y ?\2kFb ' UcJ:jT@6n[v돗 -Ap~&wgBJ;HVpt�c颯|ދ)C%\6mu}]]U6p"3U&i�KPK_/0:PZ/`Mz ݄qop} ii8@8vJT|cHT< }7 ҬOUZU%o"irJj|zP{'ۦV�1ϐ͹ϛU1o؀9A$Pj`gotsx 6`oОLAX޻Ϛ=\,,)-�YEQ$&wz[0dPU$o+*%/ \"@@e&@u e�cqbX]R)c;K'L]8olM?e'41P8@<LTFhдM-DT2> {:⸆ԨZc T뾫{1T7{(%.M /Q<"I6/:`<^G�X5T)7' CZ'e9zq("UeZ*J)cq _?/`OkrPvm'|jc"3:z^0RLrFz/\!Ti^c)ND.,GQZZ$S�'_sZϸZy>TiZPHƏ0^45Du) gʏ QȴfB�U�NCsE*SF�3:*346p"d+V ZNj<~ f4t-OSC,H[ �&}`N^,QYPFodgy�jb2?`t陑3ECX4e[2Jt+a:ui$vvOީ jF. VbIKK̷)aI< sK(@v]CS:~#}b*3M("%Zˮ/ |}è4/\H1--nXr<ah:®~"eyF[e%eɬȌOc};9j8򦢎Z<<% {[gjیM~4Ȗǵ LN�^ʭ8@ ϊ>C٤y/͔F? N4ߧSQϢ[**4[l$ S Ki�eLX9X£s9JUf=;ԲKF z>Ϩj �ceoݦJK> UH�k ;נZike/]-2S7O9^`b![b�LANЪ0yVleX=@ߟ/ EfuNXGh;{bI.gϜGaV;kV\'῏ ,sg ٚWNRs5xOS`I-V5�sJy'19ުC;PF~x0tq$DZP)h6'~1,�G( Hi\lg[~`ȧe Κ䬡oUI&Ŕw뿠UAplF#!< ˖r% E0�Hza#$;.,&y&fْZ+fwy`\@'zd�ց3onU)2ʳXvv[ˮ�׌md{[QgtxR c}F8-H�yG;za0˂~ܕ *dX_U@)fl@vR>C?$<j*n1GN�-I1s |g[x򊀾Iz^?_p1N}]-لt g$oӖ  Ӽl ^|o_QdQ ੐vA{ݝ)gSř'�QM:ֳ7-WCęxi?]G2˘n 1�22ѳɅ|ysf!I2x*Msm-,e>Aoe؜T+PesTUOh!HFF|z= m*3#m^TQRyfX U5BW VwR;1>Ley䉐y^_4A\ZE7Eɶ6i<:>}a7zV-%mVeT.`I4௖C\Kwo,3Y`{l$_;$8J)V(Dl�u\hS>+R)߽Ԑg+̫ sjd)C*"z1hYdZxJ$W]-kEzOxHbok mj=nrnvIj`Y ܄ nvE!$$Ĵ_+Fk sh);@\n-<_]KLF2~ZC%mP!Ui[ Z0PJ\\bAPi&tKWח-`*i a4_e7&bWSC2QXn7>Rt* "9fRjdAhmEm͐q>}sM#VcK12#= <  %n<; Xɐ :,N e $_S.`QB!ufu^W DŽyB"\*C�P瓌ھJ†Iy-}.4n~�Y  peԲMOWjz*P>ŋ :7 ϯkg~IweUSF|ڵA;c&}B9LN�gpQSEp@�IJja i|cWMD4F@^u$UpkljuKW\OB? 0D7cno**s'pW \UU2\HF̛{�ɍ:(!M#rJ O+r+<Bcj[`L|Ւ+T Y6TX�}p2QUeC /H$$RI-IA`VeGm÷0FWV/ DoZw'N5P5S TV/7?۞LAo8HZnJcgch�[/#\ `Sy<MWdh-b^Epw.b8au2K fH]5AfӊKea.* sg\dԵM%Llu9܎q {Hʲ(2(dRU,I؅ü"9 hZ^ca`(LOGY%Iij�ݬ~{Iu柠%N.L -[ز6zH&,Cy z-sQiQMR҂M7_{s�Inj w .TڏTi8e[4NJL9FD5ONmUH^nЈeG TV*3Hm{⦘&jZ0DM5U3L�2>Āzp7hs]@e,h\Ʀ�Ȑ FNukA%mxnϐTF'cnIn^@Bs&H $4449і $IU>ܺ[e(v %s E:|KAsc:XZN@web_-U'n"I՚9lѸ:o`JN8O>:ٝ=b)D7}ĺKKnۃx8Κ3cVFWpG}Ԏ! odmG3r=-ɳ˪|_g5_³Iv Ơy $XE53}9jR�V@zns24Ϙ-*;@5Dha.ocəag Yl.JJ ?ґDOG2R �y\Ex�5>JѕrҤlu WG<ӑ#?uc:U7$ ׁo52'[nGkA8w*ҟןǙI3<\ IKs/9V ; e�h8E%_m  FgLl/.gR n 7=:;vԤ sw7 � �ڏ6vN6?<SF*fyŋ#ʁ߆#\C]mgeG@$20biu!Lt:i2T6z}V W?cD<,Og Je`Ȇ/RI~߱[*l>D" YNUc8;t7N704Obcj]#b/oh�q5u2_\ED&׷"6ۗ.]$n(@'\5 8tTA0i�{*+nNBw�م34?4r"~r` MF"D_EʙUW8zY$@GKҭPmAWc,y1 |7ooC @<HnKDI m,*0mHGr6) ңf Ԍ0PdvCmzhn_ Oy~U*Tqn{^�t]if2ߪT˸V(B7v?X ݤܙD hip#C%Sʢ6[ \T!6%T4],d9-YR@&׶ nsA9q;6>p?J⹲ʑ'%Xo l0ڏkG;)OYcs\_*d*<TF[vۙƖQ2j.�Lr𭟀<LŲϛɔgbs3rѾ͹7:̈MW:YrsL#8~Tom70/+m>&-7 ^J5q>"30mqco p^{k>?啔3QfL I%yce"VS3A1v8:HjW9u$㙰7bjTkj9Ek Q?fU1ՊD\cn@ Sa#zY�U`g]%Fy"eB:.@烺@(. OQfrsx~ {Ͱ76`blcq9ŴEG]+[U5EDǼh3@..1,и;O[OIó&[E4M*2I`wP*;i+3+V:e0LB,۶vJseEu[I,NԾ}Nl~jp R2@5aI(d4*Y^h@%IYOp9z~lord9 r$rXw=co^4 Y`β፫Iŭct0mүJ^H&V4L%/#[G{K'eVd3cXS :="$DYKy\%WPK]=47!) 6>�MM9n,~wqERjvRR,[h}׭M2x[KIE -LHa }�?l~ܦI a.c k=f8_. Zurekd\0oEqHkZ`%OYHc Ϧ�CzÄcBsLdTAY|{}{�F'ABD9o!WM1%R[Vf$*±ۧ.x|Fc@.D"iC!>k>swY]*X2dSeewbăX"s2T5P~?hֆJ4Iw4:#%cp%Rŝs; $RRi& jţP,a|Ip)Fpka"ޚSLytm+^ 79RhMXd v/ ]�2/!*Q.ۗ<KikR[&-#4q9*;1nCO{cm.HsnNV[߅-AWGuى_|nD9r)<ڹhkE$^1sM;|- 3ĉTnObR{tX[4Z223-[-zj#gTk}:⮇GwUs<s:绦zTK.<87b@(9\s&-x]$ya^ZkCUMls'u1ujٖ/MTD<Go*(1>H栓#= ,'=6ypI`ߖ!ŭp9`OTQ> 0*nInvA8Lk8<8G/TAUSh3<5::|=y %Pؼ#>2V$)IJ3(?&Ӓbuk4+LspEQyAih|<FjcJA dkPp&v)-0DNksOԭ4VRL =2~b[辉1Hh˪G<ψ+s Lb{۽6w ƴRaUE#nE`>XTHuz <τ@KeW�?|g}Ês~ZO\0?yԈ2�p1R"5.67R�1?"⯈R9rYݿb[K`*I%+҇QI3]] s Geˁr^SOᙢ nl0̃u)yb 1 w:\ۏ@r&kC _F(ڞX`낾I LV \k�}p&mc\j,υ(9֭bv�3f %4a0yJ8'd|Id�RNk8;1\UNMuQ5E<2Ii#ǐ>5\2_"O$kZİ㷿l=ߢ=:XZ"wkClr>10$X׻Q3IHAXKK0B|tE fQm 62r>B!?Dv1k[eCNnsv9q?=MVjyn#?.16[�Np;s1u22i&6'?p,.~Q"{C:ʡ6d[1'ܯ\ïeip˖"0|x�^DQa9rIW׹[2^j%jJ#K;lw%ZwRnlN�t OβHZaaf]Xlˮ"'�86,|r)y/% y&ҦHȼ#ҹ,Y `ι5Ws@$#*嫙�xF{[1ʐElIT.+(A 5 R睛cql6ʒ mǗQ!i+2ˑh.EǵSy-'*nFӼWFo=>Xv.pf5YV& HIY.5$Zֺ=WRwwkLx3kk Qz<*gjd:+?+Fymlu_`G^h&,#-3/A>q9tc6]A%\3-#FN7Wq.'.Km-+k#>*jsEǎ)5G :4^v lpe4EVeNZ5$h{svvũdWg!@axL8j32M>SqpH$ (χhSPTM9S$�^eA-&n#ILTyj gJdhYQ܀q`%kd.\xA5$ k"mm22j"DJ45҄%6~,ha̅%ϡ}jL(A.TA<vZ^ZAj/.vGA_WQ%:=)+x˜=ŦmrEg +u(viJ,`MURͽ=K|w1B%-0ôa$C-9.[9. *g' &П? zTh)k18| #><\"E$Fc/hXCNz6zq)3#2нK+DQ%`G6;aV4O�  \jl)Qv J%L0p٩0K9WR DZMKyl}139s[S]QNkKE+~36 fQqt:07.N̺9ֵ6Uv[$:<_%,4Mu?\gm,8ehfִ̉55:v`B 3 ow!{^y{LhZU+,9}s07* kI< 3dgJxΒ?�w�:q@.Yg+RI=2GAacNU<�3F)ndTܻr CMO##ġF_˪cN2:ʊihk ŷse˪Oo*;K#ʂW�=tnG'^oʒS4j&EտRŀ˒;C�[ S,J}c b!Ӧ'4B^CH=oeN<I=qFt||%\_i&:MI=~J̦yԨn՛X\}c1e#+̪o.mTT[ao, 2"N<:OV|Q@C3!.5g35#5޷Q*6a}a?zh;햴g^,(DsWeymaP5~p&g\Λ7ϘG$-$@:Ĝmp#ߖ{zz)ihvSJ҈Ҏ }rЧ~J*usUS_<�E},s`w%=:$\ʳ,w֝%!]n-%pp9sܪnX/n.vQkHԴQH|: D9.72}DNS'SrL§1U٢R8$麗2XN)EQ.̩D^@ Vmo+ 8*X}O[cdzÀ;sd74T rBI>+1ߖ āHdi'-qS{YGayZZ& {B82L`GjZNV>+6γh]gZیxϫ8K?_JT{o,p,< `n{62&iPAF`ym[ [ yz摛,<`vE*<clns56<;+ al!O8N~G~!vyliR-X{N[.ϻ{5�עm"!Ϟ2n"t�K"C-?Tt  ђczT pO ֪IWÔaiřR!TR|ەwR_{ =I5[[6K$g~ifIP}}4j4܎y{jVbAiI׶ßO񁰐ΨhIsKHƌ�we8n*YN𷚭da< R="f,}TvQVj�tه{&"7[`s1^)tf],y~,e6͜a#q2H8}aڸΛ֟C>Oԙ)VGnm2x26pA7xM)T@ k+ ~Lih^1h7ox=7�4NϪkଟ2\6ˢQH.�[xpoפci\eU_%fZAո+z "{#J֫}Wbܝ_ LotC׾IDN [o`-�~HUgN2}%Pӕ@vL4 iaC[MȢUujuېnC),S@NE/3 [x#_{}M<FtQeM^GQ,SqvR8EFs׿<W@#U $ߜ@ 'fSHu/FIsIߴ'� fbZo!.SٜiW z$p&ߞ@Y[eRFW==x%%7*4)"i7^gj x"&zsӆ$̲*P5_դk޼ݳO { σ|gWe4UEK _PenMs t=1.oEtgxӖtBWN`* zjcӡqI�/1kCdvWMGG fQ뤤6P&][#tcF6p+22C礆<(ޙ‘H6Ď0LSӽ(eъ$o3x`K^@/|H=}O~HCOI2aW)9 {1#$b@1 f͝Pd] e\(<I! I:Dh' )k"C+{ʐv@p&L{UUB)⊮I##.ˤpۑŜY3QM-19<n@;b`[P6p� y-M]V-4͠Sӧ{*�|!9veUTe5%,A" ͏-$?+,5<G$2\ vco镁F}cuLpgQ&if)@*1[^;o-rEƀgt=mVcMM==p̣[� /i4SH%jJɾE#@^ý6ρ\7Dr�1YڬC_hQH>ĺX?$[3Yߊ4�@ %]3(Z`<kO\+Ql5txPD4;p;~q^Ӫ Ai x3aVKj7,, jވ8Y_^LYedji6.OI3SV\yd5CG3�QJ�|2'Au(uTt%V_WZݕ V\Tpq1d뙒s#V =,}fUONo�/󉮆; <9K< R/ p& H$JnA'l}:墛 yjGWRFdw ﱿZ$H7L9G5BCO< @!FBܕiw$簹|+3gn>oKR/w[R~XR JQ~L I)*N_4�}\~Xs*)֞2C=)-=$߹8{54�ȿ(iAC�8!ЯZt0$~udѦS58>vKe40H t*PSf l�Ǘ �ހi8ZyI>7 TONfb mkG޷quj`0" Ǣ9Typ5._]E[NxI?�\m}3Tpew 4ИTH$Jk\Z@wJFhTE: RF$9 dKfu|Pb帬p%ͱ5We]%<#zIvmmALB^ zgP? P-NmO>o$ǿ bnh- '49L?ˋ$eZf*.G]Q=li Z @4` MĕQ=Jb�kw63 b9[[&jZ٪[:#A@yrې>�侵1,  L4,KrU+dc/U]dfn$]I{[s_n-vz͔@lL&b]5X" nrג(F9|g 4b10ou4֪n4-\nh{C [Ĺ1:QԒct/zϧTbﶚg_RzHYt?`no1D;Klj_tOBev۟l =rYE&ab<ϲ榥}ЛG7L+/< : pv.2"ب<^)B3ybṹ*unG@'v&KF)i�Qsӯ\uF9#�]-Wfk' Lϗ>+z7 K.cP SZ۩w8*E%-1kzL3*I MG{1q-3%oDL@UpE`Co8+?cs-9'܏X^Ū8A$Jj* [0f{?gi}7),3[č‹m34Ae�eS=QhM EHɁDu}6Od@l O[uZF/@DLyQ[k_wƖbkE7ÛF`Gɸ=V0�"lSCZKy\'u�,Kܧ?4,!X Hca0)*UF)|DE9Y@upc ]"462Gow=vř`Y#VkgY]W'I-l]KKק]xjIdRb|dէ$w$xn&W`ନ�<'"�29Z qoAl #J o=7kXIGe!v o:N"~:%<]Z[O冘Æm`:lFIc0V;)-cRHp ښCAX]�!ϐfyDI=5TA XrNV6yRi@^pn4dU͔f%I-Ȏjp2fGܩr]5%~e"<KH*,hP0y]4e6YK)Ϋ$@x}qHEga's*\H1ª) 7ߩ8+jb7H7'W$Q)kߞ�xY�˲̿-*!2@}Mא`#1FZˍA<5EAss닾ߊ2 wYI*zm3 ݏk:sE\wUfFJL4]^5@yK=֑7VEZ3dT%o.}E#R {9�ϵq=ltڛDIP/)y*j=l',7]@up O9bZ sr82L\YhŦ%Ŋ]Im /cLz(|5mf"$y1 XpOQbHmx[\2tM/T53ud`& ߖ#v#. Y#RtyGcIEz]|R LE$n�׿*Kv8 K⼲31Wr!<YGΦMDk s7&ΞHkv]Y\X@6%euETҹ+Q`P~ί!TV)EaSep2kևJf-W3bwl-Qܺg1S[, @9.)a;wj;ܣ 1VTK-L0!̓& sI81sNG�JXYui8`\ i�j 3][rNT\80>j88<;as@=E5 Che3䖓5ZAQ8 3z"mA D̒7g,Z'K.@|9l<rj,/u,\DȜZ -DiBZJ|%B�y m1gQq("'ay(sb9CicU-m GzR p[ʊ-pO*[Q("=x8{&9⣖B7Ua~xÓ!U߸bg~5ee9mK3R2B_{k\ n^zA鹮/69ޜ󼞈v^20zkh1�&\^�L:uʸ#aT<6 m#dWnщ+,ҖkC:o=̈*湲l5ﲆdiSAAO!D=rF ܠaE@e ։<Eo@(2BŷPy} `0;ú/?k0J4/QTBE6H_lɄ2 excV(j& YBBۡY *5h]gL~YO壓fohQ<f S\5V|Ҝ=VU=w`[%&CM8M n̄s$�en-}s馩vz\NV$C}[;%*DZ jKp^}k.RNY<DllIv#tYH0-;};o� ZkN}~lŗƍ+`A;Ud4kj/:Bb:Ź~:mwމJ$bNyHB�$ϟk?\kҙ-D�\J,Y2OYd3J&[F㷾Wi Z.>uI�g}WlF0$ ;{afdZ.^ڌ8A{о*(#H=NkqCUX XMzTՔLչ_~_ZWpef4PE~�-�Xe.!rFXI^ 2(H?1j:d,Gl k|<FKj&dY2̥ j#kr u+u: Y,f4cОVk0MsJȼFS(3 `s,ߋ,//,$f$et�˯.XviZtT%*67xuAdSgrb HuX [=.Z-9i+^s`y<%W{st-�d755F$`)6ս(|v89+*ƲmvŰ Dcu4RRWR ߽$+Y4w-`ui jRn�IKby3|�9b'Qe77ck#8NIV� A sq~c Qq]~>һS([P�8@ %jKM  JɶǮbp-YڎH(' 1snK⭫yudiWm ucp)Z=A`ے]o Ymw�g$$*l�ߪEjOU>x{f I>N1x aՓ+� l,<&Dʳz0` qsBccgH M /x;*3y~11{\\rƓqCr^_hecq<2ͨd̖tBlklCu֜x @gYVyWC+K3`~oliR{pH *~(i2Z"#emefvM8{&J ?h5,w%GUf9m;YIyeb[Z#SU/eR PmBL~Tރ�c_l%Bn5, pR4)sf:uhUs|@<POM|09jzJkCO]-'rVit8H|eJLa*աI @"7�cpeEiNiZ6/<yǀn qQ>XdBiwg!<=<AMN5I;IXi�Wp*o$wUř\[VͪL$,WO[ywĜz-iOe|RHdrVKIIPP ¾PWQ͗aн ftgYo eK -`o8 ]y$0�mR$1f0@{\wpc 3y)тӗp'L22�1p g92N31K2OEQ  -mы4H`c\?J)&ʠG!8qK&#r4:&0ji`J n璥F9ΰϻ'*hU偤'[\1K@D? NSH uZm؂kChvp- EU6hH R4v#"K�CN$6>if:ޅ'/*5('rat� [=&>\)s9Au@n"-hKvT3Đ?AAid!O^d[Pᳺ!>1||&L'2CIk?X;l8M'?ø饃%%3J/ MmVCp}wAr^e2Lf@DyQrJ6"(aX29~&GyU~S_m?lb t�nYcQWpn{,U Gp!J=< j'5# [)quԴqd|[\iݰ0 <KCGU3rnC﵀N#UͣzLւ8J-]cHċWe R7 }̤>N3X2. ˓-8 ,NIϚ Z�?%?JΟk+2YiƝPh kߑu2͒m>I 3JZH*9ʿra[|AoTEp u`RǗ#eu*`$O&&hk}}4/5LCP`b!kejpf\(.f |9׮Sz6'd=W,di䖓/j;E[}3MIgCߢ',K);cWXۚ*O_Iz�I|Sp 2a瞨VaEUM{{Gԙ?u3|әI4@V9؁i|9'H7oSbEv6LadbFq;|Ň?~)J8uCV7unmcdv;6ީzDOW)~WdӈY^u(H\|ޞ٥o޵(H<| V_".Nď Ҧ �fFZ^X43<Lqj(}azP>DX'I$f=K艰S4V_L .Tr^Zq\ ũTqd 'ɑ tT �/+ony Z].]rXv2H6>&2#~ۿ1 Ue }Ney.7�<[0|U�Tk)|앂RIv{[lòwEl XI/K ;p˯n4qf5H\v(/m"m7NQxGk6=v 5{ZH䊒(!;kq6/=ب <a*UA$6#鉪#} i X$dFBlK$Ei. 55" PEѾO>^l$wY՜/7xšFmӾ-HxZuB -!;~,Fz{(? CjMӮe@�d/3Kws/ėb[&s;:JU!W,IBK;`FpCjCB+_a˖ةfkڬ)RrpFÛ"an�w 7Y$W:A,~9cb$Hp7tV9ϑ\'e{bNa`p8,5*Tv@cNt-5:uPo IАcN|J�>22;mQVYgˉacɭd}^k*0dׄxi:T"i: 1Ƚͷƍ~m/3_\-/%Zfh@z }M~V|69 %P[a+ kiUv8"Kd:bӗ3SS53xDNmzrmAi#]g%V# \=c0£"7[s ̒N�m=}AV Beb#"DGaOJnm0D@3G(JK an ʇTi0ͤ|:`i�'co^T%\SM"|AO[Y] ʩA*lB9|<P <. #wfTiYTjGʙI>wtO cg >RWf9UYxY Y[M)7H>+fViR1bu={ۖ٪g\<1M3|-+]YYL\#lz]j ^ВH ﰾPgd}R^`X|xˌ {]VKA2!@QyEj uH@zr@s+6IsZ`�u`@JR j`ˬJ࿉E9:AQ`O @12o'MoT<1J ig9o>ZW6iXA۝XN6a�ˑCi!@}3LK^{}9bpClƓ(wsX]a}zqgtυU0 qm cࢮUl6;㨂TVa N�(G+cibY!^~9w�EH:т|I4J4cG,yll>`n퉣R<.2H"&@FѪp|52S'91r"E! 'rzd(-H�doNw$GIKS[b�O_R1.hmƞ_5 Q|VEYj.T YCǡzɌM~`u^ KG2O%;YRzM�pl;PF knE/$hdJm+mi�h"$3lS;t5FRzP !IlD� Z)j:0<v*j9? FJy֒ukv[pK6~8t͕d\0 dsk큚:�me4 S$p� VJK$C!cK`�歁 N7[2qUE>SI%^M1I@nD0 7/)<k2WRKZk;gk~yD&-9[S %srf)3Hbi Y{#٘CV֙1 Cg؝Sُe8o#[Q`ۮ�<v}?S[ C9dM%Kcrݷly]h/?x OKXpu[-"nPerYZ�Iy%J @U25�Lg�D-Z4ZaD` "̛HtOR*CV#PI�a90TH6 %[kzCdjj/٘ʡ=%hv( k& La1v_\ j06_cm\o ^X̎#bȑ{玥L|昲fZP3CKMײuqVBUvyz -39N ^rLkd:5;'a{hC[93 060]SC1dn ]}gmA$GS <!x cCKMd։d#!T bLa~h{MuD emJ €-/ o$ �E/d=4XyM僂EUS̷#Fbi&{cA3 ͝MI�/N}}x"4:7$O]Ț*B\;s"˸81ق:q?( Qk=>v18b0&cz=kvoGDK7w%7s�9$y�$7M&'UX {v� z.,pl; \/<db#<[w{`$ TNfɥ5#3#bX KD2;ܴf*03泆, <ȳ^鸤\7-Rr5n_w\k"D!�}:RMiRX}ē<Y{U @`ݯTiՔRonbis%P iVƊͩ #!mvnoF%@YN*aU% *~0J#=}=R鹴*1eRFW?C,W9H7G+oAO[<TC[؃IT%rUII�K{Xsǣbݚf\C!rfgb9aٱ6 )d{/UT*Lڴe7 r=h')�T$D]D"6;l gX~@󸣴£juhw2qk&r@5N0A 'l&Vq`buz|֧êo  s 8b2٤ZYdv$r6qwsOP,eL0c0KòP�tZhݤR*j -s!Bn[K:ʷkoםYG =QAhƨN++%+605JH,լ*C,t4N؂Gmd>�I%KA#c>"&oG{%Tee*J*?9i5ӗXGciVk7G?漀œ0�)CC^ul$w=tz!*V'_M 'Irw~�{.C؝;,y.:Yjuw]g48~D2ݐNfwP&Q?<bZ`ڻKCo{klyZEnZH^݀ZeL9Jr�SЙ"W+^!õR|%-SOQc%(bpM@_ M,'.T#msby*Du9 좱h0¤xAf%8qg8�ጏ</Y(H3kb+">H: Cܻ:iEW-= A rM|Iwf*XIq>MLuv*Sls\}n`LO{)'JG*H )yjSeASIuqF2zEl¢4@Dzu;mlD9F`S-$InZ̪:˒&WV=v`u�qָ�@oJ6aչfMVL]Q9Z߽BC zH#BꌲJ_1rtwdy5"j#oM3i71 V�;_5sdq1_~@_$R]uE5c2jUIvQ:yGs<i9dW4rȏ#q@ Yg*ɧ(d 0 U^2Vk4? }dJ`-p/y>iqLƋ2=H˅m<V *Pu$hVM_ Of+T[.H/=JUXZK}Iԙeffu(΁} ,֐rke^ʬ~-qOCǜȱ*^@^c϶<v7!U�cf sml332.-ǖG/NrHo}k{�c2W,X:@b,^׹>dIGe1 Ѥ6{T elRZk�MK_{g֠联Ȝ!Ow×_ݹCZ@>p7yUϡB3Z`-խ6MRٰ@dCZDU=olPSW@ښ0TBc,ĴJ.;߸,x�"v-_|ƢN#KKF,Tbvf ^ Fl,%^YfԴ0䱶Y]خ�~(H-Օx @ b^xsr^ehvCEcet쀺kM>'O6X \{_lRCT5< _5<hȁQ�}0`erሴgkJYT�'c?e A35F<4 \,}ZgimQBس{+NRNVu| ZlbDxr3nf�YtxxFj=N[$v]Zۧm,AމJKU+.[n y#$l; =+[6�{ 7Ql} c<,N$>!D`+^ ůy©f(h18w*$ hp17%}8p@{(z,@ejz#w#ab o(P` 3`/iL$U؄r�}W!-�*bK5[v#/ulJpA_}o .Qۇg 1&X $q&rg1Pᛰy. IˊJ'I(-v^$SFp}/튓މBq.7̵,*wɨxyݡ!{`Vk}~c$6+&2#=U[P1vm `E�f[=Yp.lƙ̢و7ۘ3#;d 9E?lCTi`Q~WrM k|/! '#/\ڧ0IK5;l.g4W̹lՔ,v?L=U$R#H3dt9NIy-2T-µ=1j-I!\wc j3Z(֨Ubn?<Q=D8"9/dY\ja˩UڙW|E aa|,gm}NUCETJGv,ʠsdoMUKEDVSح͵N�񀾁KEyYeUMđeȾ:۞ƶC $2LqÙceeԯ(5P�n֤#r�pU.y6qQ y)!e-4[y0#nVa +4c^ $0"l0iI CieU%ǣ}s�Q!s)Tt6H4AH #XKٮvHÌta1h1<A%dkL1B# "0qb$+! 3s{pӿF0x ~@c@WUk~kzH.|<-5%Dc2)3sߩ7ޘuD˴1DV:(#_Vnl%Qi$^ڈMQt*6#k 9Uts;DXZ=Z(M~MX !5Z- Z)HakA &_Vc"lGz7 qde |/)I-9o�Gk3i lpV N@v 7AkK\UXef|詠.D!`4 =-Iۜn[2Szljuv҃mCqZ`:foC:Ъ%@R,oKZ&qL/1M8K0_!uq DXR} pd5r\T 1Z8H CM~Q|/ʲZUncru8zbP9WNcﲅPq5攲$cteqHauu g] rzjʜHdtb`ʦAn}8ec˻Ufʤs \1cG߷6Vpp&iV#i0xr t^@uP 0*L,|)fc齺`/T<E ω2؞ 2-nGPEh}TᤑY;צ &ͨ*ŏ,sHE6_8P%F|8<7hc&"Ȥ{C1Ӫr'M9ܕYExIxqA/=v"9zw8sL<_ pgV Uyd�O-:[TT\5u|`"n ץ&k `f� aUZ#E[H<|me3wCgk-2ܸko=Clq46LnBLyA B?|x֓Ϗ?ToNXaܚ(p%|FtFFEP`wi�H7ZB"do6}~~-@cLävH8l.8}o`vd&.BWV֭ Ch%aP� TGGHo w5k@8l5Q4G(J�2UZwFxpZUF}Ol&UrY jsrh eVCF$E�Ӗ ` &Q;2 XPA$ {bn?)D^8L42),q<M%ȕ)1q] XK]qU]P6}Fk]ME,Cbh#$6 ]׊HH?xdkh&8NjPE#rH௤\�=R5.ϼѺ(f}#$, pfqG17/KʪicXX/nd}0K ɌUs\4HQUPkZ J{|7T`S$ߚaV]6} n;iJ8Z �<%ǥbjQ]ϑt[],�ߟ+%&A3#Q$%I/0W#8>b0rRB&�^x`w*P17X+ߟ-=IpzސO öpZUln{/Pik.%HBNnGa��G4W/-J {?,|ې&\dZ9--B<B3qv k�PjhP+0{zdiֳD^*/S` wVPs,a}pz|,͢�%Of;i^d\]EpJh=J q�hScy~M`fAu�yt9mL,c˕3`po$�S:YdQem~+ ̯m\Y`⌆957m8p !ü8k*4y�I#X\p3+i堯Z)Ir lJ ˾Hmv[SYVRqo/HsBZ� w0XX[~F) @ wUOCzFR}a㥻`!i{\hp͑SfM+b;X\t拱 yUJfpWcai1gŔBh&*ksrIu& f3ƘF~�!poZ`ew;u+M,hUe�~{u%EfDΡJygaJ[6'E~D\d570Y䖚Oy 6c~a-2Q3! ZŠ"A,,S?|C-xCA /{aW歩6v}IM}/R+cA-)8F|Olr(i'�-ܽ7Ω{Zט3 <%j2d)p ˷>)Q'Qxw-1<0չ�#p. ~^k8˱$=ɻ" $j֥@؎g?w5 y!k H7d~Br\%˅8.<~g 8H8)kG*3.eCe}G!YH4[`JLޢIH\B[`-�}bzwʝG5N_ILK{A22JCuZlƊ9ԵmU,&@)j[h 3CJDu,6Rl?{`opUyc0w{ek. YjrZHJnFxtj4.T&oy%+̲D(E}R3/!sձ03N?ksMA/5b(bORcBڏ{p\/<!VdmUSI9ܭ튂_#B|�.XÐKMe&Z�+:Cow[Rq>q-^^*~MC�Q/gyO ΌdCmF?8U] #nK큾ʮ8κD Y5-e\`ډOoTvnx)}&#sVC vc*JT.)'K@h%'}z#V,|KP.n"jTo.:,89x}׮ DlHsMь#k-C JůkI;[&. GcN,/3�(Xh/*3G7SzUyϒ5~K+qT' Mo;Ycpx<_L��pp3\QP]*bmUW 8>+UG%H4erf5[-mP-tE{L�<4:GgO3Ua{k6V-HWeklFݺn1^ur64cn[ς5mMn~#lH Z.D͏(+9nbm?Y8Nz*ܫJNS9<֛i'(U %#f:ߟj7'~ku+8�;= -"u2aVHbTƻ mh-{ndŸ,c< "p2�Koa؈aah:jdkHىn|&jTs'^�c0 {͂b%V`N%da.B7 =m|7+l�QkFw[Srđ[}0pA'4h$yAbhi}ʚg6t7hp쏳/h:Ǵ�2> o}VvWضQ3rb\@z�4V&VvlDJ+6`z~m¯X<D vQ11Z.{i1kqR^bS#C/P\#B7h߾!ZHP<$~ DbS.#bv�XmӉ!4C+c,0ZQ\3HA./$gM~qāМ�嚓 "RvN ڸ}UGŞAou2zc4˯DǺzc> JZ7ouFO4<w0TYm?}pJp%#C 1yErhT`s}f7Qk m c n  ZLKr (o>#G\JEcavBЪ*�+LbJ46!@(#|f屄:UXZ�XCs^ HX'Ur|2i;}>RI~3Te`{{ ؑߞ A pg%[g7#A#e̲JuXrƕˊK$^fs*+I�olml�=!x;-$vB}r^ T UvMn1龞q�o rW_ :1jc@i=lo%4^vXrJXZI%Eٶ<-N[[#O$]IJ3Jzw"]!kh_A=u+43Mb,YjVy4X\�;KZMCx׍w*2FЇ[NR u˘~y? %Yp-(TlE {\ݹV?PȐ(�|Nk1 -/(5km4)T s\x%Y)8*rim9{SORx-8`tPi{N+4q/Ǽl7TAV5&$qdTzdTfymd^ :e DL+756_9Ʌ�l(._g#9$qaxtU^}WAE&i}I#Rkv_~ox)4lOp<JI]&�2#>H=ns5INpov?޽G\ .c+{밇5r. u<+CNW[4"$aU~@k֘`2sIǵӆf5.Dz$b ; ^=0as1c?sLY: )V$) ͽv0"Z?]ᣊG$5TȽ: zy[#�kuLJһWk�s``JZ Y?٪W�}� j�tіVjhu"mr=/7"Tp/!nF㧔{i�P @y[Ӗ(AIə",yRrIX#,fK䞩Jښju2Y ohrZ;FyXG$r2V(Nf|l ,a9vn]ĴU3x"Pu}wi^g9@A9I[;S4,Yiwp'%j zc92k'Yr9p$B5#%Z+򌚲0/8XUۑ96in&d*g8z D4oHz_]Y'ej*GVcyp%&49+cc4nVȱ�ot\}@I7o̶tO[Ix 특eatrV|?R3GF"н~.%obOsZΒF΢ kZ[i-YPj;1oSź +`o_W2-S'L[N&b|&ޞG,A"ܱ68 _`'sqϳ9+B߮<nXK+?Iyk!uZA~Fzz.B=nN3k<.Qz- ioll쥘Vmo 770 l`ܰ KJ5w[L &q&ĜÊng9KU|FoMYD oD0[dbhpë\H<co1Ԡ kD^q*+D{�.oo cD3Ns[&-| E2<6OmfH Kܬ~{v_õ3$gl9TJ T39"=V;p/^~ X5|-$Jd$PL[�iejڍ4=SNwS)1I*!n>x#Z[/l*?tJ3 EzdLfָ\jԪÝ*MZ~b;hٚ 4Ϛu i r~ =_�|qr`A# {KӞ|oKNVKi{\QFGg??g˘zɗ$I*)@?| nۨxIߞ=}q++aQSf#6f9$9ݡف9<.N5^ܿLfVkg.;y#NFBPUӋ'>%8iީs)P:oD(T<1mӦ8G~J+=$? w)_D@[P~X37^Fya}Z)vaя1)j|e[ؒ:?L1 @??OA4 a{s2P&\M�Є \mdn㨿csK eHZRKa{t?X^CpxO%F :]ce3g- $d1| 6_*Kck|X/O!lI$cqvݿN"e$rEpx`=.pz6(>`e F# 5кT޸1<&CezS];: ӻ֧$ *xyYہc۶ c-`~r.%KU�$pM0k0/0=2e%TaH[>F=|?$h^ *JL83!"E#[yVCɟ .;׮_w"ZxjicssÚ=VLn]8]ث2mi#R{HGX3) nn@%]DO*do3rhtL #~@JzZxV…o{[ppԱ+.o0Uk�ls9P2UxSHv.!|Ub+GMʕ 4V[ld{lv`@�U 0ʪf`GlXZ܏<,KmTWsH!N\M >"VQCWZFY;NkzmԦ@rEԯR/8+\Q4du6ᖺ`ub�I/8LNjO-{r D˒p7P"렄gFb$FҀHtF5G"Lھ7jz|nB[0VѰ dT$QFcr5)A,U[v'pA6p?]/9 ^%hdSHCBP -=�"C�ÇHSJ%tyT#(Ԡ60.k<57OS TՒ`Tܯkߨ cI$D $Fdd JU3MʖePK2zpo=I"CES $k_r//aqcNSvWA*;ޒ;6�dYF^>hLIsk5V eT9tb1 ~Koߪ5!LE#;jRJï>8\"/> 8%gujU˓ØLVddGR/R02@9Zy9Ie<oLEZzVhk]3sҭ ;.i)/CmO aC`nӷO Pp#EB+7m] @={5A<2l.5_'rw1PA=Q Oy$S'9rd4f$ոVyx % T&sʞeR]JFo pE\ّ-oԊ8Hޥיk1eQK@igqe>eQOL<E-0ؠ|x)~~}aAMes՘*+奔_.H$8ȉk+i3Dᙣ[~GlCqwV]X3O\;CBsjDbU^T>v�;c09fh8Gv4jf6-k{uW]vWMdIE<;Bb$U@s<" N \-40R3t6;MX$淾N�`2Fi1ᦦҠnkrF\r_RIv+ܲ. LN-ZP! UQ}p\O/ANd3*O$܇nxA�jN!Œ#kM;Cxw|).sM<ouj̣J,4D�M�-]>_ R�$b?}p: cZrr3.Ex+uwr=ug 'b-tURFѼ`Hn; ۵>Xv3*8OPٖQ'̢M$͵by: 4`o; PeBSF ,PWs[{SNZqy2b݂s鋴qӪF-6;T|]-FaPpl1N2y_ر:COj ,z mv^:N_KKtW duG_˧, =pU{+7(iV {Xza8l2= I#HG-W L"xS 5EtnRDRM}l\8.,P梕94tO 1vϖ-MkfM@2"g:JˈIVK1e+"{a^�)izKTDJ~3ދ;V?ĨȬn !GUeZ5Pj4(c%a[l*Si8DH)w:$0jqWl4D@6i% ndM)wS�˝S;ؐ.-n!U5޳""QTkƐ0Ü^ M`4hGY$P-h�|µ*rN~_ũ82 Ѥbl7;Şw$0JK)� 6o醹po}b76N�N)2q$#Y <};{wŋZ-߯-Cp =8H\rPX8ˌ57=~bn Z й錈d7%w鿮"6m[r. m>6s< ؑ-At +!1S|IJF󰵻}f+9H*ϲm7k66zbb;EQdf| ! _KGi?v$ mkcu>ƓPI:+Zl�˧$1*0�\LEfMǪ`bݲ\MAqUA l59w$ %,OA@;-Rx~hǑW&IԴ9fg[)C vwg|RcI՜Jyk*\G%s@LϡVDcobv_GcoՕS-Th*Ӹ*PG6UiʰZ+%,25ќs6o\EB@1H&.GM2UNu#6yXX\P&MK np8:E2倲`/~"ۧpuG?L9SjM2d3AZK5S2sy-:SA*=\6% mC. R8˟+G%Ak&s~xaZ; Τ`饲үcR(C%MQuw~Vq{/nyzB9(de oRd|=Ðx_mQ`ڥQ}U<�U6,-FF߯.9RYg;+OXnmkDa8=Rm`dG²)ώZ,jmM-ګ3Bl8c3ϫ24!S3PkCĐ$egt_;eTw i9Bە�+.$7*>�dH|2$6q$9"TdFyeviQE->S~?,4Zt Co|sN\fՔ¨hR�_ we "| iq�dn"N˕W51ePB9sc(rUYZ۲Os{_�ng^ =|S祍" Im6M4fm%$ 9XC:<$?y0{= O <5(*yFn};љRIxn[ ElԱ2nHw1/8 :ƽB1z'8 )S#�Hy C4#j4<ωF*<2$n#DLNFߖd\Qj1%k3<^Oc΃*-+Ձb 2U.?*W]( Z۟-�\Z1]IzSFd`E2*C!<�Jۨ1tyE*&S ;7%8Z׺7<˭AJ~M#j.}>8fHglWdO,u*�X,["^De>WBO!c µ[iLRٚ'K/|GI4Hה q-h#'gӝSh|@. ZmBgrrO2;l~ᆰfVͥ,Tn‚6_Jm0n+ +D\*T-b_ & iZL`fuCc]c+i �آoÞUGc\vE'V};H ;4'휐iq: {}XaI*2qAku>)łH;0gM $PG6|l @Ek[U4`Qu}0M9^?E7$fY l۟\PRp8%*=t7;^HU.$s.qkk7{`u*#SY+#*SHPFwb9a ʖo=RVeHr{ ?a?H:6<,U_{ەy_AǯBy Z\7,,- .p%W6E Tcǎ?QF6(PNp[h낖+j3,#76�tnrM:^À!O[Y+!�7cm6NFͲ KQ|<*7LxZF"5*|M# "ds";5ߗ!n.7E7Ts);'ec&\뎨O&MzB܎eaJXr$"8IBZYh ]߲00"m XFe-QEXѐ�.}ň גGia~fPDu/ipt0S +|][ZΜ-9XW2o?`^8ql/TRZ� =lpM28M\gأQRw<R@ !-%=Tc؛a[sI"*GG0|>x%'7Yh*WԡConسZ�I:D7)@.0 vCӊV"FCe@)q{qw< urP]2!| [oJE$y/�`kQ 굊e (G-6�%<G,eEeR]+�p9/V'r6ؒ?}j.쳪>T:؝cRpx$!eE}l9VRe;ϙ. /)lmtˠ^s|)s>''*:<pnۜzj'ho!:ƚ Z9u KM}.;Ǧc@h9/9l_x寡]moS$԰呪~ƅ:d.AJޝڼ+ԫt|Ř;O%mH@nܞ*qan*SQfh&T"v8 jFB?[]4rhf2lmFΊ,-}~YN TS; kǝ]! 6TSt) k`nq!Cr总E *'|Q*ů[<w^*?=I4g͔P�.p]fC@3ܞ+s6FSy-c;_K@+46]ˬ(<i.^?Ze?-0Fc<$!t: iV`!$'ܾ\šu2 Oj1& u_\xJ�"A6k;ʿq):nKH@ӹ׸ٱ#N� ,3]] ;z]#%He{a"U&؋&g_*̪ފiaxFio×> ' 7P89;Ꞩo"h\&O)ݎlWij@cG!<pBObZedNK6 33eU50K#k2PmW9q]N~) 9孪IRIU uW}7Y#\,c4Ȫw�]q!Eh;<܍T>UB^fu�}ckz m^5t3D*gI7G\Cg�5rcDnd.G%^Js Ӭ6O2}yߴ_Vnos1CKAyf+mźn?:FY/<e5J�b+>}zp6!8E@(j>$eE?|QpǨUkkjἮ:J Fme=cu]�q0Si}`\-+9u"H+ 9 ]@ u^o*aDs7.ȢiV'SǦ\I~<+:tE+*RtSer@w6ߧ\FXKeǏ$pa IVdV( o}q$zmI& I1g, ٶy2m<mQO<BAsÖ5ףgӪի,o>~!uO:3ҏs# ǖو�JWћܸQ5 ȏ{w�/xx0kﯚ'ZH?w `Yjlp1~FM^-/1)\ s/n]lOu!GλC0в<> dU1(M> e6e21 uJ|88!be5(cc�&r;vr\"fd ߘ6b1Jj4Z)� \/jT̄\l7ASrҵB>rh{l5BE؝۶)bi'X9ۺ(4@n�J=僚Wr2U$ SibSl6#ǦY=�gS51ʲE�\#Z�k8F_(T4!>P.wt4zA\\3@ Ͻs&Ok>sz=2 dZvZ|$_JR8n-EO5<,IKV_4i}۠[fC�2U}]@G}d '\npiS]#;vUvzrw~\}f=? z4%FeOk `B�7E'wǧksIu'DSxl2ZV( i*mc&;K[h6?�IUC ,+|�1g5;XхѬ@m~x>e? >K٤A|cҲxb쩉+QI|{" |; / a4-M V!,N{rApЅBHsǮ @$b5rӐ&( H33/sОX5B`cF3D,lF�ICK z8+!f,ߖ�.xd6=5FwDx ȋܹ[kt�7˂Dmbos׾kas TTYmv_ �AP_"y(R7$/3|ԜiW< A绗En@W.kʻEV,;kӶKa"qJͿ5H# 0$Y儔@krk^3 Ԟ$r؎0z4BCm7"EGq=�Xو� \{gmEV}jPH yTв5U Bl~H7'_�_SOPL!L {Iր1:}%?�|QszNɳ\2jsᘃlz-isgUv͐f=Zpg'T<*"M76g%y1q�LfM[OIJ FfO`] u흤F歩Re3QQ$Zs|4_N$�9eGkG@5~۶!Kx-=z>f"Kh00&�8hʤ4S.%`.w ]E2ׇtPg5K..3�Xs8B*7a9~:. P #{`mkZey im1U_s|gf,y]J�m@vSm-{i:Ǒ.A= "f�z|hRs_*at6e (K7v+DfMBFM9Eo O~xjg;6Ä;(=*⧕$0ʖ{MvFۛǾkUh0ܵFLf ,1#~4Xp_ $2[vZ ~]=~Gb@-nB}A.n:@@"xy (#$AIߗ<f*6\7G vd�wfgm31IQc-˫mӧܳiapg _$R *wߩkw;Mڀ$E`屾n&X)~߾x-{EX)` ?NYk'kQv[ep'_Vm6}gh,{$:;?|br\A4~ܻ䧓4f_f'ĺ Caykҷ.lߟq[qDjak4ݻza×/mA.28}1'hs}̪�#Njy{k( QfybfRR^K~O�3d` l[w/Tg2ŔS8†ɕE4120j!؋. ˈ ICU/O]UyդuSV:,rjoz_q"IkLit8|K %&[jax\e<KvG}>ovp *̦qYV@2LF-GR Q PuB-}^MY;DYCwcOsDRp f\c•떶WF՛L\#qiJW9$eyq6ߩ\W۾6Ӧ <g?hܓkT)(�b} tgx.Ͳ5![&ͧpJrqϻN6]q|, vW]=mu0'7Buq y7, .T1k偘@H.tm}"j=3Eh,MHG*%ձs8_>w*--!n-ri8z, 9楮;R%CmoQ4`m dJH<7bx;d,krA=-kDL?nt70e 6--h;ڳ`[ Fmec?r/ SEtUKŁNq3m:lmS<M Rsm�'ߢ)]GToag^ 8xpϞG$RQOV{8#׾rx8震~ajUe moTzxnge+OmuBf8,GF&/%'sL#[8E3 qAabvm{cfK:&> 4S\[ %ùeRc`m oza]8YOM™m[AoϷ1oZC ݖ>>=d3^Z2O,Pq|m6L iihs�VW T5d$@ ߟ dmM9_+Jjƍnr_p4qf�4Z]z"VR;8 ?أ3sJ`Av6�(77.p,metۓj}8E*8O.7!Am1LS[<zx:A;XupFSvS$T,ALyv=0 uWn:)2^ڈs[sn:@ϼc;(b̈ ; sKI-.ׂ#P]X�o�Zq 3Ա 0L>! �;�Y)eeM?3|TsI<YgK-h<o*BLJ >�.%6\n;GHY]0ǑB=7[r4*heCcIJ8E2cpWF!qt?\ƒ&_[\ 5ěU 8¦.WSoq|I9w<'~ L.ƎT �jߖMUL26Ǟv 5 8LyHΨ4SKœ8'[1r$g޼6'1|.L7+1+BuLv y;m .̟(f)N4Yc �[u9B?33"�krnxQĸmǒ39<V ߨ.4K\u.eJ$d l>}_ V j5'V̞"9!0}'fx~+:糯+{9 y.pq']=bԈYme,`u4:ETk]Gw)=]LenScq>tK%nq[SA1Xk;_2,MjXE7eUylLVx)z:HUǜr tت0�s ��0/2tUt$QRT6y�ovc/q8<#3k�ԝj٣dlx:2 5zZx҂SoN|U؛LtrΕ1H4�`bӬM9͹`B&A#;ǜgz7j~Ka:[y3$!2 ' 8S<qUQAm .&+'5?Z}3GY`(ܰw0Zq�(l9| ΉMmk؋c% K5["ĥ+ﵱ!sVX z{OQ r*G]>2JH-$JlT$a鿦)HMQH1SfU-4\v pJ�Ȟ 6*8#>2#R'@@SxxMH;�ג09g #l8cd3lT*ZDMtV>]5^]uUҍ{Fz!>|Ѩ b$?L 2brz$O«iU-is ̪(* ݘ߻ 9IZUB W2z,n/m6}@_w$RNnW.[GA_6EBDS-~!T' $9p,G#`X\*iu07tW OICO5h+^눨�)xo?==H骵QGi<;!s[Y]IÏKY7I _`Nf p<?v*6 iAj%HrLjډ7/6ָiV䀺 /b~<'U$߇ekѤyg咏Ow, 7 t1{'*�"ֶ{ԥ(YbO0POE1NDV+ܨ0-Žw,ꁎi_eTr=$E"\z:5HnAcUlstÿ|/PuDh8Z TĠ^~jI޷vvIDe*.jO4Inz*ZƧ@U0:IMAhdeOM観H\gA&H�^ۓ 7gSAᤃ B:hͤ'˦l6Zl#o3\c$,S岆۞ Smg %W\ -ZE^�v{E,B- 54?Yo` Ǿ: A2l-Ou=76Ui[I60AxÙa~:r;TX‰O}14)5. fZ8d5.&Ov ͜UQᩳ%oTcϪ;%`VO_KOC¶M=@�XۦXed$]TKI eW`|w6I�gj W(\1bM" ߿;�{Y+k/@m}'9h*Ti )$u.�|�/lS7+J}5s,Ssap{tG�g9'l҉[7؝,p $FT- :-FKQHw{n+Mϙs=uDR܎do:MIînc뷮1YO;YjY^̑}e`LY`wt)R We+oA0}EI19e2Z=/MLpT$Μͱ<oZHLn9>!wu:2YWq:[EZs-O,ByGݶ {m z&[:}�-ĕWUiDz) 6vy8b˒Z7'[TQձ>xA&RN}-T*R=qsHsAsx1la$z _}$~L4=G9ڎ7\nƘ|S/as-'Jrxj#Xw8jroQ^�͡ ɢZg*} Gmn/2+}kad~ r?455ղu$)$oamǬ\lζ@$ N N'||($fI1F.^z\͖AIIMDDb2Q)ǘA~f@$6m2rƂ3JY MET_- Zd$PϵGSAA5&_y4toklq s{ -h)R6u.]M͆ZLoK5֓RY,!1 ~X$"ĉo)RR5j{lcUKAÏIx*c$vₛyKxKav^^k?..'RPIS%,Km Qi͌+XXx[CUoMCjwߖIsqbzD v{2kzjxfk6Ҭ|;'.'ϸbgL0IY6*{a=iޚ|f[r�-ݹؑn\`ʹ9TTa1j n;_x >iCIU1JjSQ6 _۞02>ށ, #[u7x_2钢_5`�X@1g9Z2H (!�D#rrʾxkhp@XY)~XQ DFZ%Y?XފGV1ĴW+^ifJy #io \uw3-|d8媰2i 9>X�i^سjZa-$YדWA&e۫ponC L-$K4rJGBjfot\/cnTt ӓϢb,Җ, B z 6\%Qs\UV4^F�ZA#N 5x|xy藳z:)�Y-rO2*48Us\t[Wpy)CYzsK�̞Y7J3TqSk]ယbӾxdp9)Kh2ZXi3S� ĝN$I73|KQPT꩕km@mqHD�[3<*8idIX`f4+鰟/Pe5'P"ō" X.�]f1q$S_"R瓫 Գ j|$6KL9|fͷ1>5ϟ}OfN;ϻKĠ 5+ +#Z/+wgk7ۖ >R M1HK p8  4ۄp4O\Q�3si&y-1 <^]qͦ\0Tib6[V8T`} j nc4OF4%k{iFq4 ϟb}%{m PZ{= 2D{s>%4hPKԟTnuk{}>u&đ#Sv0 VŞ]٩8ߟ1Xc}*s`;{aysw|][>(P*yJu2r~8+o=#Tnc Y<'HåZ^{Ϊ y+s 2M9.q'?)GFFzyvk{@kV$؎_ڲzD>(VpzG0l6rZ"W,|t[iˈ9oD֋md٬=@o`.Y.- }ںO/ۑkoxtbm[-QcϢ1Q.]9Cv ]s辛֛a ȶ!cNg`�-p3\z?8GpmÜg3SVg^�M;f=L Jnyixk-J �p:ct8i;CQ �_{2'M8,"Ok3=Z0+mE ܙ'RWWSQpw~X=^kjאAǽ z (m?L,Q htɎ[{.I/$$h�bMcw^ 'OtFcn+y"{˧l4ʮ.7Tˀ;OdMj`ln:+LIhJ֭`oCed0)J^�]#a FQK~W %RotA⁥-cm׸mX8zēk<F]m\mS{G&_.Gʟ$2[~x5*�YlLP$ E~Sn"h/{Gis輮]Ty` )=KIGil?pV^../<|f} DATuFb\3V+m}v8 3OUH22˾'Wp:2ָ786FQ@]asƉ3i"c,ewjs*KA;i;^bX@mzٜe``\B6qQmy:H-X )iL35cNA�]�^Q8xlɁ&7[ףb }A}hO o=NTx0 ً̝]ZigT4TCY~YU4e�+uexn$8ɳpf. n]my4TM ׾ pMKM+W-c Q`r9qSח*aXZb�73).UICSm< Nwݘ Uk' #4 d 1FbCHt  7=}¸K ջ4tUGg;jBmǞ3708 FhܐocWod4%pmDtAte .@;c K`j5P?> Pwf]^N,a=mLփi"bCܫqftU((<tܾcGgMYF0poqpLg2fg mڶ˚($D`cn4:,#h&\/ǔ_39]鐁_ux[U&Ӑ={Pif`tLմz(j|{ZsaL#$a_ u׊042YH}!P꾜T\Cs|Bc"Ŋ1#ݷ2H0I)׺lxuJ3CHtU\04X#=77c=RV&34_f15`Os֠`$QuDKƐD?|+0#9Ԋ(l]H-͎{�Ys^s�EW piKU4OGZ=X##i:Ɏ<)5}dkU-N_f� :4aa)̂4d.<V UDB�a.hHdG mqdtuuZ ߅g�}r_(-o(%SϕVjEW@;0;Na{q C}W-\d-FH:Xt&A@?RQP0:FE7 sI#Ye&'3UOp>b&B\DO{o =reQ{_ =*B1A|DZ,:_m/ Whm>#as613s֪2Iklx9M 55�nLG O$km{|bI-WةxFwy~lRBakr7#µfH9R@ Zp2} &ҝQE&n�LIfE�dGw_V[�X �}F g +!![m6 G<+R@LPq2$c>aBF9iSjr\ymߖ'rI[j,Bmsa&=8 gj5%ư/s؀H4"ҫ*[qG3> 4نNSi <¤�%rtvMg�X}Ԫ\LI%&Nh2?yP`r w깁3NiV~XKimG2o3% _d-rǩOvd5AuD4zjY&c>Ы0#lOFrB⡽lϧWn*-ᅢ 2zqxA޸ "8),�!uP 4a@bXk[#c�qM\'/ԬORFP]Hrf\t_3xYe}Hqmߟm:*͕ǙRa:Wc.^w^ jerty ySŐř4c̀c[~6ͤ[ dmOI>[CfVXpl`ib;_Y&=yzj!W zcZX@7L TcƂfV@`Am|QkGI@ڶ>%G=<f"tw\p)xჭa{|*`"Nhin(dr,{qJZLjF Fw XD??m3�v1Qc0Z@Ao{[mD#H�cz[!YD�mjt݇:, L uW%U?? J+YĂL}/q勫9PϞ񃾆@etqIˢ|̩,TDjoק}N;jښ_,ĭR 7!�k߲%IǪP\Aa_k Dy$KCK#wR!ӯl"Hskn樎kŅ'_�RUx@?O=>#h8a%!rh !jl�'>d~`�%j [U|DUBT mp7#cƹЃPJ5# u }=EJW1)6 E~0�\)sϞm2#tѴLQє:%@orI t=r�mddV>@/;QZ Rp|V.մ뻄dD0NQp4cjTL? s>=57Iu>j2ZI]_ᨨېW�5rц/伶ӳSP<ΟWO\ ]YJ 3*<$7sh!jcK\滷Oʞ'jʲ\hp`$G7cP +D^Uqw ٝNM=1K4mQ�އo ֑2Yq"M}z^*6qqdb TϗNUuLKZe7|qWC:Qw 銸Rip "<M25M\bH^z}awlq"P!}NzE|4;sS=HŚZj�$n"<c-R6�0 l&fw%C!1�;n$66C;ͯ>}KVZn6�0hyOڎ,Ը<.7]M2<$7U r8DJGh`tߗT�ˀSU1b:߿0}p˵LOG]p:Hi yGmğ�?d!isZ-pf<jV �plw OFK9M@ӱ@PϦ1+li2J -o[U/fTB%)$@rv<mʊM-9tL(Τr.go*"~J-64 :wtW.c^Ѵ3r�s7_Ă{`}]x_6_O$ ˟鍮srYWiop'Z� z+3(5tlSGby<C*4PjϸVfoG4PC05''Q3.jwN\S.Ud+-E*`o\V\DX*i䙾wMII+l˧Eu[b}>PYNJT%[}?[. oJ᥼ ÙURWFao5͏^l)U o_f%TZ{X˶.NJ b+ki%Pe{pnxN7T8e9xo6M$51i"4bn-`lAN 0Odu>vIPc V1΁0۴0Cjf{!SCGΒ5슛�- /fo.<q_Wֵ=aӹ ~gSoX鷒>Mm@5䒤pWHY1~<JUv bgA{oiW-#\F{[3YU#.z*DLG<hĤ2[kˎ~r`*sT]m/".fͿCX-kr= CLsq!* /`o�}!:֮2F{YF )5<7Ѭ/X,$$ďeZ /)$Z ]s9 (5 ˽� b@}� " +�BWFq>.5ClA$"s;T.bHataGSpt^sRkʭ&[Z4{s1]hVQ8**ŜKb1}�ẔɰpPzZi 2[99#jhP NQcӴI=9|I%L~D~H3M$X)鿦+*qEZGP%1!4 9{xtMPs_.T# s[~ Kw�^9_c qIf8,ɤ M'k$QA>7"r"oQ1O,in/ E_ PD1U m'N* s-5~Gsᝰi.`9бMHFdbX~�\bTy N| ިω?V PVRNk~Iw;ASD�3˚Ax ^NA'KIS'*h44ןl+N-4ܮLĒonSĚtPpD}+ME9:t,hm_vmBns3%s[k b .ҕwwUֹz0=lnMA km(\k"#7?|D q5'muCk!#ȶnon^߻⁷9vXF\E_5VJ.,}@"}mz|ߪ7K!Lc"<&f-wBϥPgp |DLvc{͢[qsbRo\ߞ[8,[0-:;æ.E 0f -vzkL|?( ߝ=w,f6LOLq|B`Ҫuk u<b].ё<9"�<`6vN�|'Tn/nNؽ:oY.[.ىIe5CR%R?͗CP�î}_Awr6c RR@h~I^%*�s<L4i$#"QK 3 vٜH`E*N(wVFa~Xf?+n� 5sQ,#a|:iD~ 0o3ELN0=:O[ ,YEVz|*m>c+Yc$Sro0��L<-*-3<XGcG[�ۃ{n!!\s+/Ye^y5,U E(&}5e6hxX]kkx_eHrY&1t mIkvH�;7T\q&O$PEm$qqh:oWH`ͿJ&0KzɤgZnZfuܗ( x|_RT叙Rɪ Q; ZEcq&'˽R,Yǽ&,Pڍ 2 N@{ .CYv�X=ó4b2~<-k;Sua 8wֽ.x{exXT&=mzfTGQO٬O1ncӞ36()[].}Yq# BЬFq]L<Yin8#`e=A:%Q1}*OmI& `Zw¼g$9uPwfر;2^w7|;qS>fdr ٯoו6EM8 &̑c24 _4kii5~El5 ʬ0<~Vyʸ2qEK䂬El|�[FqG(XU` sK~O1 nZhTӢpc&2WK #X\Hg�_mUvf4뙜[3R^w'|-�Cmm3aa-- r5ApUZ7_i[2du "Mo!xVqtf\D"3'B ϖUE K;8,�n*b  LqJESWO-3h'Rét"N\i8e9ni+VnjF~xUhu@u/t&5OQ0Ju4dkoŁ;(^[ 6yUmeS$fcMT]#SNٷs&4Bu&f1"Nf&jyHˤ6J{k[r ha&@G=0*:F>!T?ԈMUZJOR~v>aCZaFz טo%mUU[;1v%R:cVߞk/Ҷrֆ;;gdGVb �3R�r:|]{ 8\Ϊd9K3ۗ>_,"Sd&<[?51+IUE᩸A7!jip51Se{,Ts;(LD cN֫}`/!sbzz/3f#cEqu[kϮ' 1;AC@ !*c'Iz֖KZN8겒F-X 6=IZ~}2TbGfU]rON,00�=F'h-Tfm|MVm[zاHaao_ %/ّ׾ڍ�- ш㢭)٪'6R(>Sn^m>_05O`cCtOlT,)$T,Di7)-$䱚\d W2QH[ezjHM*m4%C5@g+ IjUJlfq�>iώ\9XC}m&\e9V*E`mk ؝ r M3wx/ 4P=q̪Mg;V2׸rCfTfE[Fcr00#dsL R_7{<!? ^+tu :q7ܞA ߻peLdkB}opg^\rm40*HWX =Yk}X}O܃0r|Sӄ ]y`ua&G&eO8\RHE^`;jǬ,DUyUY T oA~܆ڱ:<ǐdz>1EB-iDZ;=]~ 2B )r?~P1 TnA�֕-7�T.׻m;` Ŭ=�({dXƞ;nIW�~ &/~"L:iXwU`�iA,uC l7#OŦ9Ԏ鎧0kx$:g9!<Xlr,>_%yRߤ˪]KYϩ`�`YU6ƏdOd(5YOKv $u@Nj t9X.~ͥł˯fr29]p:>�.6>bpg \@gsR`ϴ@;П%sړ#svLb D7@8YrEXv86p?Mk^,D瞙u6E%!uI�}9 KO}V{;R]tXl &m>V0s9Afeӡe+Si#LÇ0j``7;bDwF)%G ?FnjDzvn�"z)Rm4tr]Gn!2D'g]X n~;i ?_vtErmGs{Ecgς?uC=dȊk"{l >:Am'^=uL3*I2Rr_L>]?gӻ%kWx.⾑{j3ÙM]6iIku+Ai'<Rs|Mu$`Y謬$yr4k12FI܏_ri0A`g]/9MykO%,1tnslO.-6t s,�3Wdb ėU ~v_pWW:sOYkCa bS7Eq htDD_N1r2c}$q5)mIj�XVSRfSAhj@[=bQC L;WT| q~_z =K}a};O1ђI-%{jENk$y4n b7?4+s:8䔱Ҥ%;m�>w]mX�$=7r]W|ĒBUXsbO.QztaF/5Hc0GS?pQYctY*4 %7P<6zb & j7jZZ mYf'qWgkdi$!c3{_v2Hނ Ccub p~n!ɤjjKJM5*9@#�'#27U (ɲNmXL-ЕWk$؛Ê= N[Fl8X [sm* ZZrn Utz)3%̍nbnŅXI�>APM-$In3Z\$fs1d맒2yʩKV11_m�cLbv#-rr: rNT9d^ ,z}yah:/qeU_ S7 U~}QB%,um.q^y\hbT1`lw�oqϏ?udtTdӵ2,|4H]H$[⯨2%t/@:<� 9$':KH#yh^Vme[_-~Jᠸ8<3WfKQIM2,/{7P=@$6[ bby'|')j+L\k!b 3ߚcd8d\ˊ*-1I_%lG�vFԩ ͤi/￿jY1+�N�;5;kbH۞3nt؁_I4F,M#�wo's^&f% \e=SPCwj!#b"״4]P�&m2/sob08cZٔN<#d6$X1=qɝ1N\ =X)Y�z%6"9-Ncmv [.*%\YdOP5 rcPz=iEli Kmb }kLbLwtAt0fF~xV%e[Kf;o;*0I,Oa3mLuu*)."}6Pcb�D~)- g˚NˍŬ1'8;[DZ6.[٠1gٌJॕŅ;m \pMUBjm7 c'нA2'*Lree&Q#l�Z.O�Ynѩ +k@.y,KH c)Mz6$ Usog3H�l6ީj(3sTa7Cƅ0hF=�LsC%o,*3;«3k*c#2Elͪist1\}\f2?̦M -:6:,wm4Ae7ϝK41T7YNcj ^S?T�Ėy/hH,vߧ?s U_uki'N+) tK{ۗm�2NH=^*bS�SWp6]W5݊yۖIF Sǥh#fP@Ժ�~9Los;mH\ i<tG Jض9M T[{(m>(KMKIqx QfiJJ*Jj[1~XH�/Wl&8㚯vELuV^3Yb2ͳlZ=z.o<Kxi4P[<ƾ͵Su1<'xݧ/t4U5BVSiazyX�Lh Aӎo|yqZ9o(>4˾WH;D"I&U6)]BAږۨ�-"H4_[#I>϶a :I92:ǤBcq]vzmE.m37s̷V&G>=-7̟T38&x^ F)!p}[ 8�sVvն88Ԧ`e~:#ruSb/oQl1&$gWoln3mQZlYKksiR}2 _uFBl ci+>A`p7g3PEJ<t�cwBv=/撺 *͍DKm#ˠV[_sJԠW+D�?gEgP7}}�7y<O�0šAb!o/nxs�/ mVɍlu]W pUrfX ALpE&dϬ\LZuo<Ԝ3_OUK3DUjߠ`K$Ƨu_8fڎie$Y`Xy)eosC?״y%s1ݧO "*j-Xu�*&nsKT&Uoںֆj6bi`<$ˡ-fn<L4"OU`ȱLUˌ,2#yaíTSF4k|T" 8{ꦄ=}?bRw�s "meێZu_(h('h1NNxޖ.H-;Շf5WVZFYO)fX{aTCA~38nw#�88 #Jc{n *dKCf|j[z�9S)P)INJ2CN\13x;Σ.Ү4DUru`I3EK$ 1 yeE;nm�~oݒꇴ51Mss5ٜMS<&ssUv? [(ً!Ālf}?)ZlB%p@:]�nJ|o0r3mc1b9&YiG_Xhk ч6p IkKA>T,5|%eN�i%Bm~|/3/ 3{WP#s~@ԲĎ"] 2גyw*1ݬ8B<j0D5OqcaqO1hGy6雂4\2K'ͪ7|03ޒ/-A+/,Z7Zti;Y�r<,,UYǗG}\CK6,,9i'r-qU&mMS#<zX!�[Aޝ1r})Ja0ZJfH\0#{lv6 ? �}S=g\5Y0e#yŨi3=.a37+8R xRtǖ$SAT W4NS)94N: f 8;Ɵ^fzH:5;�p68$ 9oFSnb?h('Xh_P9m VG0Gp7."~"bH7ͽo<vT`[`LMϛQ#+>o/1{ />~~ZN` eQߩ}o &kӬF`q[Ί(|�>xƹߪىx.}|B>S_9GˌG~ɦlG+D~<"= `v�]dL~ӍƴH.jѩq*qmL[.�h[C&u\s2N$GD]3H:;c�lk9)f'B|1~�-L�p÷y|b +�<z:AmϡKj$9uKU2(nV8@ӝ>ڶ5 f?:oQfBMVfz`Uh�gh^H LbL p9kq;3gL({opYᬬyBH1UM8� _}3oW[+k52]Ջ96ObH;YRۦ Xv'6|4-nva=^~)4L30RC6}?\gam H*pE:�;U?AqLoM XGkns@cD)T|T4d5A{.6c 4\>i}MV%ThAp=!EE�@)W3<0#,us9ǺKߪ>#LqΣng.|닚CFe/Qͦʘ͹Tn<jTv;Pc󞃔Mq.@&5޼~9Ϊ*aFKXk ⶯? fM4#|U AJNYھRƙ#>6!*xc]Y'eZVv[!ΈuP �oơd$8 {[zj`m.v9G|MT0AdN߻̯Wgu2`9Hm7r/CpslńqW\K( o\6 tYGo0i顲yq~xv{XH{$Q+([`f\.|�]r`} gP3r}/|';)JY QD YXϦ$l36YSS9yBx?}Wk1b�huIm*�jD^׊~WpKVы܀uGn[Mf{^@"<X<"Z|%&[H'm<nټz6FHg~]Qx2! X߱r#g8K&ͬXﰶ9,EP;r�AA&5I\p"'f˸bz!H#ʨ5qHeaϻVVcJَM5*MNg⢵l6(4|5I|Ds'M%QgkHUa_c`#n;к?Ppg z =&Ap72\PVkDlzO~�js ˇPb466`m1 VƇ8VRZ50Z68NFpsˈzq'j[GQ`$Z@N("?iqb&w+_ 788jh04 9NJ>?]| &l]alJ|TbZ8cɏ"p H*5|0 =<չ <54I<uR*�勲�Q3HN<-T\!EG-2j"4%�bzIi6҇ݨ6&{&: ZV K; m�C}3*Lܞg<^P-iH�d4U!3:Iޔ3SCS-4@$r ^b TM;_2уQudS܉Iv56SPZuUumISG;H~[늸".w:#>RNHƢKbOǪU qiJvik$P�inZѦW\]ycy[,l0�O ^s-Q"*-sá'U �O|\NOGt_(-ϕ<uGdݗt] #%Vq' R@:4ozo˘¬t{k 8gdtV55e^Ͽ%K7\䓈|*I* .XK @9aT�j/FY].fj#dt^۟L@t$ewf|4[paZ^eSSH ,˭lfli 3N],�<gfkN؇�\n|�֊yF}75oAy:e,MdWdi*fݏq�8jmCUqo9J?{<j+ab i~g~[᚛IkTbt߼[㹪V6Bco01<{k|lFoODz2l=Sj[ #k«@1)Wg%fDgP0Lª---V l<c'5JLM@@J.2z�u&y߭Zė; }#&$ԕ5Ii>P~wıejs?ek<^XRG)s+{yi�9_~뱢 {y. 'yù z~($ݒ\\h]YU5e<.d3^qb�1���*^23L䪋PV;_eJ/]m'.:$,@�@I3h`iʍsL:O+^ k!IE \qk1qwݱM ğmTeHLQ:?°9y F`nA 0d$r�[ k} >�s`Z6&5#-Q.ySWҰ:,o»H%ZE18F:q;q܍SnFO�PxmͳLa?ł?Ri�lrT?$^3j gȃHu5[T^3pPE@0NC+Jpr6yK�hޝG 3yl8XrD*3gUK^�yo{y+Of"]"B&+b/]8�-oKk= yyT"(Pߖldꁳo$xᚍ %R@$7g/>M`cDNXN$cbK"72 /׾U& y/4:̮Z,!3Mo2$Xt)ITgRl{i׿1a -o@~PA wp\#"Nb7kg\<�̹8Ud"åu($9;LN1|.8M Ϊ@ckی'RDd5RIXs9�lNM `)⒨8A]ӛ,l@:y_ק?˖K ~qĜkM.<J;v@?~z]\#:XOD(MPiTd%#C7uA\Oxe\[,0Lkm5^\aqD{&-*, 9WW4沚K13�I$0T [N/-F<| ncӰܽpVSx˧E_j 5w;GL,Zq 矺Q8nQLodm 7k`f*ф'(5TjvH#QTHH೫k"Ko\BA'p$n~Oq=~#h,dxT҂�(&${@ ۼ(}47"JXս߯,�):.y�j0R1=|<ݚrzms r[>3csp/1 ';k� 8pY&S5J1]kPp<bODMXݤ޵/ MC,'Iqu3l6&$ck.$r)U Ȅ-ge 鑯]J�PcMLmWO8/sޭh2ʢGղZpZ^]?+75~SD[F95f Sa�ER' %\֐fj=%&+b (K= Dޕqnhw}C6(~!iOY=5ehʁ@zmL^��|5ü}qN#2=k?\u 0:hsawt8B,Zʺx,~ZVa$vټ9np´r zV2\N�]% ˛r>eʩYӸ_O\ *YÄ~Yzd\KWd5l1#q-mz+YuZ%5.Q&%}R.l>Xcz$:8z Rslf7بjja8G[� xFi3)!CO < %S-?iFK?.xFC^[eF[WT}))Pt7TcŃ @)o3ܡ|%K5\饕*t6nIbܮ�r9KU4u @ف*tQx^G"I<PRcJsR 8¹T< w\ę|sN Q#( F7?,K5ʥ3zy՝T܍${oZ?tInʯJ9)2T lAI.9~\"TAutr: ^ȨuLzq^_H)eac͇/Bn9Ǻ.&ol =Rmqz% χK[^i�$lnK2U7?! yd sQ 0:M�r ۦL I?$mjs-Qm) \[`"/4v5|kaoZVϞ5R2/_WM}`ǿd#}90,T4g p,636cq4{럺z@tLCpB歫d ب[^ݷk:!:큵 ⌅.ZR Q:<.lfr;7"T{�C늇&FDTb_"#qVqtt@$ cGg!t[df+@3mk꜇b9$HkUl:80sZ3;C*yi#uWEGeJWI"nX\Bp6?M S259'fkTg:e-k� VxPEӆ, ׬-^aMW#bҨǚ煞;B1@p',NJTU)Id-�oϟl0|dK\Ui1M _\0(iQk|M7<A~�s?1J:2mqA)'G8*Țj" rz[$oSF*!\fqU? d0 x$5�߯1A%}[dn=)3%#U#8{m˿<T[ZvJiO@ya|J[M�AeklڒţӯܘsS s}%P7.Pq$x;{om"`W{ml1)(l1G-'oQ뀽,F_z'95[w_c:TvyqqcLߕF݄]wq>Ȍ7F-pA{Ǯ*6Ǣ $3P}} !ŒaaD9�|E+C=; 5ۦ[ 0܀'Y춮~w(@7*~￯|Y8IuhE> -aAؠ<8>]V|d!^\.;Oe.{d2;\s9q&QpN_}Vԁg/(k98332 YwEpkdZ=O$ $lZ7`TrN>iEgm.šXٗ5=(McoLQii dz"[ooTgJ+gi/܅ϖ8kn޽� yE�:xyP o뽯遍(7Nl;`:FR@Ңj,7۸<s#ӌ4M1~h/EO+#nsYժ<Nqqqa{Y}~Ф�f:y*ӈ>$EQjr=0iqI\LH+_&_$Ւ*n-ܰz9e8I>{X:J?gsT6iK?e#+:,4(nw8SkIl섥m`e/PcnW߿6�03vͲdîqD .E70C#=مWDNBUBT2:./p؄4ed5}ƛMmiRX۶I�۷$WcFl{\+K�c f֙έ4i~9AfkdTm�ȸ<_졂966۹n[y#5 06 $ߚͯq_ 5Bk[N@YtI?o-wI!9߉e]*FXl:݉l;�͌VQ>gy[MH֮fNam&}:(l4_?n<տßfoW(޳-&%C�pm|>6,"͸%68SM=Ue=VOUJZH̃9 1|+IcCo_Ue^mWO3"pw낝z˶_=r:"H p)tƞ@xU~O"#ܞ9UDHiPhr :~u-|6I4J Iؓ_ICI8&\.y2\΃.s*a AFcJt5So\9ĹMÜ>}*WiD*؃[%+N ν l4UYu9thaqhї醠vAu23<?h!.eT @ -C� v}M\?QAthZ5);Oi!.Th5 �HS1fNƞ<&X ̜ӿSKxvdvUQK9CU)7b 3$gmHQUqMsݒZʪ#xtTR��DJ({~Hh>>d]철T쐤Fc|0\L Z"9_-Tcó9JT|[sq %ކ:Y";6Hame¥7CIwppUU45KXǶ*JLy|F3xbe\ f7!Q`-m�1.@<V;p׹L3#Pui Q $z Xd/ъm oJxrYd<rib#PXەb:t !cBLM}0y��de߰dyH栋`Ct& M<g^gr1YV9l;o C)]=o_8Ԥ0:,sld2q08dĉ_ȣ+C #}njy{̦ #x#y6A4:˧MvL Áq#mbU=Ŀ\R,Zݱ_5}]�xgߟ󚦚1ZIr�{~!pE&Cc39^Yf{*#i@ Uvv96&/�~7/['tɻI߿?Mxǯ|;6Ç �Z2P*\ V{B,ZgÚ% UAo`\�v6-sȃeZU^3 MȜI2t'_a/|@0#ZSD< Il@$fFbd`4j0Fb (p+O8Ֆ6I'�kqAγ`HYv4z<=ٓ%=D3Lƕ i`z.wl7nVu 3iөI~\,Kjat5ǻ` !Ti_CoצjÌ4L3P9 D|q溏sgҡWk{ÚnLHN="fwktHȡґDʶ l8jA 6Y=r<<e@dBn$ }?f^qfEq;eh�(ÒHr�D%F7g[ O�x;:Qm۞7U/3I&9n:>02ߟ5f[[C NkM.zL,/9mL^YlɖWe9 :شD [p_\dTǷ !"M[͕t"/`G1Xj};up;j;Gs"f0}kQ2p▒|+F!$lP s梛 .yqqRiaPb,]ԬcO O3( yHEq#ciQi`&Tض9>h9ԲH 5}`6we9DLؤLbB60' 2֦`&b :dȮ,7N[$-\DQYNK!Ԫtm y�E_Y ~ns:ůءպG LORGF@_='cR r�9_qs E)ײf\Jư-qoS:O_S4굮>U.zR]"#u1gekoNNq?F3e.}{.A#NS4p,n,Wd$uݒ5_I͹# Uq1*=Zr5R6[m }葯Bǂx�C-iDY5~֒HL/qMF6%{,kR0c DV\" ]l ;zaQgjm0i{SRX Nܿ!n1Pk}AFPr;A7=ѵ͛"zȶ]1n`w5Jp [n8KaEa ВmbOa@9_ehuC; T0:&a�[U\GU|N;s۱@9h퀷 8-;<щicXyeվL3CghF,F:idّ 6,W[�y&FC&&b?>ƕ/eu~]Sß_MUSU4|+d}K\pl:#Et$2 dpr*AEj f#Lrٳ5uSKL"]3�VQ¿˪o}\%Vޘf )m$4Ӧr5^Wι6ORHN\ �?v`:H8 4΢&21"ڣZZrT� jk]~0ѹi9^g 2;gɸ20aZfXI: 2'"0.lpv#�z+%(˥@|Q;ߟƛ<;[zzܫ5*hٛSXscD;3h$IpTLԔP)`w^.gғkST55vwQYU4Qm ~)Q75 D˘׬4I-1܈?Q $PMXqsLL*gjRtq7\]?eo 9d#᪍! rd!qASRYmqk޸4U!u1nE+M%SN]� s/}jҟ-/5!XX߮ OA)Z \g <ǪH L{ s~J#ytD8l15T�'u89{~"L*cLq}#8<\O5p�=;+yhZHB5>x# � sH1SR*;l=-tTk?[8Ω kcm(ao{uL}Aso _.522^U/e']sA`ܩ$DQO[<mU$7fRߖov 8˴Ti6cK~ϻ_cb! Z9c(hZjhZhvP},Gh=6C5`(,i -ny`w1<Ӯ�,$3N)n>eX%DW/s9y_.eOuK<MW`v/NIl&ʲsJsqm@ӝ7bzg�kj"i&N. Y5brMI&.UDjjfߦ2Wa65v*>#a�mDKE$IE1썽lfT'~Cߔq[ F9oH8A j,9M\saoը3s:(ƥcTlurܟ񁽘|FyܘkhnI:/[_#dxp6 bpn#,f7ou UQ7fkߧ։>ꗣS4Cwey);`ԩ<6{2rC]0`}2dy gVca+ᦴ]@ )<OI=x˾Ƌk]{k)@v"dG:SwRzL73>rHmT\, ZZY#tt%-� sJa_]<'ĒRA-Th-��,awSk/E�/*"+݅-؜5S8fP\mA�ӂx) HDcb6l(ڄTxN#}iJ?x(x74X@kԒj- ^WUUXm0Z\ y_F'vz&U"N%"ܶQ8^z c-U Ƅ;,#(YZ0[ߐ1A#(+DWpy�bUkE9i!cnw蜨xiUԖ*uZM:Vr6Fc-*h琨mmLãv}赚F�j~&h.%Ϋ[cϖ.s "q`_4q2xL O�xڥ{H�Db<fm/)TO{{~".pMCǭ�<qCfrFJm|$<<aZpk`2-i8,%_ًnݿa:/T�\G+Aĭ"t M$۝*{+$\D}G@& lc9.!swH=^3Awß�"gئZ'pK)>$'.}~bXa1Z,vDZu=Apn" :<9 .4僝@ﰨvJĊ >դi6X|M ̜jWkIpU R1T*<|6v`|ۯ*�fq&r풽 17k� /`,PX7REj v}wVmV�tvUMΕ�`#a'IkLϷ8FQ ciѶ"/E[u#jgam:~�;za[1p Ov+&$祌I|Q,9'kvm~e3,+\\nG0@T *hdsʩ =|΋(2Qu#׿i#!mNC#oVQgqe3f94r#M.APv&'3y,-1oeٗ*n≤YC}lpaDb�⧇ҿ >K>sB^ɵ?C뇨+,np=|;94"x)$Mk Rleg�{*+ky|^fj-Ң$$ ٝgT̈&G{|8FP]'^N4U%95 7&7[FM,~˓d٦]NYME<̪[H�+R ;G\8cddT2Ⱦk�\w% vDJ1K TrcP St`4CX#N}.]MLG M$^nI6ZYd꣚4.y )\n�坮9eo .]n L@GOlX K2g#ǽ"8W*WU"n1zm=x@a"a-K(nA2K6=0|6+δ <`FUķ!L%ɨ)I4 pFC56=[Q[EE,,81HlK踉CPb$FI΂4�:מ-M #1+6x EXC B-$/oDcVA؝_k~`@`=H@fg\UDlI:] d,<HUV3"2.J[(F¬4xetZ*⯕; om`|๮ @ UgټVqfFV s\@O5idjTGu=/큶VE}g]CRXiCG/ E786�H2uU~uf)QU+ ql#:Ӓ6F^{֩Iʧ9diS`-gUXe9xGgv`Av|K\ ߞI>/RUncŹ{b2q xD!YRP]h#f6ثZqrg1mbrzC 4n4C1sK ]-nT1@Xx%a|9`Qncqd7%n~ïN]<zL[i̓'L!ASk0{\yz 1/:X#[_"E6Z@҅P $j;{uvմS}m`e6Ɔm�a>#1 B.c[=_WYolu0�Y#?;s}*cy�, !Ej~ xZ5bWk܎voAK>S=P5�1I%P,{튶n0KNmC圀$e^zMy`h )lt82u%R`Rz￿o 3~װ4mżYpCK_cclXRl8?=/l�~U:3@Eň[mmZD\wNW!M[q*w;m~NuϖnC  +TR7`riG<2SU Kx7<zkA؞}7 -hhߨXu)�p0DKe3\iEy`w;u.4\ǮD%dbc[~a]Ƶ7SW:M=3϶t8`V$꺻81In(#6h:�1T-fD_u4\RUx: !Gm7~V!u<^"lyd3SKoj8<6MC�rw[<GZJ'H7a#IhKyXsf2C1H˳Z.b{>�.}'V �OS;eЛU5on }+e%'^kEfCQWXX :ͭٿwN%̓h+Z8] <` k}H+`̷{اVAcDdFeL2T詝 Ty#pI9gMwrwľ!5yECpTs#0p@C�#mnu6m9۟(~)!A%B/̏3eb䬂@5R|RʟA\@XXI�[[JE̾^\[�Z]xp9oBF{A0% koq'QX\==A4U  ۿ/9�}Zᘓhg:F:HkO27%=Bx/ǾyCK$X7� 6@xodb<Ho}, fkpH�Ǡ< mFZ5\]zZ->޸*H$[?ڶ@c;6NgM~a,b�ߗ W' ZO擘A$egNwl2$Gmsn{މIi8Xݻ(x{.!!6R6U: _./?fK(om^rKU\"}H|G.tJ_ 8R!RAVar{xv2[I_QgzjyYtHy<2L@X{_r:e 7r#ѧ3Wl֖6 v�2χ xᦡCRӖ46]fs%}\s?/pj�7z 2]Rβ%Z4lp7r%tSSTS[Ee 1,Ptޘ}pX, )봸;L. j{Gtvx%μ}^r6nAϯ_| (7v9�9EsӹhaZw@1=fnٙ0ܒ:s/KH X,ye;9K�{9�9d Y !]EDvzEG�_d_9fPLY^ܬ9_lHhv"1JdcDk<t`XM 5 r`[{XN NAf͑Zu䎫'Jhkl]#L'$W#i8~qa.*G۵M4?R27C4+0qG3(m .{7Cl0FۡхT=UVe@T3 pGsdMu<9,) H(}l3fdBsyLH%;C[PZ <,aX+#(G�Xs MqIv_/ڣy$GPkupЕp[b=}m&kQG[3J YH{~~i�6s"?5EEo d6XЋZ-9&wCOY;MUGQr1%*YQr-ejeEt+3|ȷUOIS]+Ұ&t\CFjOę٩XBE!E#?1'rF'IWYQI]*w: m9+Ug5)^O1 [.  \JYmf[)Y A{?+j'pk+G|]Lǩ^w9 E�cl-u?+8)tlde9=7ؒP�?Hd1ͪ29(Z@Gu%`w6lv "A.76$UdYu�Sw!M�r&yrxc�]ůlv:2c;=rRc=LLџEl:\lؓqP۠/j :gƬ c}b+=YWfiy'|4=:EEnpCL]HSMŦg/\&r^v>~8j5~W<N9ӄ5DiNB%}CׯVӅ};?C}!Xisce[kk<oÆx^EF+ЪB>mX�8^Ik<-1մTis $?ܔ"CMoۥb�qbfxr5}j /0wTaxI=ƙ̀&[ #QNN=TI%BA�O_!ŠA ų i^I'Xnl1]I=H@9ldSv""Ib,=|H k*@XAֹ"bDn{ xǪR K-nvD $*6ǂ6(`w�8m�$YT%5㻦n&4w}:_bs&z,xD:+k.$vuM2Aby>6jf4⛘e%73zNrZ g,ak_ppbm 6L}4/C!4b�v͠a 9HQ_K [v)sC!*R־sĕ\ w #AWw؟Qփ|J#"9_rBV  /b{~L >L7G4)�M\ qM 4K0'c7k9[*WwWjd1"{>C' U ^m>9vWb*GҾNZ�<&hAO;Eq9*tsIM mf[UMb=H|TZlAX/�g緥*R%XըAkQ嫉!UJ VO37޸Ms\&R%74{y=,1֑0>�DXhЌfGW&4nӿÇZ&Ø)WF]>RFT3C.yml*~QmkR�L.rDWEOS!D.;WtZ s�@ؐ}ܤS|*e�UH"�!ݲ6srs#8~QKe%{?A Go@>: 0drPseT.iT‡ӕ%�P&De+}v7f g_?'/� 3r[f& 71FfSѠX9HG^v'.x=碱 odB@tNǾ)v]JD#z|- {v?^xb9!K�P0(M6;vFψ�o) ql8jT阐.`ޗ<7Cflb8U`h]ӐƍcB*}IsI<=pNqkY~c٩�x-  H1"kSISf%3"Xśkƭ=@:ך>7Uߙ� )Ri(JB3bpmX>M2>�ru#R&a`}-KGrf;]/fSLsiq�f#e,N9A̕ReMI:YUO"V}JFSSge@!ӫ$[dxD'$i&<pzr:[T#%ppt2Y)p -}{ῶMSV"(O4REE+(H)� V{]|S* pPAb`Є+H;g<+<fsS%n\3A닼tU p0M|IS5b9t{(#؎�;Q<Ӄ)2ZiښAZCb~c!Ztq@j̢5>H�u7]= ر"ip{?OGRPKqm`9XƋ <<8Lz|JI-nqJDkR2d{b8 m:QP,t0zcbͷ~ؐ[J7TzlޢbEҞ-74LI?g@))O }d^P _zm<Ԃ<]&JI)X+\L+>4UVw*Mm+e.Y*.ۋjŋ=�n}2&SHHJo T)ZKMY)[#|7Pg=sqqu97f3o}1�|gKe4pؤ0)2I!Os͈% !4((Lc4~$5.CƱNČ�p1x9HhUbRIqd zgY"Ç0g�rf<i!T,N�d#AI"CS4Q&d{ $mi!q\D!g M$KU)`y9\(P38"Fy1A90*.aG$$qA aCny&A'L�sVUBKZb]o(Gإ�r5IouꦤZ3 2J Gە-iy_2%6#Ho{[o`|aaDr\ɩ"#y>ܶZK 2�2̽󪸴m(6˙4O #?/MYV@$t顯dӷ<Z@v,햛 ~m(:5V7=>q76lFg9Duu_ ;EL6.v"Ӛ`�= pnnS;MJdu# st�waxL#4i1\9#,Ni2}~}olg9- 뤦EnEe7Ib ~_d=|v\PNډX3Mm"3Du7@x7Q=O1sCL3"9!^f9|AIf7wc< ^ĒA:\l"2#K.[I"ç+0/8Z#ZIH]_<t�huiԋ$]mt{!MΛ{biF3&Ъ6;f><`cdRi2` `�[& h9RSp=&˥~dXC^J;ANŝ<�=cƨUZ2p8+x}u$L0iZZZx+Qt8O �DPq-1+j9ȭvfaRh96m'۾r!KM "X&zZܰH+BN�.n#9ĥyw��L&*ԜX0yÄ*w2M<yu�Wi>0p?+Ov**Pٹv1@a3l}8vr`u Mܐ}qk_\{ =t]<Q3/Poʨ8Lvkъ"zsq۳Phݩi{n/dwi Xq t%ΐIq`& cM㩺Ojzak 1wMJ1^}eUCOUtڤ DZtjUpc]ͪ6"5b B&k .GL2.H~GŻMgPD@rFsצmDO}*j{ ?#VXK@T�}5[cSE=oňb U0j7(9VWFB7Z&| j}l9pb�:pMM +d:U< A:,cKQN+ܴj~m_fb.oZmm}Vaa\a8AJ8 �en׾.Pq ZPxoqrʮ:N"v/=2|2ΧZ\JɻFn}oѧ�-#9siyI gW5x9}XhԂMo`ooL;Cc\W~NKq�7n]\3GMC+0|nѝаz6wcs^i�W~2Z--}+G,.J\Deu'>bR}@ʾ|h6$>.܈HXtmqmEe&iGGù^GG]o*)"=p6plKo211}> r4d"Xd)�rx))6�} Rph8Sb8aQBA,Q $̅iEQBx1R4� Risf#kh[⡥^0mh] Y` Y2;xlxf=JЄl=0b,JKrJr˛4hO$�81p)0CSTTUȴJcV=/M�T9dHN|QP=P] E�Am5ZaQQ956jF#r_dD- *B.ɲhz)xiPzJY"H97vk0 "S3<JAOU6UՎ8!2xB%%$i]gW F|s-V,iu 5V�77 J*dֆDYi� H: ̡T<,3iS,-w.*5!_ Lʟyhu�AGc}P0&I( bARQ}sMwbi0?<y%.*xJ̭bbK:Uik}TISS j49\54^0Yttb H^ڕDg9@h(Hμ%d#nt.$nU�6!JS(A.]qrl]ĺLJJts hJkurJI1:ԦfbL%M\5S44Pu5$Sj2~җӌj�t< ltCx8`hS[seQV.j{ צ(rEct"eij+jj $x$J1kx+E^WTUu)eIY �*&θ3oewf$sgᎦ1(i n}/j* ܃lT:* Zd^#W,O6* f5MI\LX3U 9OW ? őO̕* EoԆ4 Oڔ8 ַR|S Y@rAnD|R.J$Fz+rU wj0Ck~~X;i9!^!T@$QvH{=q�hX&/"b(_m}q%^m-&VN7 �7kr=I뷮׌n!#E?A=5ܣs6bZ,se[%tgpwN+j1,͹~p"SZls/> ˌtrĕ_'ũ�Ȉj xq9hD\&އ]7=2j*qgU[nuSng=U-488qx rYIA=6ϯPpBeȯ(nEXt﷦9AYPcHnPT4f, r\Zi^}tNZ9~qOZ"~^"D%� (|�-Kh�s(L�ݞvX22Fce�>3 'Uq MySa,ٯv* (GJWDsh'8bALfL<wΜ|hf, (場qULbtz]^[ X�]L@C>_ 8 IY͒U4-k_~|6$[M 3s鱴0=l=Oe,i<V]Dq~s\#).GI$3 6*߾wλLItL#Te)41:}dkn@#^Kj50gwsR#_o=Wf%Ğ;}OF[v/ 8OP)I�?oL,�X^W`'UkQ: b܉&`kfLۖQ҆uASvrnlKb՘jceyM%r9Hb[i7刧d_U6A<$)S*Mp@;{{C@i;zjة�9-&PtaCF;[DO&Y-bPH?|4׎. yx*+cP!ʖ]aŜom{¡$=P^iC{LA ?cx?$Ұ8 ]H \r4q�2sd̑~C~tkF$6> rןMKgv';=? <CN !ۑ,gM]SX ]rۙ3fkÒڶwܺ;l׍*U[YKS ӽ]d`>i؃_ b"l~&M%1W@}#N 1]kNQ]e ک+;7#[.1K+Ra �?>KZl4XJ\q=9xm%-#?ʽ3RqG=VB}[�m~p0߾T G2 hhԇڋIb:[DmbT|/Ya[RPXIX6UZ[H9VPSW=}U[SI"E$ =?<ԛ5@UIEÑQU9ʐ#at*?\Ŗ3egZ>墭@v$Sd兺K$uC!oܘ2.2)"1$Hqԩ4ZaAC:8ĝqi2qY&ZApZxbvuPE%uAq \Qt5Gӣ1 lcq�0fin{Պhh/"֕;HOp\Sfu &YgJwMHx$sO QD$P!r5l]oJUYvgQW=AhRۑL^k^W6u_%.]O,nwM6`J04=VIQ]&G2;|c <Tb[AgT5F�P_Ta?43*g$0Ny7m:);ǯg82ŢCo^c0paDVd \%PP�(b;iF5n#x%$g~TZǞGצ6/os0'$"45s^) qv?(pJ^#l*É"Ji^I -ᾪX595֣Ǚe-c써�yv.0oyqVjTyƫ#X]?p k^S[Vx¢ f3�5  o>Q|"yᖠ./{�(ZCHfjz9e+{[_L0:D'5Ii"#YN6_LU.%< nGsj ˇr@c6g`=7iD)� #,):Jz iPffmb 2- ưQVI&$P(U}>xA0QKĕ1֎ZudM'q%o-tːd9[GW#T" {{RNJYTvylI!3 >H/n[qXdfM.G#4Z,*�sm{aɻ- _"M~qVNe.Qðhj�PQrvQN)kt&sjq7y[4AOL+) ̬7۶bAflfc>-FH2 %dֶ߮$OӢ$._lG \ ,-nϾ�<~t/ɿ92\@XHb, ܇>A$(h/ }"36x jQ~j_/Jh@1ׅpI4܂`܏_k`. .~`\yL<dUkuW5e<9cjTIxJrZ{]&6]8&/p8(y�,o.-3xnQ s $Ef)AHಝ2L\U�L-7%Yhߠ}P$G>xPܚd%RRٺ|vHL&}U%c# gxLeP%5{16+LVh<EŸSkHA҃L48fgQ�`A"5m\Nnjg _!z_LtJ8!܋\S!l.v_nsW�Ta'o%+ʺլ ~J:|v09sJp�b~kjH"`$S�ݰv8A\�㰋$(2s{�mw9;1މ-_�k*&2<O$oNޘ Xxt/h8n^R9rm郹YXAZweHUV=EFeXIFq6+sSr$lm.`K�pX^4lAJp~ȱub$턪q'.zi'ct6Cj}=kop{"wmG v^JΧ]Jߗo+U;ѨxӇTJࢽ;[+23ߔ˾0[Pw{XZVRo1Ē$y"fٿ)? I%!逽HU?v,O 㗚]‹1V mw�`OQ[1_S|AfrJnnm偖]^N\yKzo  @`sNg2/ҵldssru_N6J@-b4MY' gYK`eݎ ͜):xcI<tGG WA-k(AkX[1qfsrlo</?rڊ,ڇ.-P)�0pÜ`{pK"ދ8'-,< +,^6l@p[m atvC,LC* G`9<6O ,JX3X6�) \\lvÔ(ŰE{9u:-M 4r@S $A)-" w I#\�~ôsyBc>o-t SL|We<Iש؅F2N#bټ3Ql�E{3|G a$B@v^aI MBߡcv/mϋ ݛ- 6AGx|CU^Q;H_-!Tx &5(q*ehuDHW@u\Q[kp&Rǥ'e>_4UjO&[<TԀq̆ZB|#C,#}.lF،DrR<$|ψ2ܲQ2Flݱ$8lxu'3lҡj3";_lآ G�pp<[z,+a+!0O "ןE k#gj:j.ԯ}|sFvR>irص R3X5]49ϴF$/3iD9uGf/LexBMl3Gɪ2j*Rg1uZbq& r~o2( $I0�z p^@@׸@i*2[3%`V큵nzp]FuyԲ>uI:s'm`hyG&/.hɝ¹>-Ep1ڪ,7)hdV!��$!5& C"˫:a�ywĴ�v5 dRK:aTy_M(߮9�PKMSj?*paI�^sb~xޥՠx\`tU 8c5jX-@}lqG sdl|4k&^ZK@_q!t!01+'[A] s߽p5!d|ΥE$q3 H]]qntXp4PUC/S tˉNhMҙpFGm|ߪ3j1@ewXȼh n~^0ΥiryJEα@NJ$h%U敦J�ccعi}Y`Ӿ3G O*me" okj-a%d2<2QP:asQfU鵀xDcSUeńn:lG1T4w즌눲.@aPx`df1_UaIEqqpts Ɗ~ IO_P(siZz3�.p悷8sGLcZe~~c't3=]EA2xS5#%0ww_#R*Pyi)ݙ<uVj/mIԖ F޸ꅱ;Sg%&I-j`<7|>;s%& [РM3=n@kr #Zm*0Z LXhj2^8*MEFh}D6�[IP{*)9X.uZ0[NAv-FǨ{l~X+ mJRkqb* ov ue2HkEKmHk-wo9!17'Vo϶ EuGpY�<I"Ȇ:"=`6u&5A<' y�-%-68s@Q'93GiYBns"{�9ٵo}z 1|BV< Br}pޯ]K1l[]aX z2ht-L[ 7-AY_g�Ob e2RΑЎ;իeS%Z�n@")XkP蹤9:őo])IT^d_CqW@)F/s]+F]Oy21d d(reth~9 Z% /lJnAo^ś\kLin;j� 忩% >Kr݉DIљ@}-to ӡP'U77<-I6iamưtVK oNXul@ 0/[\&|ȶ_LMmSI3C9{\ZLT�:ܮ7G%D!s&Fc=~a"Gsז95ncȨ1"~ ;@!GӨ-3vY/t_8Ya1aNۨhMzgqxJ5[SU2ƒ!$y/S-=R,7bH)YUAߗSa9zR'lp�gIrwUlQ|,⚈궦dF 9oo,("9gjz˫rk@znQM,^ ïOG&ӷ<bZôytm5ZF4vM�fdSro9へ40W})|7g Ƶ [nmz+*"W 7ءZTe˽aZ\%G e2ucl?׮@'>Ni|$"5 Sqc5w]8 ]5}DrMS!fqB�t fNᖼS_IPR $| X~sH8+8Ø[ٍMm]4enAO#a 8GvSBgwpZQi]9EktEGFkմfp][o P'μ#4uNQ-c!Ŝ@$ # *[0Ihil!�#w^4Ds�P+sbA␋|y_.@2G uMlj!IIa$ZGIpmƛ@JY"H.<ᄆ~NZ*OEHgUaQ T2}@rX+z@eU茶(d#[7vißE23^auDbtrzr狹uzi}Z +s%i'UwO1bx ­V4�52j̦I Ie7ܸ:idFdTY Ea5J 8nbbhj Ͳ_nX[Z n/I|ʝ(jb nD$ãM EĹgIYJ@ԺUW=N*MCg9j (Z<ˇ0�I'ChlxrWuBD\@SeTTYVcM,L^yA<d7�=pC3Ζ+/{o]u4lSu%qQɖIO4Yv`bz$l.0|ZŚgk<`>OA cq$q˂3| 9~[QL'�-$'TcwzkZuc=,DpvаyHnY � -K&he2-:+ 14b"V.[O%Mh6bdHG݄"aKL< 2t{ v2p^׉ےe$JxJ17WŅ9+\A'.)e)s3%Fr8WXU$^' C_T敁ْhґ-ZC~M8LMqCЅ*`Ŧ# a/TL#UֽM"cO˧鴺\,J"L0r~JRf]0^(Zv$A|;x^Q~X-F4,cqZI(xv8YN;`Elujts-.exFy\SR + #Xq%\#QЩXY\QG21JX8e�P𥬯+TUPŧR˷ {C]Zx#<0/=95U5}XpR�$ۋMO/NOu5raV~ۦ Ф\,,HBc Mmkݐ>{q}REb/{y"NFMG.v;sؽ(Ņ)�05lEGFbc'=}? gիPI&8fwu3EI]'!%9E�<T`_.Ӎx $7k Oecsr=O/O! g.ݚJF s!"�Sgp-w{nc6P4AHtXZ`u塤@)w`'D߬CZ5-cpN5D7 ^ FTo.q Тi@[?TiUIR0o~+u�-at q;㚂nAԍ9~XҨ[/=iz6͊7uc gϖ}U2&ߨZR$@A?|.�d/;("ے<9?X3ȃKL_%99 X&鋊tjq}8gJWP(BU}ysm$9F_-"ʲ;>[]B\v½j%XDXAӊ8~xD9X~w!ܤpx94 }Io|XC. n8s"a~zVe "K" ~bߖ $L7wt4dO8=|*$PM-ۿÈ թb82g?(n4{.>qB w�L-� q"zjA}VF_Fv]\`T�Uc# طڇ~7KhNw#,grگrGa=u/d+$>~/zЎ؅6}0_!"WZ2` ѯG%8s2z*I91[6,a"RW[l#t :QKQ3AXV=Mͽ|4%rYv%=.<6^KN>IB6[8-�:SjfMvrc^Jߠye x$EcRAa{6to+ t7I 2hrVZ$܃{߾7S~K6DXrn4*R$՞E#>K2LbLu3 ,fY"E_$~�>8m"�2\ U+T'b"5.aD '$dէEQ 5&Nɷ`_uKxj #o#{ };j^ m̦ dwY".%+XH$斫9" j$hҧO_L^BO T;(4%1f`wUt9k'lVCIeJ�!ܱ/1"3De.XbDbC}IQM9&u<UbUĎC՗e犜1󼯫 %2oE|'C%: �8xXY9CGRG.k]"ˠOާ눓G##T2RVaWMX4I{dR85Nk^Cwa6>j*vc# CeݱͰr(L3$%&�FRg虨(b+Tb9[:6-q>2B*_8ДK=lKì#a5]jl’hϑ02cgT?A>S[Z#2hF;vbr֪Qe6$t۞K,IxMr*)V̑`]n@3-jl3+MPMߦ'ɀPKH3<ށ2bӶa0a}=76ssZ@/ڛ? Yվ$TmT`e22(?Na{s2Eͣ&lIQȸ: *09iBOݏ8BX9ط>ff%ig(!~|b#Ap]ܳLYUGeULb7?9'%PZ�6o+JlnW|zt u;.^rgy)LmAv2$Gn& btѲUjSR'ȣ\Tl!EjoQu"< 70[T{4@M2�[('Jx,|G t=D M }櫒A6ҴwYUAn[iZegUa�;Phz}@Pƻ+:l}Q#4bE+<i`5N5s~c?̋aPp] *Ȓk{MKw|ri)i#H@P{mmpĴ4A|#R3U|STt59N߾X jc~ۺ%Na33f-s߿:@&TUi` nhܬ#K-m9bl :/ \T)bž", Cn`4_VUҵ$u   �=әp~ l3%eJZyUvъM4#$Ɗ%ڻzuNQ)2Zs̝UE@t-{-`=#Nju_f~`&y@6A�V h n>JIƚ J; J{8TA0Ihkn \йd;_U|kiڣLK1Bi.G�x*N�ϙ; @?L& Q..bUۗ3$!UK G\:b<Hl(VU*M{lm.-g t?[D"i[[8k)_+>m icX'z}HUo˭ޞjT7r1Pp9[ז.>1i3O\h$Hce]ݽ`?LVk Zfz s>WbX#M8(,rH qCwC ;܅ U-Ȓ,Z y[ ^Qp$Fmkj;0brmr>��r yzϮ]fo郲3KVL�s._CdP|O7R.}9|2 NRcPؙ#bgL'a~c]Η;é/A@]X!0&uu7I$.&wX5) �rJb ȸޯ 'L s%l 8 �xe7e^d8�D2(AKï;�8�7qKRin"n=波JfZyWIm罰[Mځ?jSP*#r} &�82qh\-&�W)uuӼ*e*�| rށSl xxfq\C;4pMliu cL8s}*s_<<X:n_v(X ˫xodv@ԓsىJ;k \DhIF,//{R6&cw Ov}&XV&X^{4C�-&#cv=幷ِ^\¸( $*۟`&ߤm>wX<MP'ZPJ%Ư@}gWYէ:-Uzpɚ9i&eVB=%\-Z !�V4,R1JIe`e`C:pZI$wG.[' j*ۇ)jUO&42 Ǘ(sJKWJ.ҬA9&Tb7 TQx[pnu�} p-sՖkjv)" 0 .],pw/vY ͏"1w]!P%q )ih5AԎ;NMܣQֵk, Xq y >SUaV\<"_u*Z*Z%/7;|C <vqѼTr6XS펎*Z-Zg%KMMYX#FI-9_`dI<񖴭K#X0Z j}l;(%>dMXUK/; \ bʶi#4drQ.3:4Pub:+4tsʸ<+u[x7la`T|ds-Iպk=}0Z\2"/*ɥi#{g/xd~丗e{Y}Ң6i:E==N"Nz(`|nh<Bt2.]$1DQ߾%,tnDnzOθj jJJZseij@O\sӟ5]f[#ܦU4Aij'd2?LUNJ KCfΪ3_?űۤ^񅣸LɕJ)47 ߟ 憑7UgMIVqG˗LYE2$"D1Q$ ۶ߎ_ZN[+442YˍvN )*TM!s$.G>L.Pb%hDߧ{Z۪ 8ޕ3 'euf.FܱVq gA-2<\-"TYVo0aݱV"fhbe8TN47In8 | 環r偪Fhʡ׷ MN,4S�- WPbDU#H6-{~0̐5 _wv7kCR>_P/G-&w.8/O4 5�EէCcpi+\I1oSǜeU4zkWp��4]DnF{6iɩ4�l۶/L5\p8K]}-Fo"2+d FץLxÚh~S]TCUR 爪*mᄍϧ(ipNfO=2DW@ݶqS5 Il}TujƖQq.Ifrso(Ll  0lQ)4%i*[i1[O3ns 1ޫ)�#+,ǘ?cIU,0<ށ9\t E9:IXma$J\[$ߢ [%4uPksO|Y 1%/ih*AK~틵pܕv�lo˲TH.iU}dVviԧ(7_m0[\~v`UD {( 5mm@~X^- HuǪ'G{.HU"�G0J �̩`CMKݣY:Q|M'Uiq A$)Y$`ockq�ym 2Z:|tA&@y_~XLQ<f\'H0а$flg�"Rysx(\,=wz4[ESA.9ۂ 83Kk H7;m<bABZA$+md{_ihmfAVu03 R<%MaCegw棝*m)T ;ZPAZ1[\ Yŏz%oV�XHpD}X9 ?1BeIH|CvxI 5ё] T #B�]oZ50vv7wDqLu(W~C ݆RoI9f#F^M‚7犱S8(Fx*4v|2_8J=QM*Jw8O�Sg\^C$m:F*㏠AsjB H@=:m$sEN0p!5pSKU%$ߖקa<?+2̛*E ӎL}sɃL8FQSsn{ap!k8T7}ӛm,!Uc2*|˘�ja6xYtSS:Bw;낰F\}UGL�\D'PH wP6K )( bɢ\٣S b\w'w5dܟ96HWy=IIJU×3Y�,1UpTudbHB~=7=!ֻTSdrI$0׹mpsYX67Jx#SI ( #鼓'z,5G;el U;NbywƃiIj?s±gNLřKulR@iU45WV5efw &m buWsF<ª%3JNi/͈msi՜ H2jW UIr6&VsDi4R"Яf�aWgy# $NM|?M HNA˷l@FuG@&z@�t\ E0#wRǠMm�A)`] 7E+m'*<M~x^0{Y,uG3*X$m|1L{<4eotՙdruf_q rubb-dkdz K$qv߾% -&u̔Efy!nčǮ:Ax!ngQ*#GG.CLl(3UPT+ �%k]ǽT�yV}Bē$9 S)Si̖iŗmؓm/%Q yO<DI2*˱V9𭳱)Y-}eD55L<5k_P8YGO$-4T�ō<U8mtgx飕1:Tres|Fl#J֭EBm~ͣDMAYgԔ4P}GщĞ�E٩кtThK8F_5(R=7ĸ7uʢ('G&|4OvJ4S, lq76uѕe5V U2ixkxIDy>1މa;NL2I^V�RP8%3yX0_4AhsHvB}343AhF[KZ<F{�K,DMf.SkWs1SP*I�!y&dMƝ9ef:ubE7o\ʯI: %Shx`j˫N:`rn4+<T#H9�{ぜSh :w|"y,Ke=0\A%]K˗ҒIaa:?p|iSWS }Dt6vuOU ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2016/�������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0020562�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2016/05/����������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0021006�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2016/05/stickers-150x150.jpg��������������������������������0000664�0000000�0000000�00000017561�14502137606�0024272�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 82 �C�    !'"#%%%),($+!$%$�C   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$���"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�>+hZ>{o<mAMy{�0?5{cym- J=i-!{p'$ⵍ8slto�kh�77n. +v WZFxx<nUЩEz?o2ˤiz %/SgwjXYֶA8M2Y9{IDu>Sүl|{c檲Ά9�^MA~¤/!oS|:3W azJ[NT◄ƷN=v`Y#?E]̧+!KK8$6߻n F GKw1\ٌת5h~.>u ;;'l<GOsMX o\p,{: u%M ox}cOȋdg>M# ~y*.tI7]08=q6%n *I8�W@𴒋7 96B|9jr( C{G>+cX]~Q؈\}[KG;Or93ɤ|1듫֒>yUOCWZvzث)T230c8=2==+XĒadorkR}:HEОՅG}))JʰD9<))D�U=}1 ̬!lp{~4W�&䓎jU;LΜc8AyY<9;)#|� s4�ɢ\Fnwr#HSd�JP3&Y5l=ޙ ;0�PZEֶ̺,wuh !|UfHll{E-K-��rN)~9|G+h($ն o]+y5 ^ri0˙m5Hm C{P2 7R.Z[Fc:6w 1ϭyilg$ۯ�Hϸ}1Z~jr3^6y`F_ sU':ί\+)<oƽ}P=]:t?pWvѮv̪ڮ nJH\<;ǨZn?y;?ZR23o'|Ck bSCgO.xB1�G u`ewYfldc �pOrͪP%v�֦3M{qipj]B"`tWtsV%kat�xs|E4d1^rڐʸw⮽Hevb(%;DׁMV/.>яYZ)v};xYFH8y<Yٙ$kyq}V&)& +t@bNP_-Q.5 b,<Ȥ9V && :7u? yn MNA V隬^ͿR|3T8e}?T?^D⤴*qz\4RX,v"bW>azη)omЀ/_J*e#+zWcҌЍgXʡG@O121lw 9auI1C۾jJ"FCOOŠv/3<?kzLIRfLwi$y5�ЀqmyKqma`ZӮ"wdǻ8`k@bY)�ϩ�'?x{3 ji1Mcj/ZDQg >TPI �p (uVm�ǿ^2-ĺs[II/Ѽ5Z}-YF~x'kHFZ*�L}i ?L^Ӑ,#_Onx՞S!!Qyw ڍȃbD�:)#J~2�i/mV\]8%H>s^,Cq e`} b :+als)KX u=jO<cаz^`Oed,OPq}kYn| ⵍak2 �H?+. U"jZdpJg5TBG|<Gcmb$:~vz5. w\ǖYa~ۛu-`Vi2t2z\U9<miynd�R@8fP2=Fj]HpVk6wIU=x嶑%P_DR͌'KEhpEmIYm\QjLJg+2wp195MZOٮ$l];7?z:aYз+ׄoLwK<[^I<H ={ݽş4x牃rS 6"ևM zqJ`+"svpڒ9C"h[yI-ё,<yM;dUtc7 #۱cۙd(Dԏz(hI�=EsS>EծO݌<tȥk*K<ePsNEyW& !/k7e*�+ YJۚ!s(h4+U`ZVkn&?#<T,%aeY1ޛ6Qjr_=- rćvyŸjohP:A@O;zzo^D:FA$%[  Knai!MGWǃ=iW]HuM6-qėk�n%=k>qef8=~Ț!׬ූ-:KTլi ÖU=+J &ac`RoMOLAmV"#Z//:{הo0Y[Y}Kk'�J-u&nXGO+๴kuk|łDrwZP'Mr|ZozT}9 (zk⻖MJF<匕Һ>//,uur|$0qJ<-Zi:KFcq T%<gZf9(|:@E` \pr@8h3ӑ쬘QXdrpїᯌbMiEˈg*�u@>t[E NQƓ<[J`FeIkӾ IKeI> N9 bt?: _P 8?kX4T`*Q9ZwfGV) <k P%g@?uZd#4崷 .:a�9Rw:RLOPWҊ8Q1C!Úa?WxC3 q6gɴBڻ%;1?|+ ?ZfR-U\3+'[ w<m$%ų (x {9$]+3ܳZԏU{Gn"eD2vcp+r N{HnlJgUfUijH7kR0F�z_ jd7! >i-AqZ-r/:p~U�adt=+L}Zmt*_k=7N<2{5գPAdކ&nۑyzƷaZF @[>R[W>$YZ |�XplMcc__iba7:שj 2 t9.l0w>Vo/7R]Ez0U_xVHTK`&^j7tPY8rW&x,f�zk𾭪iMK$xOU巈"&imgX  &AUEMY7MKE[*u}@[]D?.ׄ$ETīuOtQѧ%8ymeC.Γźahoj79ebz� x5M6KQ`X7<C;�O5jH$ғ[iG^H} .#Y,LDeNOXWe! B;5OeLy蛖؞& jGn-4$ u@3IqjXϒ_<*Օ.qz:Lt/OnIՉ]JrsL4{mFLe~5UԲEME7,1=^/]7�oY'mNUv�xqǜLڤBOC׏ֻ*Ox}ye"RDw5;֢E# ",N+[m'R{,,-]yBY7sy.4=vE{C/or;s-KBӯ.e5x#Ḍe?²ᾕsMŤRM#On-=|R�f_Z>cOcV?s>xN+A$dvmB=y{V^�StEM:=s�ʰaUGvoPtг-{}ClZW}\oa:u}TDFuv5s&?4v׼;yc@0EpPqt7ٺ[8GZ"knFe+Փtw+14ru wJo"O-kkw*f;|N� ԥK )fڠB" 5(*DAHfZE` ={uK9)MIJGΙ_kZ[jW-0'ciFvD!NỞR(Yw9C5K�fnt?X a%ƛ-d7mRb ~ZmYMey)�=jam*;fvܸ:U)h+ \\Om,x"8w*z:w4iw+$˪G#KaAVkz!7o1CJ{k|I )#bcEAROX*^n+[DZ/\j53{qXiPHVs"V"}+%╼f9�_Ȯur+$?F"YW<nF\0Z %uhyMvkgтњJ3YbxVN֮u Ydc&?kM{mnI51} I|HHn3GĢLN9^ NR.`1ȣ�;p۝#[V#u;gv71hr�'<{VF8S6Q}|ͼ9MC­[^%Ds9`�~C[JTF} � +סnKEmneyHeӕ%YyUW9 W}zWKit]F$Ȳ(a`ŝBT($��¬t V(q5_i:+}- m!9 WG)'`g'IfK۸erM)w/&@:nܟj8$ yIBxֺZ8R<ҕIR4eKK-,cb F4V3!+]Wk5&dWz#^p ǵCD]FD͔.Lv W[EZD-bI.%f\8ETY7$ p8gsFȒHg!`kVQNJOti\ѕ!p+8( >Q ~ZX�؊FсJZZ4PIBkhnay I,ڀ,QEJJ|]4So-֯xmcvon OeHZP;.m_FޣZ|tRwDS^[3OrLHY.G\zԸ03[j#y-4+%XcW+6GCWt^oJl!/.Ի^qBTA"Zw3yIш Y ݽbK  #=51^,̾$ZE@Hp?#Hy"~"׆eKԮ by<JXէf6,R.׉&i;hTd y%iOG'y`)|Q=n]CXrc`zwk{+iuXDz6P?r)nsN1KgC4sIJ Z}q�]:}S39ӥ Ǔ r5V)2~4.D(ҝK,:pR#R1~.{ jG˽zfm:5>je,jᰞěUg4>i� Oº=wUm'HK"?jKy 5އkaK ;ZƞkɪlJ<ϴˏf+ L7XNjsJM;\^x;;OSS>یy>^}SNq V~'vj:]vZqZu}3A o= ~<7wne{Ifcu9Vpy#PKTtmGgx=*q\^|iH 9*(!˲QLy{C61ϧ5Gim<A}GM'1xO0+e<O#Ėl[&"څ4M QJF\*9m*8tA,al^e|iNٶ&5N YyJ@_.~shAWbٰO[gZ4Hš2岾>ryV|<בחb`t[;xV#I.c ޹ɘIkQ\>PEZ}%޴ߛ;$�ձ h2[�TyѢ][QLg [?\}>QDz6<^KxYKXU^ < ]%ޙiŽvУP sڊ+:ZлwgonP4.0 %x:aN76$�j(hͣLEHª(=ٙ(r}袀9Cis"S}hE�����������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2016/05/stickers-300x225.jpg��������������������������������0000664�0000000�0000000�00000043152�14502137606�0024265�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 82 �C�    !'"#%%%),($+!$%$�C   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$��,"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?� ? S\p)j'ۚ>Em nd�;Wo.~Π{y-zvdpϕEFm?+r2^L-kpY/uL :Ly8>砭AeZiu=kAe#߇ZƵnȋO�{\TI<,;1$\(X[|eMh֢!K7- 5-oQ%o睉+<07�{ 1HɳZšΣFY N=SFYYx*G"Oaif(n,/o"{| }?ƳIkx`K4DZşZdŵ~u:o\xB_7e恩Iit&1+xFX|[�ñG#H;}E|wpJ&%ũݨhh@�WEGZI-S7N⼯¾&ƦQ 74G/qT2 =};bUʾy!{{S/ΧB6F{)<M&[9Ӊ=;xB|3-lԃ=HjlSdw<7$PkξSX)!jyDzEkoidggW\s-ӟZBs]w/XY'GPO)tGePMlqn!-l..LQ1B(''z63O)$nC~X˪ѷnz2 8Һ_xO IJ%Դ} 4OHp8hu ]Zj71yVE1vhꏤ;Rh<nVL*S[ҹ?.3;;Z{k.|?/zON}QҺQaNc~<fE\w>EtP+:ӹH19Hǐ.A<-Cv֒xPږLd3>SC,+JcF:stL8!PWqGǐ14ۘ]\,Αr8 * jkW-l%1*3IYMqjbh2N  WזRA *A$s4DL3sH #[';棖Vv2~?Q `HU"c'lL6BXW@<VR ycVީ +I-$(Mg RL6ֈhwTiw % `: חxSƥ h%`T] jYbK^)4r)V]}O|CgFA-j巈\{D4 U=+c>:ʼnB| {*/K-|<uԬ:}k;xF3'5\ZY$ãT׳O3XFH+7s5%&Id9N B>U`s]=2j[&kd>TLs*` izu-&</5xJmKKE5bHnrXlz =JYx{sft?ZЧToxDK|G^�)5鑼DZ)<C5[~K[mAB9lzTlQ'<o�_Y.:?$Z7HI;dAHz]φcCs4} �AkH3kcC*R9^>ZxS7pWMŞj�kg6qa(wFqKܕW>}ҵ\X\O;x7Is ^tkI O uWZj6co%gݳ*ݯ<[=52Wiԭ{xˌO C#(L𶕦M3yZ)`qk*Ǐ/VVɕ_L}~42̎޸E k~$m"X>rgR1Fcqې8`{#xQ_)yhz޲dkоW͡�iǰh[9ر&50krׯh_t-꒵생?Nx~2Tqo�?Kإ9_|5(]NDW II#~1^\f=&m�I>fU|Rmojٛ@`=A*2zR=+T`c,zK-㸁"V`vٓur7@\UoyL{T.NzK-а @4mu9Q\seE91|ɰCM(~Q) k,�d98'Cwlt,1֘&fʀ+(VU a]/*9|Xt,C iF<8')vĜ!ܾ蠹RF#I0Z$\3rP(2Hٮ6ƈ?fQ1<,?r?"//'cj."9 [x hZl͘BGQH>^;P3Q:' uݨY{5y< jҥ91gڳ<9<7Cv sUćֽeO\_Ht&2G;x]]'؃c%"V=+>&Xx{]kSm.TSBwoWOOZEe>-6h:3'*_,o 6qWn1Cߵtdj>{cxfo7�JvQn`WxVh;}彵c"pU/GA<:Y)2H 5R[:?_WZ$;xRӵOK!mBҒPk;x[q嫞Rz֡-ธBHp J̛^MVq�yBHǶk?L5M\H3\E}m60EvY >hr#<VFbH$oڸ(xTvCsu$wq |1= $_+MwUmoV1۶p+MI4r-6BL&=t?ҕH\;;�%!N+Br Uݤ7\FC*u# ywR-50 C] kR#y~8Գ;TBM8JҼ;"%J>i#s~'?â@g~y_u4s| uzO;xE1 5ϋ.;ҎN#+5j ~y$9N~ҳߟzjktb@yvoֹV9QsVIF9V $!Ά?Əm Ll8J.-泙'E<5ok[[{j */cW@]Zj_ 7O>0f-쟻^,)<M`޾nմ jkr sDr:0E{g<V%J�ѫ:iˡF t[Ƈ�D~daS"7Heƅ͠g�]n ҨgW#b#2I?!<ǑۡF\Qu;&VVˏ⪐[YUQwm9lԗ,wD~lS y PRВ}l9 UW)'DrqןKhby\؏Jhe.G�uŠo3'I/,Q�y'։Y[j0a21\}i$FF3L\>א+d/qH1'j#kr1w- 2&U0Ȏ ㍧"ْ&Hw t2[1b9>! cz>J*yt6߈n@>Z6Wm? ~I"S0U>3?Km~z#嵡#@mw̶t;%=? gQOb ?W>}nHx\0E}'Pkt Sak:uf|<L%:|9gÊ!aqi.=+oަl6UoZ)hװzVɩ+5NmVꚅ>sUOW ۰Y_. <ɳ1tDlǦ[ܵm>$2x{O-=։zAh7bnVg8Cprݰ *GKkwUie((<:kԯoc+;{ Ǖ`>rnƆWO+O5BBd >E!'+瀵kk3 �}ǭzwhiY&mJq0~�AE_ j ldgR0j|,_ũ3Q:ּc� K<J0Ƽ]CtZZ-GҞr{eEw+Ai4/ß7P識-Ȧ;;5wp\۸2 e(8hͱ6wj, xO`;U(Sϙ#Q*xKX}x$8G?qS,Z>6Opֆl@AOm@�g<sjZ{fhOS"ZDlʄ֨|$%omCl"'&G\ٹX_D*Z>vq4~U>]秵njW3MZ9~o7C˨4m0ZFIǾsJR;.T뷧Þ(;o*%o7i??R׮%o/ܥw cOռao3W|Ы|\~_NZ%TuΧ7989uxKSxoW"JE zCy[18 ^Eӥ\Uz4Ϩl/am"pJ-?;c<M޸/<�ؗrb)N`,~�wZ46*r֥uTY} !"7k~9Lր+5]`>ئjtEbs^{բ�R98&-ѝEky2'8(2vTu2 X.$i8kP3`,li-A.G$n2<jА4i$�[hf"8dɃ[e2Cz:A#), J",vc+>¡Z`*&!#G4n7WŵCˎTXFAPFqy'@Kg5f;V5B]1昝ǎk~=I !xH|[xŋF"'9 n-E\h#(]`z{]Uׇ X7O`y�rq^׺+VyWOp%9Z>aq]2mzیWeu-S9G 2Ϧu& sKWtSS{ |٫iW:&=zdv"".`; Es�~VI7ѮӸq >9r3I+y'iIswbYAg8Td^ԥ&A객^W3纜ceQ"T<ÿ 5d,4vt#zuk�3@/.ds<CJӃťo [#2]8S~=?|0ˀAn^!kognpH-8n)Aq.m˶rynmMK }AJY,UiʎqҽSn':煵+}OOU�iVW<7c7gB~h �Zh:O )XE�g{k IYF. xd`i;MXiGAĚ5Ɲp$_l4&k+=?_GKQxE0$T a𙸶_p'*r|ľ+vivu u@uoQ]PYxàIun/-/qz O+౐J%%Hss1'cVՁ;HJ]q ^}i82Au~'[G ;wr5cž:×L:L]̠Sh#> Wqr\Gg+'=D^X GQVbsdOH?qSZ:- dW0BqhzmTmh4_)@O�zRx[T[gq'�dנhżhܜ/vV<aǩ�BHP)"ySثw9 ;QU"ۨ ׼x)m]} $oƼ/\$HUb4"h{] 1H)S)?Ҝy+3ݵ4j6P<hϿsvW@RyeكgĶwr JBϭyաJe(`u`NsU%c?!FAQ||Ub%*yl' -=ANǩ$ ^?d*e9!#j-0( ll[`H-O,ya## ҝ 9BC-o$vEۜPy+$b6u;Qost=<ذ۱oJQ,Sq( :෋Hk i'&4G=ARZHRbxsZ0'׈ʛ&Oy>XrE0IVj:6[YGʿSWem^fk=Mt|=UiUFRGuVȥ>ĞRծ$c$z)=t�3OҐ;�1J<_X!x%Ռ-٢kdSI<!,w V=�u1ԟZkz7^K9"jHݞeĿxr~ڴD۹hW#bY8� S7z]*\&ǮEv?VV �WmcQ[]q.Z01bpК&jPznt-%q( wZ<Eh߀N=:SA[u-C]LҖo--<Sox![s?<:Ҥ.i,5m 0S[<UZpi?V Q R4#` ?ֳ* f;On!`/F:��Ϗ�_]?|={2n |Iy6sehFGOK ;${7ڽXUvy |R/,֕q&n�Iӱ:QV=Pˡ7-+}PdazY^{\ m_C޽N,JfHC^mrA61W )-HqЗ_⫨f+,v.3zԺ_ԭԗvQ<LF?�3h0z;-hH27u`zӁެ7Ezltܫ8z)s/st=7E0q I^yl`' w׼y{A\w3ȥҸ( #sWne"3>åZ#ǃq]J}h#˵vX^fV48Sb*8�{xɯs" ȗsj|>4kv>iXlz7I0&0=+Ӽ) 6Zn&Xc^K C*".㎼VrFMN8̿P4:}kIūltb��sU-j$m&PEddZx|[Cg *I%Bs5iO>H']ʱivpR <Eb,q̹#Ȯ<AhVj38nEHN^o?.CߜLI#t CI +ݰe<2:|+д]r98ʃ?<{akr>o#+|E][ܕz1ޝm&y|lw> 껢a@r1 ̄&IG.rY%Xѵ+oL0?U7.4o uu˷�NF=wtWVO7AvI;;|AUn�n+6խ=BFƓZ9ZgE"Oea ׄ|N#\+]p8F?{'Y&>kxW)K{N ԡ3B8=5e$G⟲L%f.OuֺO Ecw#;A #ӤImkQ*U.n'%0ycw֩0Q<út߇oG,W6nZpA�Xոt[CxJm Jpq(=M&is�VrQ%[iXxz?^*׵5y;[S1`zO[[~,] SXYn]縖g#tRcG/ov7b4 hCqun5(&CcJL撖`N)3K\ύ<i-U7En$zi_D i+,MlvLK}VInqCӊCR5 T-gwJ?ہ^[>m~us012>2.KlVhzl'c9#یk-dWCjZIܖExoL &yWo9.XK;ce-(GV锍qqɔOx\dv]n`soz42<i4N$F[%mPo"N�8u BB蹒uGdg7h-W5La\9ENs=uswn`cI3p]~]nmo#o7 P [3mWL+[Mn%ѻv+�&#Vj]x6i-YY%I8W4&m);w&D.(~BMuT7 YY_)uv|>X)4JZ)2�)�HFHAff8�w5K HmOǧq.{7XI72۶Xkm /Lwes 1%ۇhry=~ֿm]&B8æyFEj5Sdm3@mGU?m9ݽYq?g[n_Vu=c*Ei2!^o}ᾯ9rC$$+X>,sH 9+`:֟nu7Z0I=XVdP&ʗL"gKNڍ9՗,؞&V[%rYlq'sJൻG/h3�i顔n1`/[?i&eմF#uN|Ңle#<8xkXZ[M4dC;gӞHH5690=yWIf)~k;Hߑ*{-)Xe9+Gh"@ȳXxZ7鵫#(7=\+KtRҠF\?\6x֟׺<x@6DKsG6kt Y-#o~RS| X$K6*ji$p~E;,ixyluGV ܛL|({bx$hn6CJIh�mx#UCoppIʷcXu_ uͦ5@?ϭiZj;[-XhdRJSxg:ͦqu4;"- p]UFLs5A2ɣO-؛"`~~Ugŷ֞LGl9u<gɿ:#3{gs<uX2G+[05<1%GO�+жs֑Ӫ+T1‡ƪ*ssEAsϿQk=u#* Eq޺-rn7 dH?k]>m%UiT㩍Wo7#�)m/|Ǧ{"@^ٺ\mtmN?xl}C]Bqpjaus]/Rz~\)ZM}͍űDp >+;UueE o’A}ke5fT^y~KuK5@# К5?}šs7 ?¾GMR$ ?SK|K8ϡ@tjAoz؇fX?Lz_`�V=1Z#Vִ2 e U5hC^0I{`Wzu!fttMX85K[r o`Z99?Zt9^ONEZ(im?[m@3<gӿ>ƦѢ 65E&4np?c៴d^3~ n(.wŻ�^07[Ù"L zLXl鞕2"jy?mx6wSאW*B[GfB_~6?B+CűxY]*/A ,ѐ]_3ۚ}+7<i̿p=�kZUҼYegtz>юcڏ{ۯ݃gLv�GF(Y|\\0Mӗbg~⻸ ;M~ii>럠}źxÖ :F,V!,v_kޝkoh+mݲrom'ҟcK-[+á5XyESK*ww8iWP[ys̊wuY<>U~Zƣlf.1 |v{~/[ K"C)$sS&%pz=oLje|?6ِ ~Q^MC{go[Iku M k# *)+_yPt-C>$H�,xj3M!G +@} A#dqcI5%+V(km|+tCWɨfFņUZYf"r78Jo]۔4Bj&&g!qʟc\ g:v<6fXGH8\Uku!'6FKy7&98M/[]Y\$J>P<9$w.;hr6W7vaK[Ȓp8;gVmXHсF咙~,0EzVutoFVvg6Vk8մY�3[E. -xY^2z�2õdO5O33#v<䓓^g z|V$|g@JQҼO|MN66Y^y3dW5w*�{}kBi[mP'?R+J~!xEEbvDgG?”F6rTޫ=:�q߄9qS~jb+?隻dm>'GM{Mj`ɷ']߇?e9, ˹7rM\9k/ma4rT~qEĭjwm[ N]SҢ<sEkW~V:?J[^X]yvC1Ys~<C+��eǝpFv깨omb}TQNMcO8T=yr֠v3קvk'Þ͇11>zғBc֐})sR2XD%ecռ;uy\uRA{cX֭tAuyyfEyi=+1ķ E-0n aRx=y*{h'cPB��r>,f@<mgF==j%Xe"733җ+icT}KspƥE돋wIkB@ 1T|RK7K5 n`,r+Bj=ntw~5cg !e={TxDwDHJ4Lnt#psk;HNfUKƳeul, Y)!k'q +5,N2 <VW4M*;IYd F9͔5TVҬl $m);+GkxghXCoBy; Zͨp.E`�s~՜Ko䝸1kB瓁KU xoP�O[I*oyyk˨.m2Rܬ}pEusNgQUꎪx“g'.n~>\GPh앲Jv� lr0@9?mV1o~lb94cՕ3 >1I),uRǂ{jXoZFh2ys=]94+ 4{ـHOqڼ;Y;תIgi$ffrYI:%b#|2$xq1É�tp~5ssf/m,QQ+Rz:Ҋ_ 4Iuwż )Sۊ+tq[[, Q)Te4{;ؚ@7nXc Ō[’qW/38M:b4 9^ت]vuNQN};W$3PY"JC Y'W^4P@ 6m#nesVxYHH(:rbTS 8;>Rx]YXIƗ/d~&|学/ӡGխŋyťmɜP[7#dh[o4+/օڲ4v9^d$U(Cp^~Nmyɧٴr,Hp#$kvui%̭:̑\ حha)%rzT��)9&4zIR37:?b_%ҬT3fQYnJq##ߚجaM#YD9Y<�8R~5y?7MZb2tҴ(gbqsBF =)٢w{ H~u4 'JZJ�3EJZ@ �bR)+^0Ҽ1-W d�xV7p$,8ʺ)قdAǽ!'χ=֙g%Ŝ\qO@ek, N0ֽj֖$ ӖgVS5 w� 4=,}ſnj}q\Hb[jw|2S$Xƒz�( 1E^))h~TPGE�Phz�_Š<AiWvA5|� dދZ(jL^wSE=].ims17c)yLJ<MOTÞ'b9*psAQ'p<WKɧh_nrǦH>͵6oW?:|Igdfa\ݓ.i~(B-%)p/o'j5_ xP;U=pVt?zދx崼\v7}i}K^[XFsqFF,z )XU?+FF5(:I%M u{n'b-F7ƏqK^ ԟ.<`h\|5Ⳗff IȈϭ7DB�~">dЩkI۩_j <A[:ZM%}O5=MX:R #q� 55O}ቸ!5Vv4Zs !j^<b"SEIZWhX{yC7ڸoVuMRHˌ=Gz";l0Ŏ"uҮxR|_M'_MGJ(9K$qvc�+̬~)F}6I4O3bȉc=騷4ҨhՎh.'Y=qOP~QUu;=;w1@fmlnoJdV>InAY?1U^ ^XЊb xlg%"HN=*ƥ <42hw bS\Jӑ[̛ڊ+ha`JԣȱGZk8E,� ugxf S%h@2Ӵ~"xW9$ʹSe[OWjM~Mɠg5GLt�gLX:B�#NyG8U+ԆnA3]C|]xCgǮ=+fKZxfm7̷/wNG\HԼK�[>˅AH6":盲:p*b'M]<ygqXi.5kq`# MЭlFr$~T0 :9xnD!8�~5_ x-R߅*8VJLjBP՚%ojC53ݾ#Ꮘմwc=R_bjx⾝wdK\&}յtJg5e]^#6w(BH^2~"}wX[ YI= =?iDro Z<eb?O♢}+ t_/jM:s[/>u O'"$<fr]}ٲ)D?*gE5%_!Z ״s%hf?Snv֧vn϶FkˣTqR6\k| aʯc3=1r\K @y54H[>p;3hW^-mOZ׾3uvIk+n#5hk'Kd!mv�^s^o6k,Z\Oνq-Q.##<bC1am=KMWvǖ$I;i!w/lT~W.$%w}J`KkEmzpHSXZspƚzWyp�gDH{D=Mbx3KݘՓh峌d�mOAF?3?BY1Lz};"I5 i~Д F�O֧ MqYj\�J~/'o]�qӎ;[P[U,@$(k+ndZZއְִdL*ϧI^6vλ{pqQG0A'Eቤ.;\n[8][�[=zEOh>1 Zc�U'@_nWC5>j]hRNe)q/"}7#)l-Umulyɏ4%K_S6�xK{)tX lNjm?ٚ6:)=#EyeE /#tkm3 rx`?6\,�]>"<Ygu{Mr0MWb>\+rGB.Զ6r`21ֹTVάyЫv<ė^m$yKpFsPMt"ZZ`No[GofSEf 75zR{kx̎a8lwZnF�=:E CX;}D~_>D'q!�[?t jwzŊ+y$E]ץd3үgյjP<39AQ? 躶2Gg֓5b +% S �(qM;;j\YC�56zDrb?" xZzZAi27VҬWQ:^F "˲8)_D$?9:l_e7J]GsNll+!cJ}oOhlI±�5G);T+9Qj6sZL]c 1F>y[{[9_2RFa5]:7ku$NF0e Ql߇m</ B̛7b}kT~5nZit {9.KJFsԞ֥n^-cD �~i62|Iq_W GM>袒gji~Q^3D��T_?l� (aw?UtcJS[|V�#f\&�g�A?_S�Q|5�%cW�q�/| �By�g�f5f+:7vONпG�\V^;;OZ(墖UҊ+*_ 5=EC!GZJ(�{( :{pL�ynȏZ+B}v�AQ^~i_(�* |!ESW<ė-j?O�U�-'uEO3����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2016/05/stickers.jpg����������������������������������������0000664�0000000�0000000�00000225761�14502137606�0023354�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF�,,��WExif��MM�*���� ����������� ������������������������(�������1���� ���2��������������i�������%������6���6NIKON CORPORATION�NIKON D3200���,�����,���GIMP 2.8.6��2016:05:06 22:10:02��(������Ƃ������Έ"��������'���� �������0230������֐������������������ �������������������������� ������� ������|���2������,��5����10������10������10�������0100�����������������������������5����������������������������5Ҥ������������������������������5ڤ�����H������������������������� �������� �������� ��������������� ������5��� 2016:05:06 19:40:10�2016:05:06 19:40:10�����������������0��� ����� Nikon����MM�*����7�����0210������� ������������ �������������� ��� ������� ��������� ������� ������������ ��������2�������������� ����������������������� �����������������������������������������%�"�������#����:��-�$��������%������g�+������u�,���>���-�������2���������������������������������������������������@ �����'���������-����,��-�����$��1�������������� ��1>������_a8���������������������1��1R�������1�������1���������������1�������1�������1�������2u�������������NORMAL �AUTO �AF-S � � ���:�������������������������������������7367139�0100��0100STANDARD������������STANDARD�����������������` ���` ����0100��������0101#�8���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0100��������� ��&��� ���#��� ���8��� 0224X{䭄Jᘒ^uZHcK&⿷Cb7 Hxk2:_ƶP“z~4,0~XŸmp4(F";` bND,Wih TR'==4M,Pp3ɚ7TXa9sxmD#ǰ!|(կd0TwHr;zQ ) �3o,EN8AJ\wL x?^s)wzUj̰W*Vl_}=#ʓ9%k6†Gd |c1ۤu}k@0ȘsFӛ!d>1/u4Jx1&k5\ `b7F<[mu|X(M( MP3I`>}\$6uڒ MٞG%qiR,\DWईPv+ *)-t: 8q(]Uu}9A<0\5j'1xih4/s@i -0 M7붘8]<2?cYp㿲dGۆH!6k͌bOSnINmm *ԁhf{D=ܒ_C>Py~S*g&A񸖋E,o3F)"̍eTZwXaƛ|#޷c~}ԥ Bv[Ud#a @Nsn-DA&fo%RM1X ?C~.Zy~N)<WLeInSOb%k6ַۯ d<up= -3a'Ȣ00Yx]4i+=4uK'x�&f¿s6> P6ۗ:{-]B`/ M+bؠwbD\gȡ\݂ª8w^;n_Tv7qN$,C:ǎ;Y}{k-} kzA<lB= )SS96 0 a4Igl,ykAPC3L13͌bMStU1ӚzIM4^~`xQ9̑9), DWith R_QP3VIT .9d8Q* OfdW!\*ExdyuPTe"`) �3o,EA&g*R~yP>C_=E{fhVWmlNInSOb%k6!HGdݼu_N~2<=k[ᑚM # ]̎5zI{'j5\þ9Ҝ}u(m8n0t bLB 3% '.dQ=rZ\�U-wS.v+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_R-hy�hRz�*}�z�񸖋h=Edo3� +`"$eTZwXa9ƶ/9#ܙ޵|zيm5$\Rg$P) �3o,E P7v;c~QP>C_=/D{fhVWmNInSOb%k6!HAN|2pYc?2<]Jɡј7MpT1(^|.P? !Uy1&*:\_㾐Ҝ}};8 :t-Bπ.L Ծ+vQF $Ur^20^xU$ FQXv/w O.੉B-t:q(;窄=9~}:W?r\l'$T^]O5$ j-Z<Z6.[St=z۲dGȆH! ЌbO MKxl#bVhf{D=ܒ_C>Py~R`*&!񸖋E,ž3�*L|eTZ+Xa�Fjݝ#0h+|լ viQwRԔ"_`) o,8�A&*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=mPiF́`՞'Dpj;R=ikQtpW3/{7T9Qx[ :Of!`݃*ud;. ͈sor쉟n8ӗEusGǾِjR^y\_$HꪄhVWKsm[Qs2ek6!HGd�pYC?ÉjZȣ7M� i˓.xzִѨNp4j<|/8TJcŠEU j a%L\_V5^h%$Ի'Mۓb<B\a�Mw3�+!]l`qk_-%Cq(;ʚc9ֽ ޣ'ζAx4/֢Qg(w 7빘[J䕢\HY&俴NہH!6k%͌bOSnINlmW4hf{걵qC>pySD&p'yB#R=3)`"̍eTZwXaW|#ް#t|դ{ .ٯ(UEveOTG(OS`)cv-%ĊGۦa-Jm2գ5W(mmO\+Oc2J%k7!IBG˴ݽcYO ?34园[ʑg7M 3GA/>5K׫5'kڽ]yÿF19hj wu=;q9d;U-^ZgB+w@ b ΝC_1ǃseU%dGGQN M8$/z剁C6u )<`}I) n%'5s1)z'4si>M붙\J]<c7)pdOۆHݷĶ%͌bOSI𞯃ru~%D#mC>PyF]R =&AGiՕE,o3�֟7͓`T .9dxu[|# {rxĈaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!H,GxݼpYCZ?u)H["&FҢ!q _/2dI |`jTQTþ9зu;(q8 :t-Bﳏ>VL ]+8y M{r9Ys>TD +MQG M8J.p[eB-q~ Ā(;窄u}9о;K1jI4L3s(kԝwK]daTq-/,/lGۆV!ʷ͌bOSnINlmWWF~ԫD= [Cuq'Suv*gx[]LC3+ )5mfeU;#�ƛt#כ왰#|դaXw["`) No-D]CB&v@UO2x0?o_SySͩ$wdNI{#OkB%k6!HGdݼδY12<]+[NވbK7~O'€ɣ.NT{KGY{%R]CS@ ՘҂}u;Yq8 :t-Bﳎ.`t;vQMi_Z1^;^  PwHo 4"B<)|: &q"X3f}9о\5j'Vl1x1z4ojksy6h(ff7"\\3c !!ReQK6k%̀!Sb\INlmWVhf{=ܒP>Py(p "R',N\ſ2B(�?vVe>yʣa ]04't#ޮ8դaXwZTe"D{,E70D@V#g+m;RIr9^%${EUmmVcNPnSOb%tk8Z!HGdݼpYc?30jNȡGB74LpI˯s:}Jy)�'ǽMLI9dN ;6q6:4R-Bﳎ.L +wf<w $UCUQtUsGV$  Zq ,#\WGy}u- ;窚u}Fо\5j'1xK{hd@iޛϪW![V5bAϿJ5'ٻ1'!bOSnI<G WVhf{D=ܒ_C?FRJo痻o!"În͕d4[[!a[٘7c}hȳ ku#bձXwZTe"`) �3oÒ EApgayqx.>CUbWwz*iWWmBgdb?ɐk6!kG~ݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 :t-Bﳎ.L +vQF $Ur^00^rU$ FQv+ L.੉B-t: 8q(;窄u}9о\5j'1xKz4/s@i M7붘[J]<2?cYp㿲dGۆH!6k%͌bOSnINlmWVhf{D=ܒ_C>Py~R*g&A񸖋E,o3� )`"̍eTZwXaƛ|#ް#|դaXwZTe"`) �3o,EA&g*R~yP>C_=D{fhVWmlNInSOb%k6!HGdݼpYc?2<]J[ȡ7M i@s/4zKx1'j5\þ9Ҝ}u;(q8 ەji1VlLV+v\[ ܸ>_05^rU$ FQv+ L.੉B-t: 8q(窅Y}qË_ 7hEe5KOFF �0217����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mow 7ѶB B_3 d^pXsGۆH!6-\%͌bOSnJLljUVh`}F=WK>Py~J2g>AS`o'�*կ싃݁e?J[Hq\UzG#ް#|դSaڇsZT"` �@k,DԸA&gRfyP>C_=\c~pNO⬅mlNIa@%d!HFkC&Vc?2<\J[ȭ7M y@s/4zKx3�&j5\þ9Ҝ}u:p8 :t/BqT/ %p՞W۪bCpŢF2#(_sT% GPw* M/ᨈC,u; 9p):櫅t|8ѿa5i0)sοN,SZeiYw~0204piޢMTdNJ%1DKX<2>#W��������������������0105��������������������������������������������SCENE AUTO �0100������������!�0100���������� H�0100���f$������������������������������������������������������������������������������������������������������������������������������������������������������������������0200��ASCII��� ���������������R98������0100�������������������������������6l������6t(�������������6|������ �������������,�����,����JFIF�������C�    $.' ",#(7),01444'9=82<.342�C  2!!22222222222222222222222222222222222222222222222222���"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�d$a@;IT8 K}*o;9\ω|e&y&Ưq$gk4:R|Vnog@_6+Oztiq>)]{gvFMqk"Hvݹ׎QF2fWU$MpR#�,W�ԺVJGkHcJ z(:ETZah,7YiK2{ͥ+&3֏MZqrAx{SCч!=zMia2atq�׮im3^X;J:ǟJft}]nN&m~Tu~ZhA7qaʦcz7T8Y[a0D^[*}EOcSL�Ā@%"NPvupC{UW@!7AHײ>2jMiug)B#XS8�8ooy>6[̙V<qjZ~ߪ]q+ i!]λyy雧Z5.p|U)tcI+Ǹ ԏδY#ž"}Fi\# Zd);^Bgx'_в �@>RO,jտ>Qr5x[ZG ZO5IJÃXU{SG< NMfD0k1/I,sC,&B7.zPo/̔(3j:G a8iHj>`:FrTJeO�{)�{uS2 #7?9B �B"1,T`sM$%lǭ>8ϙV.O gar(ѴA0wӁEDKc9Z(Y�<Ss.BY9!3V> {]^�I;7\ɤKݴ}4 }Oе|G0VfS9GY{eѵx�5ԅ95,6ZɶK-~}޹I( D{(ż;[$@bOH c5wK$)D>Pϵf rO.}#Nobb5HWkHGR}Y,CMڥA$MD.u9}2;ifc7gP<=FGj9]bhNciݔ&4 $,:v�H*O3z*e+V&ᄄ#=᲼zutC\@^W -5tdgbWR3ʟg6y*G)+ֵ:`RQ9�MK__<S1qN=zr|湕Xzfs_ #SM6zvt;D`+rR]RmrQ,>k@e7'\>KVrnc٣t:OeX}"b PXn$CŶ{W<>>7k $pgjGs5o_2KUlqF{l.Đ܌;J..ʇ#8>K!D?w/b'~,N. rՅΚRN8B i @13*bǟҖF� 2ç>I1bj"dr;S\3{" r yRT~%*I=j6xd۴'+9Rw 1JJ0`Jw 4RQSk0VsE0u >5ŭ-yax7[$l[Oʟ*Y1\j$33i۟z)ifp_>8�4mNךi~l 6;ڽ;:tm3fleq~(šeE=51c}ȣB^[먜Klˑ�,4+[\+9t܊F@?uGu;=p 5nD+ZQ,%l'nIMh5]yPk7?axS#2sxؒLV Pv-"?=J#sr x{H:0 :g_ֶ4ZmD"wҼHDn'׏Y0X'~&  \݇5M%1kh򎽔W);8/C 7Q" N+4_x ʆ9WsxKőo'$?/KtQcoY}rvg~Q&]c|Ex_kʅvz,E9)?,ܴyMEEa -Ibc j.ȵq<K4sgOH$4 OF۸f JI-`~h_gztL$2ю%YQڬd.&1,P漇FTZG�צ躤 aTn暆wB#t|d*2}?:úZI@9??ʔtvga=<!e_ܾɼ d Yx'ڶod獁�`^TGu`r8(WWU- @7j#U$V$:$Hi$ː{֟%,�@a~^;ӥ, FFCci&`VMy@`p<g�XEƾk.0sEWϴ,d&b9&-9 }r] Ʋ/nX8Eٞ=3P.KKg3 >vv:^Y --Y1#%UsbP:lڽ[Qko"0r3]xGѡ6-I2b}}[Z>x&{[R\w{JR"|;0[Y+2?cҹ-SƚƬZ8XG3{_Z?f5Xb*kV+#Zi+\M9ēJ z[[zv$lco'|gbé.fb%JaX0;kEfRZ]4�盞BI,"Is\-.e2O Q zFqmsm%Mշ b#1|-Ky.X]� *q�kil\[.N gҴ;4E' Wڻ_^\xW2nOqK.ǞiZ~ڬ ܫCSuP 1_\]ZĿ3קZ .$\(Pk/5(ol.1v<``=9u8 f 2)+ulw�g5Z^2;eiqb20+XݪGm xO_-ƚwRv4*WW/ @8X8#TmouK-rq~ר]J۔�LV孕"X#1(1K-Z͆kozў}Z[A.&H?*L2\Ʊv9M"̘;&Y1Զ�?oSC(e`T=j=/3!t[yQTdb'>VA4RWEBMJ)%$1*|1J* |B>h0HU_ڜe̒mǁQ#� Y$cdB0`J+Ѳë0|=o<w!nC"@+>nx;4H_*[�>k׳HH$=9JwvkkwGRA(QkotH]My!FHIޤt]ZcmgEO"á<Gys6瀐c-wXѝ*($Xo,u+A$d< 8JjBƇ](OI*Xt6b>?݉­J,.{R?{Ӭ1_嫎ߕSA-Zj7+5ۇ0:֟+[IPY_۬$S+ [3ͧS\pxI i_\mmrMè<]y6m- q}>[e)p^ ǘ2�ujx&]Z=ԘO3kjOpRlJ( C38�=bn4 [(kY'Ld�֖wcb,$W,}qS]J콯jyvlVV~:4b SsP_Zējэ CL~Ԙp�t2U�8jY7tY|C.O�\T^{ ,cO ү^W#9֍̈:n.{zQTFh+2tijNvvqs,wRJe|j]Oè-"08(W9kxLGNv-gxG]R �W^O=fFi;H7Wlh3ݼQ'?uDw)k<Sݛ�niڕj.-%Oq6sƏk`- l_IsڱWJyMDr<NCߊ䃚W*Lj%lV}2W:zo ,E&v槝{%KNxD{$WgbkE̤vu]$eăчFN4~KZ?s].jZh浟AwJ�+h6&aZ�@穮Z\ ynkv�hvk:KxkEZ9W�?jeG{9u{hH/:22= p;vĖGHu_-lmO`rK;!eX1%]8WToΝK-#L? 8lʷRFWC/-V%�TU{l!X7:SxL}'HV ;$;[60HԬ?O<_Y.)<F` {T gmq}k"iw �' g7gӿԷ1+n{%7|ph༚[ikFb?AO> ڌ 3(2 1ݎ4媰6F6#ۂc rY2nm-/.Aj`&0O8'V-< d��9u}RFgo˸ :nE)sGGsJqV7t[趖m�xRjk�R#_$;g6iQ wR$+n ~V7bJ6BW3Хjyv,Q`:Ծ &ۂ3{�p A6VpXXҏޛ$ c PX$�:ף#zW=;%`�]�`t#5Z+Ȳ1"ƻ8'Vz$l49Ԍ0 .,F U Q"1PPĮ N{Zj$%ԐFn:z}[snGڈd+;˘0^�';f}Vú( rd;HgMwaq"3#` 7?ԢeUY1tq}Ȑ.}OsXZ,Ź36?uv=Gl2*A #m7MsIy$Euw�Y'W$)` #Ej(U5Yt5bFXFV#4V6'+ lF;IH䁥d@;C�xOZ1!HU,GCVJDm8BQI+9Y[Tg\E c o֬EZy)6Yv|0IGtT|v 0O KIR36-#D;"F^vt0oW"GfgdӚE;"4P3u!FMH�P�)sI[aQA�=K[<%w" {Vv!2~}us%l�,׌�TYi6t �<b9fEϖמdZJvK պNXDWn2qlՄ#UTEUQ�--0 ʖ@-\u.%AS�#cR}�MEs:WԶ_IjY6>esMM�SK( =2KwE(fp:W:ݾDս \;J5YP;e>I�W5m^Q=Y1](:ҶuI-*DǮ q:|)"FрIpó8UE'= 5Qق$�N5?Gzůf5QmkWkiVI 8$q&JVs`ғ4UGZX5܍/~+sDePGZ(óc\Vӛ{H٧D9ȆdW+=Mܟ5ZX34VEȡBsM!BH:sJKU<�3ɮsel;X 7BۧNխ]0Q-ُkRmoioˆ">QFoBrD% 7{;wr=rXY=RԽ4mw<3dsZXT JȤs W3B vϵ`h_;\yDpm17P_hKvY/?/\/4t株5*{*ٌF픸G1]ƗŪC2W⸘nk_ssɲ}zV?ѵ=nCs`N?JH5s6v_  e2} Y>%}K@fM9 ry"U3N{)+֣X%Ewhn_gCGL֚܀+6NЬ,tWx3ʑU[4 # ;5Q[heKJY x�猞ޜ֫跷elm{!xu)֍_6S v(꾵.KM멾%CՕQg|9|˸?wYǫHMqqvqZH 5UԩNJأX#Rr]c, z^tinQX5xVo"�uElt;>"9`{sZiھMkЋ;67~eaoc &M2 {TTiJ\nVKOm9V x7u9d+]N ^\_ܑ]QA0%G 2O`37$\^"$@LS;�PGZ&YXL-ɔ}phVFg=qMn Q\n\?l(&3B*ƿ %xH6 h3(oj<�{&GN;zQEs�gk�"}~Mt~�??g2:QEVeFڧʩG˜uJ="QTD(ER&o5VQi.FHϭKEDzCE\M G4LFEf$s+@ ČcTV Ə|̟�U Z�A*� >,Ht[g{%`CH7�+W c4http://ns.adobe.com/xap/1.0/�<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <x:xmpmeta xmlns:x='adobe:ns:meta/'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'> <rdf:Description xmlns:exif='http://ns.adobe.com/exif/1.0/'> <exif:Make>NIKON CORPORATION</exif:Make> <exif:Model>NIKON D3200</exif:Model> <exif:Orientation>Top-left</exif:Orientation> <exif:XResolution>300</exif:XResolution> <exif:YResolution>300</exif:YResolution> <exif:ResolutionUnit>Inch</exif:ResolutionUnit> <exif:Software>Ver.1.03 </exif:Software> <exif:DateTime>2016:05:06 19:40:10</exif:DateTime> <exif:YCbCrPositioning>Co-sited</exif:YCbCrPositioning> <exif:Compression>JPEG compression</exif:Compression> <exif:XResolution>300</exif:XResolution> <exif:YResolution>300</exif:YResolution> <exif:ResolutionUnit>Inch</exif:ResolutionUnit> <exif:YCbCrPositioning>Co-sited</exif:YCbCrPositioning> <exif:ExposureTime>1/2 sec.</exif:ExposureTime> <exif:FNumber>f/5.3</exif:FNumber> <exif:ExposureProgram>Not defined</exif:ExposureProgram> <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>800</rdf:li> </rdf:Seq> </exif:ISOSpeedRatings> <exif:ExifVersion>Unknown Exif Version</exif:ExifVersion> <exif:DateTimeOriginal>2016:05:06 19:40:10</exif:DateTimeOriginal> <exif:DateTimeDigitized>2016:05:06 19:40:10</exif:DateTimeDigitized> <exif:ComponentsConfiguration> <rdf:Seq> <rdf:li>Y Cb Cr -</rdf:li> </rdf:Seq> </exif:ComponentsConfiguration> <exif:CompressedBitsPerPixel> 2</exif:CompressedBitsPerPixel> <exif:ExposureBiasValue>0.00 EV</exif:ExposureBiasValue> <exif:MaxApertureValue>4.80 EV (f/5.3)</exif:MaxApertureValue> <exif:MeteringMode>Pattern</exif:MeteringMode> <exif:LightSource>Unknown</exif:LightSource> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='r'?> �C��C��R!����������������� �������������� ���FV}$2)AQo-6ԥ0ɉd !c_MxY+{#pA+inL;.)s42D_Iwk`ku$;:jtW88͚�Hn\ e+IODS.+%8w^5m7 8TU鸔 sqam vp;ď!Z^91HզPtL`zk+EHBF *S4NRpO'4ru {Lؖ!\eO 6]Lp{LZjrdOz pMsA:r9QH܆osP^ՅӬ V O].HZJ}26r:!seY. '!xk(A.DZ/Oްr{,T#*XbgYv"/_Jl)pTKSmVkYɣ-yzr*9F*i4+81NПL?r4ru@'9IA_Cv<gZlp@63=jW83ק%=r*Vtgx;64~4s+|,L) :emwt\K㉢(|I032Q{輏\SSP. Rx8Rm&sbn2Z" l3b &Wxn6gsjh_q#fJZϵM\#r}4<Kχ$ɔr9̭|5k~OlV6wNLTҒGSEp!+h^[ V+SFf'nlnQOg*{ZZOzJ{: &w?򎣔Tbǽ/\�;ab󠺂U`͋ME\cdZ8ޡQ7@H盲vר|(hmrO$�|NMџLVu<" gۚbX)5Ct,L/vN#,+[,&J;. 1t_\ʺae55\keU FTa I4ț kʦI龕oD�Jr=g^76O6G;yw|B6ǣ:sMIj`H('Buե k>fmx1OLҶsm)DzY's&) x bR!ľ>h͘\OKeayKmOٚtnw!Y m`f.5-)8A-*ãE TZNY^:);D96oa$Z뇲e=K|u, *>;U`nN]S$ɴ]G6Y P? liCrER_{aO LJO1zK (vG8L ͨcXKl s�]`!U]lS\@2sAc6*9y^έ:j9! hEK sc->z^m(^kO+f# s5DMVcH�u#n׭z{qN1*SޝqHՊt(My.9f�PᄚqqU d)٤usQi#TIiSʋ*^qbw|ڃ r �kFuo;!MD2]646s# siɟLM,V+ڐHy׬yz?HnmfPjs7Q,XR Pr Dsᮯ}Jbt-'I-"% J6Lp}s>1Rc6y˵lJm%3{oYUb|İb>3F|`sD.`bĤJ==o.V`E@uYh `%}A4O˂:onRP0aDR0gwSsGO8c, @ UP JE;ׂ`0fpRe'f�N3$Q]2AZAmH%Qa[a207-EO%E, t"+ʄ! 3X2ޘm+F¹e2 w,~ymy`X)l,K#U1|mSڽ/v[JY]#� ,{OZqIs8W=JnӐԢzoЏp3=!X.~k*ZSߠ!ۘ]<}Hħ/tf25\_.̨`rAOɃeR ΰJʽ.` a0:{} x;}D+FR~c6Uto5s:%D Fyyus)jI6+(idz>GzO׺=V2Pu:Y=RIK`gW5]szsH\ܖ34ݻ!|vɥPQ6ӗCi؆4<�`ɎutU”0}�0������ !A"$01#%2@34��o DVqS0ܳ$;9Yѻg1u?1Ձ䀷J^)vc9QPr5༷` 0&Kإ_%~Ωȃ!KSJ²*ˮߒ:q<.afi{ Nf2af'aqZRb]dk~-^;s~`-MUvO#\!S5)mʹEq!~EME~bhݹ]eRȄN!FQ لNѱDCm')E^7q}gCZxN=%rĄKx &2h1brج9J]sqZd2qR!lqlFv58[Upv<r+& WpkTU PC'^sBdprn<p_r;>KS2㔼>M:>qzVaДˎ<< ẅ́H{oߚpIu[ZЦ/Z ,:ZKOm݅ҺіCە_j 2h1!h6U0,&FRwaH-Dv~�pɭƒ,PPyEvTf\Ϟ0?Zˑ'9\~W~7M!LeEFݐ&ɪ |{59jճ<,PmP$5elS:Չ pYqSrQysOԏP=51 Vso!eYyM/_[Sd&3rS2GLVaaokcźH"J1۩*ϋ~) izfyj;l׏/D)aR-ē3_܇\'-(1 /5wfb+0f]{sM.CpSl61«geWʉ)N2Xa)g-~)[OFG:Jo-2/x<7WU/"ʌhUΉ}E B#Ϋ|Wx*Kmd>-&Bc Yn)u0'ʮY:"On^L@Gc ֵm1cxq/-0Z?ݲ)=; m^XPMnn EuM8u_O'8푶s#,R eg @&&=!-+<6QiR*<"Zzgt152m+!ULW-k {5ӕ2洶{jK3|0JeyO˼nRYƝW de5θJ*V^8Ǜ (ZHcMa2–Ⰶ%p@ifSV`ط q;zr{%_"ӭݲE>K_wB4w쭖|qvV{7N!-�L>1daw Fs"4h%J_Div�XG-n&sx>ܯ1@OpTN++{%:o'X`xXA< {j+oLtR, eW эf .4xHױ:e0U g8ț 8ĂU[6o~Y!18l"^k8Șiey!ֲ#+", *Wܭ?iq<ݯYHwW»YEsɓO4W"U\wnZc'(I ;^Ǒ.{hQ[VK!0c2׋ Ԥ9 }FO<Pg.Yn!'^]ۖ|j4gYc3HW&[JsT`ΟdlG!-ո1!ne\gfƖ9:ŰɫVg+gT2j3Ɨ$e6x&bFI@Vp"*Cg5(HWL+@*=; 0'8O@2߯u+CCg:Z �=N56Mnk4HgaP;tvLvu=9j8V5l)#�:Ev3nnlEejs%ueEҶW8L!Ї1cÊ�ts PdŖ[3;̯6LЈ|u8�^Ŝ"?e%e߱ :')\W 7E8Go_B9z~>H*zY[APZֱ^}uf<&Dgw!߭S9 1+<ZzS)eN!ܩ8亵j<݊uVi- Cq1fԫ䙋MM2|\/MY{txR)H,s 8S5q/HwPZRA *ʚJsƈ׋?߄ }fHYԫ{T|ͱ*PߛqeƚvdˍR5qE%B?Ԗ^|4T3M#lԆoȺ^Yp:裂# m4X|{: !hW~j<+ k Ϯ+ťHJc&h:VL̺gIsʭ5ITdT 8rcfWE]P Tm:c^H0lJ^9Qs0fm0au"ۜVtqj`%uvRj8b_nVqj.Ml35|w <av2,P ]\#"W^e򋎽Q]UAj&<< z lJjMRr6+6*"זS1V.B kAfweL$cJ*ͼ&c�w. T=V]kڤ&ĐZ>sZJ<=.K]I h/ݎBcP c ɫ!KxL5OT~:aC$77 L2%݆ǑtBq8xy$IWo=ZI,eg$rS#:֞+UlY+Mc*Հ-S� |R1 F9p)NbYj^U XO 5o^r0HGZŔ%,3$%ƈ$aKTUkVˮM|?G9, Jڷo;nMEEZ7Hh%XYfÊqN<((e$.Qqj)Ys$Q(x1v%r$#R[,!q9�9`+t_?6P@u{L;;#DI0i;\1DzFx@f1=zq%(LH܏^M\c ~Rl>N 2+ ]낤D9W*"R=Y~dX(RUʩY[xeJTOugM26?3 XS+(亐9:Cm]%TE3foiR![-vis�Un!6o.McS0O\;pV&V7isda@[5CI" °'$}LytM\j꒒~ #JnT]ᕽ>6uiBi>%##.ʔ,*S1%m3kXF5#9NW~8=G`k6[-߫uCXܹ7M9ҤFd>mgϝb'u CE`68/4}o,+ڭ^Z׶g:~A7{1DQ)}n>(1Vwb/rzWu֔bt[=KEu Ӓ<">|`b朌&,ES_Z5"́vEbV~B=U3{.3rW n{f jP;T$}vݶktg[?c�WLn[++(qI^"1MZӌI)mqjU r=YJ㥡#yil/g)e-w0 (#U>f{VOfzf};z,X F _d}6*qs? Cߨ^'<N]A菴M-4|I_{F3^}>پd[c e 4q׭5p6=~SixjXCE>V !by%>s}R7zdT[)u*SHʐIJT7BRR@�A������!1"A2Qaq BR#03rbc�?q * x/+tU,/1֙X*2Urρ IBmn�I-Ĭ+`XzFN(̑"t)S9\Mbc&XiByڰ35GŎAڞ&EKV OLRA9fPnZ.<UjX7ҥ"A@~7OZUf3Tu T`.MsM74ݘN, #PHVBLt¨m+Np2R\5L7 ʂ?ֻ5CdY@Ng5p[N̅mޏBuYg㒩giThl*MC%`gj&~^D䩴≘jB6:\Aּ,[ye1|#9AiZOի~'1UCUQeS׃vHMgX* zyȠ̏Ep,'0> Uꪹ:pZr ^LTWYTnU#=ZXF$[vafGDX ;+J t\}QtU[ks˲Le[6g"e:ZٔeM7�:m,lu/RK D+r Ew:7G&yUlLՅ^*'h}VZ|J+ [V:閡$x**Qֺe4eVokK"ZMVnaɼb֋Ȫh^[PbV Rw_Dޣ}PQmȧpZNF%asW ZчՍTS]cQ*O%7ѮUP9*-UT?ދR -냘}S/ke9 e@3 ;8`)ĕV*Ba_s}N1UxUIQo4NN9s)@,WuFXfn ": w�57P uɔņBtM;k`dF̆ *TMLΊQD'8CTrpL}7K~_FAVB� \$-U"xlWU"NinEa-a3 S7|廪<2�sSo*eRL3EtUdJUªPt3f(Aa�MxyiUZ< M٭?vOGhm- =چ‹ZxkJhUK f\lBBSV'y*|3oabk2<%ڮ{-m TKQrLoT U7:v9JO�U.VtTW&tT] h{[U.ˢvsi/XlEH֛_klmn\?.4TA-[J -<�Rmԡ:ڍV!Py2wJ"'}lSlDL}BM٘}R [�"JhK7;)So|xKSU\..TE3.cY?' MHah4zԧ<bȴvj[O0ϰTs ܖ"EvĂ8}tqm6OIDgw@2]cR)â;teD.V'g*T7�g+"-5mޓ̘I:")MƗupAVܩS v"�.$䃁sh1`;Xll]: #-t)Z)dpw渋; xznv|ܠɞV\~ -7s$dUmm/kD�zz|J5֠>.V@UgCĢmVHʓh*N`'aG Я7_ވcXv?¨%P79l3%J5�ej~oe bi6=o~ PGY@1§R~$ꙛvҡ[30WwتZs5@0aVyy8+U j"eT-xU贐3GUz{{dM'հ>ɝn;1[*4?'5ߪ">j mE[v�ּuI�;[Lb/~nwXM%bPuR:RNS8N">9ЅpVI Qbw}m#ks3}^1S[aqӵ5\�i-n δ.c`xkI9Ds.)թSxHkP :/�?� ����!1"AQq2Ba #3Rrb0$C�?hUg*jN/ lC,یYS;" JߘLas^j1b &pUp⧋,k°"̚.U=&9j<\XEJqiV3"Xmm`Bg#om!îI<ղ7Z#JC)�pUvU+hGʅd&7SqUV *>,dLd;#ex!# |f3bv&pzGUHb[$q>K+w _nGaE{X,&b1Q*pWvю9P-;"P^ ;,#�d)9bjpeE[5Q\l"WomxOr]g&ng*uw`v74"p. y-LʁrUMJgM+5qP̺8tj9.3W:,8Jv@ ⷺvEwI৏u 6Wq!M&1j:kfUkoLEIGvۦp#G|s0L9fUܬ6+e;(_w0-89RRK(`EUu|^F6#+Lt) PmsU|x~kLJ G!rw ֖X=U|we-&OiLFUMQ*J,[BuI&8CQ jAPo഍+ M:$iDxpOŕ1JC" hST:hiD9UVǎ?dՊ^R�qXCaCٔ2+_ gG7ޏt7wSO U ^pO{`;oGX4PU# (-xu;Vf&߲ڑصdܮUG,V[KPl<#/(_ϗ[B<u 7M&�a2e_ kp+hHcGf֡~'l-&k)%ڪSQJAŝPdz~U|{1}oe,M+rOR׶ܩnUYYpJT'͎R Vq 065p PM7 nyNyvva-t6GjϺ3s;Dֆ(U쳁QJBvߵtؚe/NSxCU#vWH}5eSRΪ:RLu?k j fZwn Tд7n 9-vͺ>̐'0-Sf&]�"ۦZhe|;VmN�)xP9ܶyZUuHcLIKf z 覼n,~m Ot^Mv3z뒗,vV-!N'9xג7g݊�J:bIܶo)MفM B:ix\EHgdvCe+p<e&xڔiA41et8se�jAlc nߗ%uTLK;{GT 8qU[qDV+hU\3T߈PlgetCe5I?�ۘxqe;6lVΆL_5quUK74l|&1r3ܑu: S1Ut̬l?Gul;*m3mD*bʜig)^e7*8M!Bi,7Gt0qj~UoEsmmm5(+襙Y N*ݗA @DW<\(0v.�ʧb9Mhe^??ӋKos g98kk)><UI ]q8W҅1pG] oo'x7UiSkw)%dO*.몦pqWR}U<RWk\=Io2FɧTG}`>OEϊHNg�GKČ{*IL޻#s\_ौ%flx\<uDB3Du8`"m@u$Qۋc*bc\*Tb ?fHV]d2ޕf4T*}\Sz1j,%l�9w?,T 0.o0р\./66W~[s:],@sn_=&}o@#>!<ī{Vh=8*w TJW¬GAT'TMgLv#\sp7;;Z8vyXqm7u&?zFn)*-hw –@5fQ*65Rd|T_,;[C޶/GHʧ'~'4APMU%F3. `nadcٸ&y8AMn#d%A "ʋ𚩽/rX filbA#s^M6gSrϥVr;t<8vULfmC�Һc)\g^W@bqεl/'u]$O~.86=)y�Q� ���!1A"Qa#2Bq3CRb r$4DS%0@ETc&5dst��?˽ilY�7oBxl$ O}NӁeY cy1qlB.™BL4SR{{fD˓o0A#(e^%cj̔םc%(SXTѢ#Gw<QvɃX@iQ_مUϳ;R�q-. [%FuG]#mwԤgjJ؛0*D=&@ Vk ċ5.CkDŔ Pe/o}{ F,Ye^~{m_8X=hϻKz8̬:yn#K qn x`yD賈IJ$(8x*ëpP!�<Eͼ$}Sp]W+qX.={Yz[ ;9�.o"08}cؑ'&餌jNŐ�Pp ?Y/:}SOI -b400֚<E}\-]z�eqK[n7-Sd {0)pc^\_W]O0hy̕zñB\iRbDCGu\i6/iz$#s?ga{31UY=V*fݎÊjªQLRzr5Z6wWկ} q_њlPW 0,e[\wqM;TRhmxS.HN}) ARx!kPG�W<R4fd£l- PhBʺ3^X[[! Ϸ 9y=\o$BY`.,ض4(.qy^e%|͝0ҊZᓎ};#TΩw�6^*<a@.LYg>OpR~^9Jꭎ*G' Do~iwnZ9g}=]=va}yj1yBOH#be>zUေYVqI >'Fo}:>qw_;O T̛)*||[ <plc@Zs- uw'\Gp+ȕ8Y{n.],oT*jEGM.u/D*RR]O6t˄2<w%w-fGUUNS9xe"Ǻ_@'wkU{A7뷮tQ̅i8)Gyaot3ǣ 8lڸͱ%K|R‘NOOYFBYj/q%90Ȝ=+`)ߍcᎋN!ryGy*mJfZ)Ocir! ȝݩȵ9]FuIϗ5JZ"mY"9J <iUPx[�aFS׷@Ғp ?5h|F\�I`dL8\N+Q[Sۮ)]G}wxa.6BPpݡTg_M"ٞ q1U2e5yYgC =et֔2Ҏb%Jiuki#&u1NۮaPMk7Aε.k\%;)pf2s rϨÖ!nw\ )S=0M"tCs7uy9!Xp?}2la톥߭FLj’í3);qg9zHa?X Zqm7cPdR q; r: eA7^ld4)'Bb-A64=E8qQњMۊS㋑e ZC[×ڐGPc *$8W-`Ғvŏ(aG^P^6'?5BV\h_Y/)l}&Jf�_}<m6Mm%ޅm܆(#<lQ"-2`Z<g쌈4s$�c N^Lݒ\fZ2߰8G VG"5Fc&v+.U~z0>l<|=hx!J�oZ"jy<pZ0Y2<N݅_y~ӎ[bBΐ@% N\. .9%%)W=\q!Řsn* V%Ɏ88l8cLfM%.$m݆ܛ)'@,iWg~BX›q*mh6(^adU!3U!nԺ*x!~LPM>7KZ$;p ^! ,/h6UYz:?[->ڽ S(AR%kbSڒyd?X+8Kn[)=ubB`WAst>߬o߯ĄPw'G8) ^)BӌRSl+ ,ZKynX>"yڟCDUڔa99Q 8C˾N3'B鿰q8CЪѮhb8{Ftx ]>Č8" 5æK~85wU5ۈ})i Y',\4G;|oJfSWTcjFE=fIj5bJn;3lMKk <1"@ꡃ ;pRT:R[B@Tut6:b*d!n4:n o R#>96Gl&4`z:U-Z7V"~'މV۩[kl[gB08R٧{T=U]-%Ռ:}EĒښ E m\tMHzT{3_#fN ew[oѐn.l۰9{| J"#19Qf\w)!=i A)S!:9MT]336*P@d7YCrr)E 0qRU{1Ť! =oO9B ƑMߤ{`6^h�C|p)a4Hᅺ[j\9|B]b=laʇN`}EB:њq'&6nS}52w:Y5NtPUtiGLJjgM` %kR7tdZ4۬9sa'Ov :=QgVVHI<S~5wUe~Yhq/%ca<*{ÕXC}WAh2b4 O!ۍb'kD .>~xb=b*"VјO#MrO<|86aߛfW z*pJuRTxRF] %mU:Rm *eJ͞|*H][?#UkM1Ƚw͕(=46-FJcdV[Y-JsZ7 HHbiN"|}Ld!k1,|Oc&Y9'i79[拏7[؇_ځ%JY}J̛*t 2"c5-G@rND-!VRp!J)k3V7[澇13n2˓<:y=Qy�i 㗕mTz )ekkEؤhF: 3Tqu8r; ib0 KdHŤ \oI8 )MK@in)! rA瑟'\½&-*n.hpKu[0\T}fDG~ v(Xu܁i9cc%kS .^M0fd궻?5 NK} VJ|F+Rކf+{jmؤqev~RA|-mS,4c_CDJqA#k^Y!D \$wsF?8@iO25r'WGfFפ捧Ğ8q t<aW*oJd{&d=J'نiiqFd<6 ͥ[@Wp>bס)98GTg.>^^n l=v<۠PDڂE/{GYmkTf:VP?>e (8K3a'O n]9!Rd?khInS#2R/�,m{M[tD't7s}iECX\yI;\}SXKh HSɰNy$ :$t/SiShW>r,4z(Jr쯤Y^ͳ#J;T5RFt\X+yVyoY)R~6~N2:i6Ҁ2Xq|DEXj(kG(m6,Gp%}j7щAsۗ<!Tr6ׇo܊۫LNu#W P)lR8aMljcv7ig/= 3w&{aO<tz90W%<F?w'<u+9Īl#LABi@0O%x⹰KIq{+%G&*W>z{[q'oޑ*hzW|0*3 2H59|Z*TR@BF"<TSWsRK7pP§S&Svya: J]VbtE"([_:z"^\UU4̳cQWQK:6W�J)}r W:zMF a+r>Σ nPB ҥ^8mfuHA߇1f'<!RC|F{0Lj\qZsw9h*ڌؘeEhBLeP[V&hvQҩ'~IOY}Tvfd`Uek_lILUl6n+M>/f]ȹUzHqEp#>iIR^U\1RP3HdP�m<ep5y;N֪@(܎JMq[?֤Y'OI!rDqZVFF<e\U qʎ0 U͎�5k<�{:ϝL{|5ߌrJ_F@5͵sN>uj$f7?E_GAAI\HM'l@VSqV#~S&VSQZl[pb HҚCJ:C$CNJ8u2bJzZ0.ئXQ8JjS6XND WX  'MF_I�I$ii_0n"S)!ZI'R9'vL24Fdy]IO֟EXjJo -x^\%5i<e7VZ` O 7&uB1], ;'Xu݈(iqBgmŒc/RˋSr{ӆ(oeD]J=./wa8'>wRQZSG򡶁[ls's)a~\8; qc՜C_q,*.?[o�fO;4x~|-S'^ LJXTBd|FQ))2#+^_ *!'$^>[Xl>Cw&ޗ 7,X賝@_N8jBiВh4,ۊiW q&6η>3tdVr-PQCQ߼NYrԨ/{iJ-O%M3[oώ.3ʌ]h]9{/龅I0'n)ÑV&q |Ga((o MK*BofJpXC4BIp[Jm9$8 +R}tjM>MAt:O>Ïʹȯ)|HtȭFYtXT?}*Qv)U* )_RI$~7»O!5zm4˔U(v\Ҧ,RiԵSk H1TTSO2ߗ㈛GF1)NghۊO`&ETA۽-oOO�Plo?$_CC5\lUq94>QK˥ݫpct*L_\9])%n =Tm)pRD$bː0{SUR&֨UVo rUxCbHGZQ;ģ.@-*jԇK]MĐ0WIeiUKI~72gQbq_~8W**[H-5ŃN;ou#@RtC|<pmY !rz wH�eS\~s? /䮮�3Wm~]Ө8'<<-fơvٿr}%~V?0'e>č0,Z ~GHs=N_q2 <~D)mpP+q™QJm¾ZcCJ֞/sInS6�7A|PXsL5qOΥMwQ Гm]]"@Y_yЊYűK`ܱ효e3Z>Q^rNlKfZCa-+w_=)�-O#TbUdI=@}xkU˛ +-濳 Tu̕$�&Ð9n<)=*•[QJ}펮TZw .L6)glf:hE \ UR|hBHW9 Am\*췏ur'JWVo;v}!I/̧)* ޡ>j<|7̤}xqBҦ ]GrKԈV}U{ئUlh#~})nW'kvv;�$ǔ[~)6 T!&x)-Zf"Zk)8U9! '0N#1PHBC9Õz,l )>#tQiPQ' &G�-Fj}:l%ݟR[ 9"5yI*be7?ðn#.nYybKe-*Vհv]&ϺnQZ{I?2ЃR廓ktQ n42G K}pZ! Isf3{lc5 [' ;deCl $$v q?ʔ!EI ߾l.xa0etٺO;"CaFeR32v'Iqx>' auO��=ߖmѽb}^"?“8bKsx U?T7ԵMB 9>obya3)s½f#V|(( Q?<DW "t'OqKJ">ob7BvxÍLj'9E{-H!C"IQǦ_?GXSR$8;qɴRa!@>[4IE {{0q̗7I=ߡMظ0i3k[+-֯;=UaڞN\p =QUH eVQsz6s_lٔyd2bm <)_)&R5~b>!1cr�!`e&mef^oݧR)j/'9Ôj(SP} Lm)6zmydA?MIqH,:>8ʈF=I- pBU:]R9(sоRte m{IUjIM}~*@2[9AFRV:sۗ +e 4zS)oy#jx_ ez O2^ -RWT j܍_ڨm*bmy+¶.r-_&/M[?qU5Zo?}(�]RjQO:cJ8Hzxz [_.u5 ΨS=9C%]?*8.ue.E_A8گʷ"͕Vl<`*qZ|1vf#ޟ1Qme^s'Y&g-~$ev=ؐSlD>nn�~*6~Qm뵙`)6>V<m]m$yYnq:c5jI 9 Gk{m$}!*�Οu~yRӨ[I݋e#6g̤ ?1Ԇ+1V0/?mvV))v�$_}ՓW6`JK͐!&_zߐwmJ;Ԯhi.=@'Dl־<nDd_7ÅH"HEc1NFP}g >a7|,ߥ[ޕs7k*^-reXX]Vy FGON Zd9'ͼWJI�0l-Dy3%*`Ҹ)s[x{sv"5'*I9P@>Tv"E1%c(WS #-*UftZ)uY<5Oln+CuV5.?T%c~#4T;_G8ಉ}aUm/A 4̵gߔOP{4?"2:üvbAن7OR򨁢xN)�E~8y3!(: a Z ὶePkq@mVZlf|+X\vFyǐ<El(vy@ n3x_6u4m|*1u"ݗrpWPb M�d1:}TuR5� moLdͨ+[F[7{78j7�&lX{P3lBom|1MKܲau{4+b? NTzx}2poc݊EnJLÐo@\E: (7y<Ϸ2>`'\,jv+eSB[lz-IaK[N>-وUdfS˂ra onRbWmd8s~\7*#冀.)+w3^ q#GY:KB<e&u<2ER|pe99Ma{)W>S2O'l|jX^e!'Uߘpڤ%2dv,G\Mg9|H֛Gt+8#w2~quPs6~y]ZU (},oIgevN3{lF f!$N2&BlpM݊ $V( *A?_H'8߯vI7rkQJUpî$Ԟ[=�/E.H(zH<06OvG3:r'{G,JM".^u_~<?+MBzЉ'N<:Bx$ȧ7i*ݣt/7VKh tY@\^bWf#7^ةlJQ9̍[m6(,ܬ= roKXlEUiP*ZP,t,(% Wp፤y4Z)%-/DB1ZKM(]hrFW *+LNKR`xq(Ǿi sJ*&ܵ>Uow )oMn9 >'18YqNk2$;ul:[ u:܁cymmbsf'zNlRtݩXW<,?]<wC~J#D^O%)wq>#\Pipeh[V{x[@n<TЁo ]>˛*<H"J<N au8SAң+| <[,D)!ێ\gZC^9V;qZKU35XoܮuX2#T"Zn3,;.Ml\lkORQd!gD1&G`[Aǥ>3%֘UsDvs刳zc0skf*sdF 4d0,'i[T%)?f꾝-@J7-m-E%%=G -6N;O=DSS<gإ/i#KZ*`l-#\6`#qM9/ufhʤIvTbRB=9W }jO?Um3}$,bLCKu (p s᙮y7*_FJrq<ic sc. lHZ6.Tyq] %G^ "\m=/A%:F}cjQdGT q\wW}-&d0lZRbO9HIˌ {}q.DZct=Q`3;Eb$8)Gs;EL&v7iJ@JG�#Ls+e�<8⻳9{ҷj_u�&MA�/n8SIAA|bIBXq6-|Ç~ n5MꄈW9$ LŠof �>N[#/qNٹ[ķc8I Oٳ},F%IF 8ͥ9NlK~"TZFғȗn5 k32GCEA"*l 7ԤVWbVr 'R*dBnx<($Pp)JQ)!ICPv %Z+/ϦI m(˴ M|]yp:{1Pda{4]/2 Ұ/܎$DnWCq*HiW7"$!&X:Rt(ξZ8bdxg[V.ۆi[N`T$X/.wwr?kg%: g%E]p�W z`�K2V^Oo*qIB.V�K[p].6n~ e;{qTX*yJ~ M@pïƏ&+letp>Y "<H̷U >U=)ZÙCTݮL/Gvb=w}'4׼`yR$Cn/kWa:7-Ϥ?ۿ--Z*MF{Sn&e.cR<9~;Cy,CR:r$0.TvZaIo;J{jpԘ!z1* O*݀~`bKSKis&o8Gv*U–3-^+YʝS33H@~~m @Ƞ S#m';q:Jw٩يJ\eJ?wBR Wq'ٽGz}?ԋ 6J JAȑZ\?]G1-l=@pĀәÀ5�tvy/'f}H_ؾ4)b9-+u)&ɿUbTQTxno)ڪ(9)tWm} q.A1H4ۤ)hRJ{,99: mU<jaIyb;N/iqRj(?0̽G OklAXӇBߖ>OtΑݟAS7S~,m#5n5 /v䓧d`<w*_r2F~9J?,P&G~炸 )�WζTczu:^E-M �CdmVT4Y N&#n!C9qpBBv1zBqϜiԑdT6 +SO+vNVt݃%ΰo2eH@-ǺRsȐ<M:4l@JuG-.KR97u{ ¡s$SNڏXY 33zZGz8)5mV(GͳT#{n\m#'*'2ʯЫ@m9%;CgcSO:STҴh;&Ǫ|L-I%Rqa~ =}[%t[\GيSFԇ5M=qa;'C}ݮ9.p iP(Fd{1E1[7uvoN(,e^.߭څGXy=z%nsǟExS9 [FeF\""(줧@)Է z2yCRΨ̝eנ'giZm·Qdk"B)v;FY�58ⷛ&h3n6xAi@C't:<h(Z89UaC8OʳmUdZ~"خF hzLsD꫍} 6b*uQLCAi6ӏdzRAW4_]Z> 4(UY[Oau2JA/3O9\][jpfNJUZ ;�g|+ 7= {O4? b<֒s⇹~0U`MY} a `'ue(q3UߛVrЌne%w | *ն7Z*:2u?YzX}X))yV6~J1ť;Q:?ÎCc.:ݒRJ8/kb[-!|NOݦ%69�雌Uc=hu)wX`4~!h8mD(!;GPlF RkJI[;^{B,xb6桖Ύ]Zr: <fluaVy.o,#㎘r ]dMFN�Qb16 #$$mAdbR)_�+'2NU4UYR~x']?@uy$6H~Q=O}lg{r5+A?= B%Q�TG[G^0eƞHR\J hiHC2\p.Bz!#PR| N6}\iF6fR5RZ~lM]e/fv>xo52JKcPpZdS<3i|HqDj:zj'YR[uF+O'U:IJLLmzcO%bsSEqN8iKN'G^q%'݈7-BrLV|7QA>F>u[M8's&[xceLn$kԥI M=32-KTNZ;cf\JC^S/h>6-X%^:=BM[3- UىOAK JOs%Q݊z JI(a[�'�����!1AQaq ��?!- Jh�-6p|?~B㎌an:ݑ iMD%G^'B't wfz^pӶocehpG8%W:uoG?pA/fTz.L&f<WvD!0 ;8| Q j<g3,^GJ`Wn ¸!\V"' # *? =,zpYOF+fTE]fh,9Bw|^NoKsң^qBaUi7'1ʴ'ΕW,m6JَO^;�T5T{;$lImGoRVuNޢl utk+qEܶ^`^]qNn ,�YVq% s_昫V!CN t|/ Jҟl,riގӸb!#nK:zp{9i|EY/ -3jZt�e.ig`M[D]ep7nnnlL\G>+{a֢xnz@v^u 7p鞮zֻ=pe=lT _[_);Ǒ%gÃ#0 !|@: Ѝq&EwE�t6lbEn\a62T>g5\y <ܷ4;xbݺ/VÓD&t [ʻDֽ(#}Ps"j&V;\cѲq^lVf p6.sw-PQv᭗B6#.|m<e^6GC t/<C>;A?N{ Dw!*:)ipob=P])ǿ>.g,ey~s1Fr+r.!f j3̼!102WXwB%G-GBSI<gz=vJ]Q~ ^L=( ݮ৮e>*W Ѭ^+酚TC-V.'%m8]PWϧ|Gv:<J e(᳂hc]VB,|mTUB03� >)y/N@>E&HP._ʔ_篁m+[0<fXw8y (%;sǔѥīܒ@P"q:!l۾dwxyT\LGYF335U`'*1XͲ .%a쿢9{|NÖ}vzB%G-iHхՅۢNu9Q j5�S/. N<:QUCtL8%e1%�d*R;'ct#3s/Wя@aIb}_f4; \Љɯቷ4a(Eax=2"� q⨳bIu/ԭXv{`=ɐktmyq66xuh >ZLd]C(;갼Ar�"ʁˆD孮H{JI#}| >Ku0\|d)Rꖆ%-ˈOnT1 ;Tu%X�u:5^)[=pTD~C?,� GK]?0ݨsSf0hU'=$lG),JcޝR8zjkzd:`6F^u,r jCd:/8Oh<XI~xcL9o,zR4Kʲ ̻,%w֐k(OR5' \jj\gJH6A16wh1,1Vܕ,v� uD_&Dm.5tЍl}g&lڑB@Kb jX[njww]e_1oճ^❌ƫHҧfOR.?S�z0¥@/;vgQw)% j-9 ̜{fg�=r�m_h#T5{+qP_{䵟#|Z/s)V /(Q,j㎩vUR@_VϩR&jrWpD^.q+;&uO@61?Kzu6FGxy% y6uBSOb2@:PвcfUIǻ#SRLzSA? @;SQ,Ȗr<_Q�xH{PITS#N. uz")0 lkV7\�tS<ynQu�aX!tqNRյ#,#@=묦UL ny~鋈t6ƴD oeu)7+Ynf`<ep+A@͚f_q{@i%n/@08]њ32ng1v5wof/fܻU~RXX74+0\MQ y5&-5Z,+s4�IHՖXR�<Ķ#jR_6_b0l чʷ LŦ-`"^BfFll0_ 5=c((傭#`'y_cTW)�MW~KKJS)Ap^x)K#f_lRv' 펗sp`ɽ`0A:b64D }į/[uǑ3g;%Mԯs(K<x#Bu+A;V PH6E7wwi0z]~ 7hb+E-?sP kmV`ӺRP%=\1}-vJve*Ѱ9 R8UbO9.SN-vcrk͐1nit�wgAvweamLj~5oijhsF"u~s0mW۱ĻՐAXgW8BT ;0g*b*N+r fv3fqn%sY}/A[|1aUӣ/PtS|[�Z"'ŲJ_M\Y-P2KB D>LLT'³ .�jqhf`Ϧ]ꬬ}"A/$<̮HP`�][ sO4O{c][.ɿ )jA}iU=JX/_d 괎{)6i#o~`S/Y\,9*d1ȵXN]2GCq)˶;c{3(pEc!ԫf:iY+ONu4#"#s|[="i]U/zc'щ73"3tGkb[WB8%oQFHH"`~0ie^KtEg*)7d�>b[76-7i.[ZĮlNܴ//: N 6Ў`V:_yu}L0HJ|acEf%͐;+R%�P\d�ۭ+v5#�zWH"xBš2 LQ}(<^cTjRxr{=n3[_Ŀj9HT{la&&ӽY{[~v/*+nl�@?c }Yfߕ'}R9>6tҡ+#*軍O ȶC׽nɯqU`zMQ>Xux}#r7+�6?t55MR𝭷 ;"gw*;4amZ�H?%wae`Ee}띛Զ\|_~?fp5gSY(:<06s3(#L}eꎰ }x~Rà*5~'}wwozm+ldV߬a{rPR*`pXO&Sm:"2TeV:,�}ngWELˆi]!r X{3TZ=٠~V Slթ_`]\'Y=3zCeʆs(Ug 扣906ӕ< euj l Eoyh;f#H* ڄ� }� ),yCWS+Ș"p֣fXfsnfkdi'9liLK(H;*|<r ʺd }C~�ng6<0gUǙ8+hhE#_ke\kC/X}Ezl#D^g48&:>f=7Oktz9ւnw:wR@;+V뻗]{YȲV8|YЕiB= ҝو㦕__USe/n v ٗ3Wf(Zn*Wjr[4k56Ƨf],N?[mdLλ!F� gbҍ Њ`19+bwz^5:!p:n3!%dbr`2{˚X:N?l 0l0�^g eVR#So{ *q ?KӅBkxnjY,HWUMPSoL^#ywRz{&}W|e/$mN*]*Ñ�XZ1\Gt[jhFis*zfҠ?yK'_ֵ􃱎T "l[^ꧫIO 9z7ɟ(}d4sB`<cZ͔Y˩V#i;W{Wx5\=GO 1^{l1TEo+- !y(_b.p�W⾸eJRɆ2=Ը sO:ℼ6ch}62Y ?㮭;<^;x>5pyfNJ*~nM!upSkbLGWM{~[}vz~6yx]wp7UZW^ê"G A]; DKggmH̉#&KU+@jݖ[)GvP`0o5ܧvQ΍{[m%|Q,:֮J%A˹Z5̔lڭnTtJu8!pzLϾgU*Ը4SVM>s5\Qx�~HCW;3 9%͡ Y-ޚIp{&s#x* >XEue<v}|@ۊ.sP97\[D3PbknQgPiϦ. ouyB~i,P"<;pV@hiqI$BLSQF#6:6O)췹/7Tሖo4&+'tjJ/0* bn#L1暠z&<X/Pta|8Q1ݨ aY%3^%Ax%\܀ed¼K%1jVfF_)Qd@o,z ,.]y�.ngF pgtTJlg]9kCc}8:_1 HAlvuI~ar}c7FR`Лj\+SDޙ{HDV8ĠnVfd{Ӻ 6If~mpUmM+.XQ=Z: Ey*dvD=0F(˫M]ܹæ=�!ڴ gw`wVU_Tb$P0w,iLqX�\Xf[$ssNTn#)Ug>f83/.SyYhg+:&5@U^!>U0<o;5/*kTo&BcqNtuY Nxj N`C?%3U]e`Ds, Yedr_LwiMΧumA!Էn{R lШƪgJ9b+RlQ_yrgNfuoe5^ CB˚ѫ_. mY~^%t "n;k CK�v=BX* 4'TEҊ`%Ys*!o3Qs@t;(tHaZ@4�.Y^hG?]&~&}~cgeGxeSfTS-wЂH YHhRgjJ#됰/[~b8C}SgF%u#U߯D陡y4g̷lju�~g|Y폏�vBȏOl(?Ǘ-lf)> vy|Y`7Fq@j`^i">h8�+�؃\z1)]93gYz#�UtJZAys.(1fuAf̿@[; EuhZpu]+S�t4^l΢zѫ�H|[7=YF}{N2(0`ɿeEeuW &'~?rΧ=1 9zDͬ26glV_g�>*w|mxMu 1la#9BINndGco doق^\2p,58se`$yN_8)V7>OF[ K (̬!e>P#c?v%ev/;}S2x߫+`r7 &CzsƳN ǴjR-:„%A:D6�Dpg QO3SA|~cY ; 8fΥMq@ln.~X ]5FTImڱF\M.t_dTZKy0Yr+BΥ8NHc=kJ!^n@c urZʗw2*eѕstˢWB?(aʄ xVM? ѯg1u6 s3ۭCD$+o$~V8C.$Hm~ NsΥ1pri{ϴT_&&Z]fYKC-[d#vX`rU.Q!{t֢{yq�)pmL +!G9^ۨWjhSRc;u~~|Ƈ;$?HZd&smo?,DipX tessfᯒഗ�u' *6L3]<m/l SL$7|&r&V'H@W{ı]0^D > 3xw$}fWAn+_Q1�nX6E,m�PJ4vM X^0_ۛxyf[zl~m�!xJ_nTUc >"f5N1<l*HXW(Q\gk>ۯo9 ڑ\ # Hsٱ[KfC.d=/uBJ& wAY}kZ+*,>O!"qpT )3iK]Tݛ"�+0\\f<@ʳ@e\ Rs\B`fFa�\I;@q ;ӹ[9?5Xb$X30]5 C* m3F,kT˃ �7?-QgCpq8!c;` .fUudy 5ю\ko^i_Gc]63'dxnTܦ\`ʏcC_p\A:KDXh0 \VA-<O� �����ƾj0Sly- $cT,Vl=v+U,^"@b6۽QjіZ, ݹg@Γ١}p^‘R 9H aȎ}t?-kόp+c?m*[Rmz+<z8M}79pkHuӸ 礥!ΐLqK}{|Z\*{Ophk숻\~VxAa�ҏFdW N_Un]vdZ9y 6ߣmARUrϦZp43k2^L&�oTHK\"$ bR#iu%l⋿�&��������!1AQaq�?@�E|5SƐURE<9<1"/Jgr/E{Gdd=`pߑdW-ħ=)Vw ??#n&wo{in453=tcCkk 8N; /2h4ү'_0`>yQ7j{Wzjvm�OH =.RSvYO8ZT /OhD\#Ma0>% :x'8r98,U<;WOn ({D\pcg4V/u-g=׎6XX7zEs l_1# 0~]P֕9~Tu]'; .;G}Q-qڢ7?m@�_Hv _&d (YKJy|#(8_6JA%�b1q8yd?ϓ(P*O]3/"^YħEk^yar|#B1W.f1NlT?QEs;K>28˿_c25O=GIɱM{GEbQpRmY) W6yC>'18ypJ"dÿZbdcIQZY}~a*,w4|}:w۟( YbMwYSK/ӄG}!'r q{jm+ޓ* p&{(9,;W8!Ʃ=tJy�J\Sy({<5KyϡVw?%*S>Pt#+okL�fKߌt;{6ǯG;]k L| 0[|}Ȓ'3Ov~/W{^+O>tJIwK@ukJnRpKx+[,5p(ba�hz~}�pFIqU<6K;DMP>>.(Rs4Wp}0jSAyo�:QAYjbz![1e|p̝GF{Sa/o8@San52ps?ҫ=& fHAsonۿ8$o~i\AGr�+ĜE]]tt�Gc H[Aq|`7f8[g<ݙ+IC'nO0#gK鱧:0,_VjWɧv(Zz&S}"5wJl^r&=JUۮw|J޳46}8oļj )m-7<;g 1UN"d SCB}2HsoPLG�aњX -CP "at29 l55).s-Y_L k tR. (g٣H*S)GSspg4BCу34C*-k&#B]KPAm /7x* Sdʱ̽*.Dʹx[i|g eU~rي%ݴu.4˵T. k 8Of )*Ƞ&a&{\w/pdFp&sj[澱S (NOk (*I u6KTFT<B``K0<5r=̯y1D.l[L~Ipph41/nמ?%u:.%^B S%Uh]5tQѪmB1c :AvaiOEw�eC�-MÙ|*HwnF* \<y<2 b E얞fj'Ʊ/fD`%&# �k*ҌW\b�Rɬ*\w?0:Bف]I˻p=k~aU,q7NqI\�UV,j|Ϩm4xLR6W-勢`Dy, 5}󍘔TTD:yf'В)uTAm 42j�9iEorZ~iL40T EaX`j�Ôobpt ,JKXߗ˹XPȢlJkJɎr"J7 E{Jyqal ߨf 9&!wU6 ǙȲYN�CoM]気5SRV3( C9&Ox/.6ÛFzUrS( 7<y`(.z8,'Z\5q>bbDtNȩ v"̕ KZwtGng$TYw� a*!Cuzv=9Vm+~Oa+\(�DRl|{Z졡pVʮҘ_;(ڷb pIW T ^a@jaS+|?]c�Y>nkPS^{ ƠZ0]жc3b+\,nPn !Ba8;P4\?PV"Sh-FFO<l1,X'qh� wAuNdi-DA=lGeqOV]TmqKe�"e_ Y!ԥqܴ`b }Al�&��������!1AQaq�?k2gwI@�>"܊C8NRM*z~^/Y%XE9>#~`] hӴ#xN& 9hWn0pcԨJ9Z<.x7Ϟ:Tz;ˁ 3. [3wX~eQqGe(<X+�}e+#{/HKr x]lVkїt`|D?w7s6 :q(FӼ GʴC"tAPaaNU-ieNj% H{<F%jx#yiRWlQ74eOe:%v3c@;q,@Ql0�e^s'6vOra.=7J'GLQ&�ho*Xd6aļ{6 el _Ȋqq7(d{#t1)M^}fј5xt' S :?ojBK>bVLw6 co(W(YԪsu /iM65IA ZH`<A%P?f41jzéyqS#QWCNTThYwnHֺˬR |1*·EFhzjʠ|U1U1^%ā.)\n }u?c5+ i^ ixADp4qǻ.Y9B�LN֭#lVEvXJ;[яoŹQ(@dq_u DU%)�t甪e) sJq#=#!2Հ?`ܳ\缡mAHG.Foy5P="Ӝ6u{5PScu:\  tG JuZ6ܸ6|![eT'KZDTH4bz? du^TӋ2뻏ta'Q=r|z˱4ϼhàjж;{#Е͢9,ņK& ]q 5CeBK)_lT~<f+/<tPG4|#~q)wD;}e Sm% ra#F]qJ#Fж08ǤT {hsF#WuM [;^;Kq8A iqxʍ|k>uQKrO+pn~}ǚû?)Q]ڣfa3f -)bb._4_|`cdFefդ/>n�Pi{%c0UY-gCP9Dn1*T\˂q9lkpR׫H"at�2؂;VUju+TXb-/ b@%~J�bzS#WɌ]JQ0XOdŘt;'IwhalA.&qPF)g]8Uʶ~^:\F-[!yf. _uelY׀.U:xR[o& Wwȇm3y~�C-�Z4Se2c ti. ApV�b{B 9Cen!b=ŖN9mq nރW7O 8s!z]D$uSJ'vMb h|sZW7"~3:TR+JV\q8jk7SWvnZyESHpH >V֣w~>]1ږ%'=YXssRxlMRg>܍RJ ^9e9sNkoa,\�'&u8&^N8PWai# J +hƄZ3n\2ϙ{,=iH&4YʮcI DžChR^TgnE]kׅ h뵥VB2hq|DI@p"p-ykq:=/%�pc:!jv_ F̮MŜrou(ST?%V`nRҒWE9WEԦiBdh,рv1Z_xшĹS &Q.[Acg|3Ѓh` gS>R #FBK!J6{C-`刅AC+H+Xo dMmZĄCNF9Shޫr'UƪkcHyf vJIV?󜺝&"Tbu4i)U-v8oDX* )M'!U14 &v+'V 49:96J=uy̐ɘ. Q05W*90\HuxRA|O5ixʪ3:6\l<݊t_\Ub qOFl>+o Q+\}s+x䎆G;q|pJŰ} keҬouiЏ2q"Q|TWө1NczN{1C�?⽥ f10ñ*f+p!8�N`^\EeBuŸ=Q\n2yL<RKq8UUer2.BDC`e�<Sրz28J=|A͕D\d}QJVf(mʴbyٮjCPU׽J@W?55mqFlcMUըfYhq yefTƹ"U ̯p:^ڬ"]̍PLX6ҕո--Re>b='�%������!1AQaq��? p]#M#n{E6q}R6b}S�-my{O_]JMNŪpB/<irQaP95LQA&g'XLa�ɼq`*͢ZzLHӖ6|QVS&4%W~p64)ɕLnMB`3ne1, _Qq4iҼ)ZDn E*kQ&WJ"'cNԻGg6f2-vH #I:A.*+[ )'.0M4Ӌ)-4}! _;*:*G)k1Bc+C^1,6�0RR$xp>UӅaOO@0A>mhw<b}eΉf*�<_!I4O9\A[_%C$H7? a=&U_oY3QX*<9{Hc@8 P�[ bm5 X 32 PwT˼2$S+յVMI %" :ec{Wqۅ^@.w󁍺 Rx$Aȅx{/S8o<\eG(aoLu^ϥLm GN4|<\^1M3� pXAH> 4鶺˽rc<"7K N8:j*]+Cy[l|xH#g%3\&ӯ&kEh`tL)GsxRuuV9!0`+:!�AX|*eؙ'0@2#(!h8Qt +@`FJEhE I@2 N 񆧴55}M{[`X\�rNaA^@<V<K8qZ*y>I !Vk!6`s`h $h耸\~ ks{k�}fixt4u**yq+<(0R`A$~?%ˏ!MJ?oWUy<=cG�&ޔnE1#8qHcPLwK]q|hT:x־O8gngq"9`]Q,N�'~pɀ|E)ه'nZ}CXFy'&Dlث9=u{Z(#\=QCY_ĈKDVB Ev(bC:4J~! #Ah_0N?6iIy]FDoc,%բka A<ܲ)S \ 2;[%BXsrpwPBf=<эЎ�AOh+ xIpQc_i?q5 !_f\,ѐt%3GÌB^?x(0 42Bgc%L;U6LZC(ֺ>fjED &` . s&+~qMS CBnhp^h>\)Q]2! B޾\t Zn8h+{7+Oy1Uo~O;q�k #v)]_; bUE?+9�6.m!NS iJؔ㬷yqU=c3' ߫KQ c,)sr 0^o,fl ?d&pzMݫKTҝ.:9Wgww.SZ>ϡ9�-]T "p\kW"pLI!Wr;>1BR ek`f<Ea{}tUF϶ސ!NŰUںC mĜ~q8" DN雉z³f͇дdȤZ 4:uN]G6Cmp^ '4:6903T~8 ttSoc P*x4"KlW`r {ց<i4IIb녎;rX M_ˎ< ۱ DqiA /z3]==\ltOg|eH p7 j~ 4bdGT#ȓ~ɨ(gԔ"KxT^op|tzY[R ku$M'K",΢-/ K'T8:Gt= @1Mע 1G]F�@lCu k7sNQ2xՅP)g =Jk(eCgbr8ѷIƼ|l䏂4$*=R<1V�^�:'>M <)b47`ARҏS4b1WN^[6bzr;eV a׌!)")C-RF8-G*3Mml[Ц� @NϤst(#0 #56h'u2AGBH$+r!pxR�w=;9@8 )E bqo]Zy% C '7xG3/ l[94@#Z+ɛ]EC*>67�{)D8x'Pƚ&vtjm-Q Pt1<+E|gKd<WЍ `&=\^՛D 'Udq:-fBdHSHS|?�!`Ƿk S%Df1#TPUB9(FrB4ҴFFh̆m СTFv�(P$4+ @w<! >]KF1] I %]MbFk0*J4,M&i�sΛHfOI1$FÐ'i.9*(8K ߼;d?6\P,e0]WG|)Fb͝nDXU#~Bw|k[!M7>2; @4h ZHŠ[G^i<&"'JyH�<8}`Xka�exdڳv0_ R8E'1:Gͦ<LzkTpU# h#42ڮ@и<0:ЗH @O+G<.^{'ؤqa1Nt͔((>į7;Cp\%})Ě0zDd'$` [M64VU8"r8"Rm֛γIo}5Ax�uAK:g30e2 %eCYTp"jX?A�OAWm�@gpy$xGG<*+;w@,Й3# ke)FC =)͔q(#A,ax\L4T4ϬaY({.xf/Gz?ITk}μgvYx`2 yGQqnDDT [S~APP< >$9kRT\A8w]'ez=KÏ9>[lްhM*J<Qw7(W8mUD|35rPbH#J? mvg"ܣ7I8}YN7y]8*ÕW~X$EK`C5>j64lP4CF A%.SDѼP/⛄ZS1Kd<)b�Z!Y|$v@p:k$I:Jv=\B)bJ ,cd#WVv�ώo:To$l^rߴ-*!{#GMB98l j 1Â>гZ :;&O%y6Q|PhU*ӚbD\v@/D^gS�6؊�mEɇrXfsI7íDtp' 4:3@]+ZV;X4nu/{瓘A׏]315a鴹,*^&!7~'gmg*?AIJۇCgb�ܨpAa/+JC"oP)QtEͪ$*Dڝ8h9D$$^6Tܚh @Y+1,Q\!SnmF\#y'"N'2:|Y |] JCXp6e:*�BfݙJC_#ϓà IƼSYqA [o#vbP ՛wy@4!xCzЮ/*pO4Bqf7`)0e9bIo |}ou wZ_| P=qGt~p@gj14KNt8L׸ �QN X8(a=(h[l+A.DFDh޿pÑZ^ m}؂ ؋XL=X<RW&a^$!�-TUD )ܹX M(| Kh4xx&daܡ&52]7zN @ښ' z5nj`W䎀dỴPpmLܼ$g, y�W}cE'FgXtRWb1tYbV^W#վ "tglצt. /g[BPw=?.QPWU41"tgYJcu^yb JPJ?:! JCf"Spȍ;jPTb2hu�/ hC1"ajôSw cd!PHr +*\T'!TPop CJIH!kD*Ȟ(]ˠa'vRMb'ZPַ3]q-M']xLl1ƃsp6d٭x]5baz8m^6]8Z燤<< N\/ 6NO8mΈB_Lz-8o)]Ϯ⧗8a'Kz Gi۞!vVH7_l*mb8ʈxK CT 쐱4%/0boQݾk` Db 5Ispې;?oyL] aN:$:xs@bNj8T:V6 jr`8yа><;?))6a)ɲcĨDA8,iwgZB<1)"%lm65o nm-/�V//* J17aVvF 8E.!CQC`+ 6O=cI$>ȋF#cg M(<p9Ș&Pbm}, S΀quɐPAo7Jx&ٓ%i|BEBc¦bsRNJF7z|k0.2CT3�7TK^?+6I;}έT(: ~uc ry,~g ;F|bc6eD<[y^2.Ǟuŕš]ˇA:ր:a4&XR4f;4 +۶>4A5B\eu9U/2<%r�pYPyc"*0nEuxhmՂ7+xBݮ*J-O<eǬ[ɸjoN.P�6#vk5QPCyKN�raς @C#po4m+-S @{*V yA=LU/l%^�Q]cGW L[O #h;K-l]�?}-@^( 7vTXqQ%L!E *d4zuMpenP|杈!\Ly;%AеHD薏bxFш3U*Mp +k/i6Q:?OIFNm0|u)HmJ;iv_58F^w%Y !six֣XlÑRI�oDO�po<uRӀy_ 0*=,4jM֚ 1w(gӇ9QXzK`16l- ЪF*AFD~kHdQ%qhE"7S3Qya!uWаdٯ"pO.C]Eۣ8zJ?ɼT�1G{b{&Cx)E#FOW1m)@ARtAV9XZkM1F]"�>88!vA?J-Mt7¨_Ȑ>06cEEڞ p¨{L76H`-MEwVWKHWX75},gahasE#>[֙ |7x;rӾ22,.5k8&kJ BjoD� ZE; RAH id8�$9*,oPXK*)rH0ZNy*1qpE bJئ2I]nY"ee\8S#{yʆųΑ7 r4d [Z�~[V54j̓d]M@!Q11ӳKA "lYZl#[oDM0 5 @�^zX{ Q}Mc E0Ɛt0S%(ؾ-JHIN I:SUCoTpPD=8h5G";άb^!�(=#Fc`p�ME!._b ijEv  MpSkv#?ZBJxِQZQ4^c`w7?#u&N&lmG BM-y`cHtn!CVyp[\*z-whBSSODv3>>g Jơ ~}KvѺ2q{?] +Ar0JNs#]q.g!'Kt'̗5ЈPܺXaEX^0T}N1e.!aK1@RQ}F vZ8B2u�Ri@ )MY_X#rz@N~1JUĜ,}p'(,/Q<$Grj)2(B6i\X}ۍ!�tIF>!I K8f8( .)n7.+ .'ܟLG ` TP]x@`b`&Q5y8BTyqdIg#pj q*_d(EN=1^9Áг\T�m(vS+4Ӽ=VH*P{R1J9ikCL2L}$%Sh�  (F05�Z =)G&AܓdAf}k_f\(yӎJpzG8-q͔e pYMi}"( B6M@H- jct u)-hd:_$`G}�»g8P-=&}alNdצN)JMg"5]7^ Z#^o;E ǹ񎄆nc M=c], U<KüPXӆ 4V>bT *W$5 6gQ\co-;T 'e3$eꔔ< vߣv8Z,Bv8agPX&֫p VҀ1ߙ8�N˃lFyX9,${P;%A3w7@Pu/nc3Baڤۦ]`% 1f %Sp�y7 u�.!Oα FMɂbv+#m vQHdPȤG{}d{} Rai4aFp5F72BHAЙ:Sź "gACjKD )_Ƅ. Kc�'S^u@^& t�fA!enaKϤ\TyCl4{w_#@o_oa~pi;B1Th,b  ǶL]8P}`[? 8]qL +[$dg1yu&W<SCQ NMii5RGJ( HL#j ,:%  0v< *� BћAB&1 -SʎQF iRRۣh cJKkx;�uAcXS?*r]Jq^&!jfgK61YN jܮi-uO>s]C,"Lo#7[5}" jvWT w,u*[j_!�J1M*}: %X$Ǔ"Е�'R]"p ,h{ a[l"(<+*vZKŅ8!v4:󀩤A0h,4b{%6??I>Pn?:'9_*96]wx[Kv6}hnylX<6d=3Y;RT?Xt<?"VaR3=�׌΢ �jEY5l\ Mj� ˃cIE"77֍kʍW͹yx,CEDz IebZL"k�EXm./a_ N uS׼ N-rGdRB^صںsWNTIZ6Qa~Cz/j TTрU{�~1@p�s1&5#\t6w�JwM8{� '- 1+'_1Ѣ(cm:S5 ~9\j jtkyx; � g)Fh5Ӥ&AW\yQJIN T |p#ˈ768�jW;4u]`<s0v" ]&|pO=@Qg޷tUo-"#P[J Oq@?l_4i|doH<z�gl1PPt"52)2=9C@�T9[tPf`A`Y_{q}p�xlEm:7:*!cZ{v<".;jb*x Ħ\xNbMDGg' | ]y`J4́}2.s1Q7M3l 61?mJ%� c-(UJuc;^>T9YXs6QžHN(b#htaǙb`}?^yfclDQLBCN8 0n\sjJxAF* Uƙ1 Ɉ>TkH,"$D4*17)u䴔yxLg=Sw0%i0eiv|W|ǟ,>Csk]J;R1Q7x].u?<HJw#vC20^J>8߼J#O/92DO{9l{Iа.HMF0AHJtF<s4�T8[p%4e wvnOQmtCU |轣m^�rf>?xLhp}+v@ Y]7VFЇxV=Z! Q baH=i�xD`9(>ƸvAv@$�@)Yufe&dy\>? H|�Usؤ=&ӿ^D|8pH[;xGxyz= 8mo)y1\i(**m)A7CxGWƙf�o"ARyZT:X FA"VpxǧQXƯX∅ Vk7MՀh8$pک(6;=eRQZcc�d�>peXK,Hf u'f͐Xyc ji&7%\T�1">oxZ|Q�lYK@cF3 Rqx Fhosoz_knuFq2|ب="9ŝU}w'̈"P|Jo`F$dptԳct2Zу *CN%^@TX9c!x$TJW�Z 68ȀU䞰_4kt0{|NzN?X}w&/=A7ҝ1RWtVGKCp5t0qԢ.]r-|x'L΢{2٨k4Z2Of#_d^h vgAB΂J7n6Iqq{xj�QӆF"5"U[0:@aM�6XiW6R%G1(6-!} "oorL>M Z^۸o} @/H aXȰR{bZ%'#f`Q\5;JBxqkr<cvhj`�]O9=S첨JQH04l׍ "oBf/cؚEƑ;gqEL+AI̚BщV6⥖hmPgمPU ׌ kA^զ'((d3XpR48U.$A Aa7#6N.!/ڴ �X DQ;ʕRUy$A N\p8i\mzq.'70ĦPM'Df)E0ث,wxY'"iT'CXMuzq�bxEN\&bZ5*4 `T~2M6Mm80h 4!K=eW;އXT/ )DG^?H/儩t|c6 *X RŸBcScjR�А_yu{8m8`HsD/g6H#z4щB�t,OR<g8,X/cj;#vrI)+#7v$ yOd :Wq vlSlwCUWl@Z9.^:2 , j���������������mosquitto-2.0.18/www/files/blog/uploads/2017/�������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0020563�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2017/03/����������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0021005�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2017/03/img_20170308_155049248_33196894011_o-1024x576.jpg���0000664�0000000�0000000�00000254340�14502137606�0027134�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 82 �C�    !'"#%%%),($+!$%$�C   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$�@�"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�̩\{ɒ�㊍o緵G$$Y1fZX>c#V֙)"SCvE[k!4,yU4wHIVnejz".M9cո-eEGn5i?HŴ}oʥ h㈻�'Һچ#uˊ R-ccko)<VnƎ:V6'2T)S%G|ʣZ|?e\O (Ck.vs̴*T&RFtV@"rrk%Մ>v6ݲMv⺔)\@Y3ffF$p+vHԌF:V}8 @LAlcՙwR0]CǸrq�e`$7ASLps#+=GZzך+2lִLU۳:޺d0^1\ECǃQ[$-жF}k:E6GKdt#nFNI<,v=`ѪTTRg8'4v2 K/i3;Nzt'ׯz7#n k!{҆<}1QPFI GZC DZȌ?ֈ<(zd dj%Z4E`"T]:eͩ(s?4đcve_Z%u*A?ҹ3C(el095IwGGQZřN61u-0,v.Eïc޶WEsڮ?9JU/lu*F=*}ySLoQ`_]˜ֶ} yYWޭK20k]p)F' ؎hڲݧ'27RE"˨|2TsZjv?n{ţDp�:eM~n�J/lAz=b}+ZrLЎj"3;q+ lfzuBGzB$}euY <WkizQ;TI{?`$d`y4y;dNB~nzZl|SwPLΖliar-zǽu Э'PQVuCRhhϵSj7O#ڮ#| glAǿE$,9lv>}9^6Nq422v iqU+}E$n1W ܒ;V8f !%HHI$/d>}TYanTFSy 0}�1d>SUu`s֞�t#ޘ$qc$Uub8 n)zzQ&=SL q9'�NI'<Rob$@ATr@'hI,�@O[fFތ7~)@QRPDcS"aws8jP!~cL2 =&4qGaED33m#sWt` �恒I|4Gc=<=jn<W~fd4zS%Nn8?8CR֙Jzvjs88F8#Ȅ iƥׁsJ&, p5W95=>Iv1=X"HYA6ǂ{ F QbA ~>_>Y㷵i9&o9D+[zSB0m(c!Wb33ɪft27vjI`7-sVbx94ANGNZk(B3U\>CEUZR$sfIXYd9T䑻~W8<bʄpy(�Ҫdj@?'8+Rp1^29)8,6ƙ>-\NSF:ҽd21Ҳ|3myP w&xvg# p>_#ig9P:ֱn|=yfFGQR2"|ɫIuU`$*.ǜ� nZE9%GL.r#a=566-rmQiIB;Tt5-�ͮBiFwm2T?١9͎j.Rg`ǞHS�R3GA�l} fCV<�TɚFG>c=_ǠT'}+JM$cܓ ɖW@*T#�#vSMF~Pگ!d`PisQc*'׮*X?~�T(qY2ܞ(v&XGJFrp q6. ǭNTqqs3,!21[cg DҵR"V!58Xvdgjb3WtBj4$<KkgyX#i;`uvcc p~u-k7Pj( m#WI|=f;�iMwZ(*T�T9tNm qs@9Qա {|k�zsClMs@Y3LĹ$wxmNqX3Irw;=& {sL{ ,]RmV=j?V9ϥGqy c9ޭh$QN56C]##vLn:UYcqՒqU�W;ʲU.%HbGȫ3ʡ\U.q&&Q`2Jvp Cq U~=Yfv˱,y9R24ሤ`x9z̷#a:MrJ֍2;ThJT0'�Bב;ُێ55D!FweJ⁑ӡ&W9K&Kuugy߭9yZ@HH^ SqӜ攁֥5 CHg�< ʫN}j[y^GUrE fbqw+,hU#&ݲb_+Jػ[?2cZ@D9ޭkjFL2(<o&Nm6g# :Uy(mNsKrc:2dwnj 9֭Mj\Lprй^k:\F>Yrڎ5hryuA 2иYPOƹ9t뿴ۓ$ՐuY@~iE;W1k o_CWX Z8+Il(r/Q[(Z#W3$SYOv=*Z-20ՐAXu$}jC*` ɢ*9YC)[*ަ, *jTVZiU;XMS8,ֻ[ Rk6cb &1GVptd- ʾ{؋mv) E ,d+Rck: ͡v =Y~/B1 EOֶYXnh1jeHOXl2SϧkcB2SIȱZF7=9\!z e;JE- C@kL'Lg- yBH=2Ieރl.SZDsׁQ\bJT>¢+t1XeH;e_}AABLV,x2O5f �  3z=O*R'\b{csIjbT3b&H8cB,9'4ax9qOxX_ z\6#J͓[Q83@2G0ܚM71@sSۆH^��`~H*E敂䟸ܚBrsU{UQaOwdrR'8i3p�� )OhʻOnFb݀jDWwv �v@\X1Pa\ d`V?1g&df:�I=t,ʼn22tU-JEV1ygk.G,4*3jDuNOӥ\XF~F%SBv=M"RyK)<D0sieS{U o)mV*|/OˁC9iA#?BKMGOqeԶOL{}*҆ wB0.m'pM6呱"<y=QY"'`z'l[Ya�[[H+&KC[AFQԟǵ[K\Sیu3JIXGךC0SG'a98[l`;UOAڜ�vu fTI W+4;ZC!tWBU\N;ԶRE-[°ͺXWc1%thbOˑGZʓcC?j&cYH }^KrO翥S},H^<ۊir֑,wF11zl߁�ڥ6rqEg qA^kyl`ޝ d S>c6(Q`SƝ2OAt_`jp89I3BɞoSQ6WU(�R颕Cm:xOaޞpW^-EciP1$Zs9~SyY41{TMi"fٍL3)9^=҈8AZD>{03cVb8#-(+|(W9ʛ8QR2AS"*8LղfXFH<qK˻R?0v#QX8w gua]rETQf,;ܥBWrU[gvfğ!$x'H@f8ŤyGpj֭v5wKq#>Z6Ȕ: J7u+2R`Be*(o\ܜhHU3 1f,Gw6 N}w+w3jt_Qjrp1}jGd(`#* 5 銆Ii6P1\5RYaզ&=zeRҲ.djܴ79| y)FX%ȇ"uU#v )I,NAҦVݎrESWŒVc@'5Fe�^3pN1-ݧd%!mJ<q%H.:v6Gj;Bڬx;nP)I j^>3"0c4Enuޥ{elF ݤ,FUuKȅ[USOy"b>^HD<bnXqY�#]#̸L3#R"0q# V:j4PɱcnCYZyJN2[�I*Gw,YQ8"]S̰vzںî޷sNg5U�瑎+ĺJz�Nx*hy-f3[ G~kTӃ r٭kk%7ܬ3RIFH0͈.Dܑ/pcvX1eכ*O̾ C(88.h=+J)~ZhU|α,Tdu$zЉ8e(-ZM<GZqm.+J:a\gֺ!;ьD֙\7ՐrRA+BqH1ScTnmRTV6T1uuŹx'2[&Bb?]Mk(Eo.NǟsP㿥`<m9R=p.s(Y9*KM]֞nmy/g]0mYZ\4DmU㏧MepvHAyWvJf;^HN=*>`͖Ug)ǯJqrݻJ9QF=i=ڭD<vs})sj$㝼sOQPGNO֧ zj++@�A =M0' Wܨ}i 5;)L|Ɯ=S0ITIAR+d 99HDe1*= * <u5* t"p%ML) ܅'ޠC5"84MǏaHF@^OpOU 6qy>q##X9HHz�JERܓ4U1< ]rʀ:r6 %cVXAz7�v~laY>>D{Ҙ�$zUxdJeoҋy1#^֫H ޶@I�NPdޢH3sP8Cqӵ1e@֡Y6\i4SG6r`5VU$HH$)~HJ— u#H~8mԝTr,2$*ΔlvH�E#�,pG5w+·g8/4 '8kD嗑hCB#R!{ԋ8F?*\G=깅bq%G|i=j}uj c!^hrC2qy'څ }�S##pqҗ0S '$"uX`^{Tw6k#d=)6γƤkFw0ON{U,dnNic;tzHD㯡Ylot85Ap*]gqҴ\7zV#+ <U8<E4+�b g*M %Pq{`z"t11%eی y=P FG546qɠkuH8qfץ!ܯ�0<n&ˌb#kҰԊnp:gTB ~5yxlӗi+=Z/ʒpJ@pcLVMg֚c=iX#n,@GMo5 s51_16 wc\J�ZNpw�zWgbjkc�֪܃ǂi^2zS$Ku* 0A$3Ӄѷ$BPZVP>6l}Z01"Xho#w$_@?Jr\H~T TL#uېBJ֢A UVm^ef<AUw`+ŎՀ*G˶Y ބdP3| ~Y�8EKjKkra {T\tgVT%94搷o4-s3*26ϗTNqck`9Mx23BchL~Sݫ]F\ڔje%CR䨵!^{@S_bfhބ9+:͊'5O's%<ҥPO^@ sҮ ~fG$*cpzʂ1uB3mT*- *o!'֡0\yǥi&6sՅ!2-Y2)Wu>N冠$ywhp!0vI5øaM)}BA CqϥMʰdd{TN6RL)$B'\ך^%T)@$4BQ\t6SׅfUϲ_>_0(@*rtwˇT5=nsکxwY73&A?xW@1 .gnJ1&<],2$F d:N>azJӒb'ڦ: Wk4rӸHy<,pO&>ctv $I,M S4!r:Y6Wrisc&<{4])VeFG�mP;Lhb4ȮmÃְ 2~S٫쎄TP+k7/sxP2?Zʻ0<5Z:VO$S =FR<LIgVGaҹK6.>˩۹8]qDn d^bqN�A"LmٲÐĕ>ݫ.k"s-[]B ϵsזf;?1w%|g�N7-nwiD2+>Å`yڇlq' *s7b8Y"<uSt muEQ %k)D  2>O@*-x*w)8XHeBݫ+ǫ�SO.}D!o2|}֦R߼\ \wc*Nj'#U&"W r}Sgڧ�"0=@jwl? �xGnJ&Ns$^9\,qڣmxh4>"<mzUi hKrC"m|Ū_*|cnNjt~^Kp3@J=�GNIF!xZNIҘ=2GE8.iXwWa'}1V Qʄ6iZHʯiW{w#Tq�"U ݎOL%laGQ;�k֟HpiHgjΚNÒz;UwENǩ-Rfl&,_yzzDG9jӚ~zqn989=J-Dɳh=L @ۓYpǎl"4qO]J'=Mb(|{sRnۛ-ힵIѺHrW7C0=d\U"rG ZLi,Z93qYcۚ7˞IT[G_JS|Tז@`tpSiKfzҘWOc40H<J 8îzUT23}$9kF88,pKtvύU%p#ޥ`jȹm&c: $S]ۤGSި\iJI(\7Zh:hWfRwwE_B0yeT2*{Ҹ  zTֆWSjr0=8ZqP#8,& <g+qߧ8a 㚳=dr[ޥ2su1�&L8�+6;*~a{  dnx@Wg=NF9=1V>]:�xʊ쭞IL|eLЀ䎵 9֥ >LSIgHT~j6׽qdֶiCҵlԊ}qP#[np�jc)\sFTar4v:= 桒RooJS+m~Z!@=Mv+Iўxs�  ^E ӥ|H|-ӥYB7ZE1n{ )Yq#X2̠Vl>; y?]fNy{UKws5mz1>:``uHAlpZXSe# w@?JȾы!=E9=+6G5ec)F t3ɚA%ң9!F-f$Gz.�r9$T!�=0H4HXŒ'eNzT\3"Qd(⡲"aWZ S3:p%x*XK<>b�QE^WgMqgjL7ߴi66DpR:zU#$Յˆkq] aP�3ҨۅZןPx#ڤbzqLg 3CV ޞ@~ʪIԁǷZduQsМƘ!@Ab1e*&tEOl if͉]ׇu{|t2OZh嘪\PiKqnۥ^ 5sX Wޱum%gL9:j&2kMLWBw0j#i'~ռ?gjHT� GxGǽ ͰOGN s6 0HvʃRRga|ks`nG Q T5i'9#A&OsEo4Uθ<C)@&0 ִӯuRe;P{frJ5ӂ^jݧ"3EʼI{Q} K9`A9͕!ÎTuռetK.U<:j֌=Y#%k]r>"d2u Q 26lO^Cq ph& @1⦷Y`YOSWV|1"179Q`ѵct ;\pԷ0\QUnm=k?O%3[8!j۹Fq3%IևqZ (>0sڠ,ǑjEb]OUo|Yd\/PsY+( /J WM=$ {柺 IښwuZ@>槊\ޫ 0G�S@=HHV-GzAUjmNyҦܾ?.# D 0 izd| \o_WYF0{ՄW#ުIJlޞٟ1^{ՈɜjV*pW,E4;�XJF?(͚X7>H ƗGC/1 O U3L09f9=)Iw)BIAHDU :2Z۔~ҭlVG1=r:Z�lqSO;GC¸9ww!1ҚdYH`FtK�y5Z 8힕[O\UBvf cA "/h_Z}Fs_lU[sPkrCUa)U綔bA�v-s$|gTfV28�,ٍ7|}jo2N8cڰ:.zXyo@:\n; qϭOqz/1ZGÓj- ;0܃Hn&KʠxE&Mdd T2RLI�nqn>0�5Ld5nMz<1楋 wwDs7t Y9:ǜ�8^3hٌ[}*U# V~8>_^qLV.mRğtQ9qj%C ޕ'۴4YU܄u*z\ Wth�,8‱L\ ciX0Kͅ#BTQqXIY^IVpx`A*ݹ4\,h:GYܞp:T#�85edVQ'A1AAH95Upy V%rH3Ӹ4<=8iTy4,8 H I~4�'O<vX*v3ɪ ԴRgΖzsIZ»xIedbq\tTONRzuZF1w'euInOR%'$: 23UnH$sP-yaYg@?θ�4 v`Ĝj-tq#-BQ'ާv-{֎`@=ұ1E*_OJ닺9]u0U9aJDEpv6;ʨ~NQ#y*xi89G'S޴`ڣhޓeF&ut6F>�϶+B dn~c5+8apHצ*ҝ@'ֳM0Қ!jUg��JC+y?0ڄgFb6*nwf%p=E>`U"X)q <cgܻCs4,iv# �Flnjt dbW';Sl#֤qq�fc;2$T IwhmaB&X#N83}V+HB&uǽLՂ@��:cCckz7($=H/c-;<3wȇbA.{WhQWHy.d}=+hRlU,p> ԯQn6 V [i>ޘL Dp5H�cw-�=* :7:k~ׯBd~''os&nzJW1i3֗*;<;Rg,gmF7[3\Dː@po U$dgǁX\^hwiqluB uW<(5N0QX4Ɩ*8UҮ.KS!2⹭kIdLvH}.؎#GL9,EiEIԼ�NvL �dm|<A,WmBx>26n 9!"Zq_r>:<U>93ƴ麜FWClBWkI(b]~mĴ}FKp_J=O68dm5"W|egOJR1LsޡLjGgP|~=2_l6rA5웹nXr�-0|i~!R&FFje/d8\נ@G,?$׵S6{{\g8aFGֽ&2>_wayB眲UZG=GwvV{x2;th62PJ;O1q{b53˧F# $>#d]<c�es)i_/bfaީxq:2pH6o z{W2d GW<Z*u چ*4Dg)UcxHɨ*$j f3N8 TA?AzB4㘒g�C 1FNGZQNTqښ1ЎíMῇm~~2)&^9R!{O4CzޡK zIdn8De=E\Y=ہS کXeDzdcҫ,gN8)e;S) P#ܑ/&C'?#9 3ҐݽT^k2rwpҗ|F˚C <( y"(.H9"S/UޑHMܓ}0H!#G 8NOMD]u8Ic9<et4a>&g$cҋ9(l}zMhwu��߁r}MTpY3E;؆ZjqB#cKqgHpޱ-3*t=iJv,C63yK]:�Kg=Ɍ$%2OOsTrf7<n~{eNFX( UPھ?єC�z j9\,RXӭR.#o 77`: ݏв`9l4Te�wNoBeeRq˞Red4-wEueQRh榳h⡇pyÒ3vXvf7q֡e+g28yHzc#4e_+OJrrjFFQ_ޔ" zq׹HOp) +ҕR�xߩF@=*ȋw,޾[Ԯ219muS9 uhٹqAZabH񞔟f'Ha n9u4$L`px^L?J~;SL#hNLDtPG֕R'5* #z[t I<u=*a6Gq?u.CFr?wiFN8]+=jWVXI5Iǒ%֙5tڳn.qETj=^B̷ 'd=*g :g5!v\{PrgvՊ*h2Ϸj-Fscߵ^DmPkQ[)8ZWlFXlp1ϽYkpj$1ʞO%ZM.1YW0yݰIvq$TI9]`&#rRiS1 QjN܌U7CGsuڡȵ{c WކNpV,`皲 gPF �#?JAAltl,]E<'<B #;Tg$qҕˆrN(~\;zӈ`4�} c)(P#O2p"{ ֥�=)vtMFrGqE� cK$[eo_Q€2LT ֓ E:A\OQk&$[0Z.uDAsQ-X@�W]W՘Um+,KU3EHʻlr x5JG'=jT /̼j$.# PsBIu8Kz:ś!. @Hπp9> ,'llcICO On`<c8KcwClqXTЙaVfR~to: c$M˧'?Zܱ8C`LqOwRB HpO(!y#H϶7zaLywLBq=O?AC ATM&q`TsL�vU$ Oc!NG_Zdس 鞂,ǵ ,�0#<o* OǥT_uJ.0ʁiN U:`~UI'Tw4y@\,XP0Ɏ4ޫ]@űrŕwUVtP6d86 &127�,ZJ!<U |J:`7Y-܅OT#EM&#y99QYk?bp up>-\'95-B$n|df=*oU~\ k&^@B:? מx=H9A,!gWv&ZW)Sȿ�F7|:]Ce$^l<2=j[}K2+gqeGjORM1xM'B&Qge)l�NVQO2$s$nVb�"UGF:oY0k@%;8eFO!Si@[αR<88<c zV'N[;>ʺ5{H64/A94s'r 1{fI•b1uJHLHM'Xbv9?ƥ儁U3@})XJp3֭sn9Tb6AOJ1XeH=0{<�dgzY:sTcUvHQ{ԋ' Sxog%)�csTKQqM߷$m#w$B�95HE#+p~=3R;rHB>R1e,S=(c; Tf*>qe ˏp*ڹx=9e"v37_*H'̓AHdiX' u4aaP"c�O=MNK(v_T矼{QqhV<n =IgDNBiGLB4\v$>Q@Oj7f\(ޚA�)P=(49R0 ?cڱt… Dv�cS^8֚dbiĖy2~ncFӔZuEp u;`9-I˗t6خ*_/i erX… =M" YPE'LK7yLsU-xt3l@==u< Ξ7p=}jͻsIHI9Qc9=> 1X5HϯOZz;FrrF ;V8صkrmܞ\�~U19#Hn sm JFa2xT#`3ޯ*L؏`iApAbqjTwrW$0{Sʿ1eyp}*FJ :1NFO4|'?1K 7fY:֕S#p[ @( e/#y8ҥoa%8_Jr;S `rܷӥZ#2>Сw h ^[ @,x@MnaZ[;/^>nQ' Yo}*T~S�pj ޿ �i2J rwڥ7`e.0pGSVaa;A3Bv#jhՅW\t^r@B>^L3S�NjjV@}#mYmW;֯[e>lZrjLnNѻ\[e:ƩnF>ZDű5g۵;jE�(l֪¹RH7`}jfnrzZuS=:*�ҥaMn@`֨m31z涤EdaTe%s=62LJ2soJbo5zx6Mp}=*.h*={ӌa@TXh#dOJGrHO\Vm9aO>*' Ǟة⌆ yzW œxHpxkFILWjsE]00?.7r`IBONzQ?)i*Xt$Աar@<'4AoZ�kg3wTFi #M֘\HqjM]ĻԷȮV,7}JqxIM*(70ɭ'xZҫ�kҊ'qA1Ҩ€F7Wmzn5B6z{dp$T96#%@Q$�g$\6?6nW0IvY?J&"FF�<[bO8Mp �sVwpVx` |q9`9*xOZD܋БSBsjB1 5ZI@X�9bnOdCK$+?z ˮIdMC@2˻z3ګ8j< 79̮3*2y8Aսj&Q,xɪ<Ā<1QKv1pgm -0ÏZ(8 ɨcꨣ8 $�2y%B#. mS<³& Re,y'W70 7|ňYZŁڰur5Bd|W=}]3lFXqۊ#XӹMƊ^[ oȊؓ7Y~q L~PZi+3C5.0͔miWWKA(yYvuPX`#ҡw̃yjRYF0D �S uWZ@[~` Ar` iIS/.BAVo4&  °#ըww1QH0?ά}޵\;dUmƬ[ٔ}皞8"u*9I9;�e,1ci%vs8o0tۭʣqR[lR)c!~vHCayU>BaЂ^ªb}Ყ߭Z<:wRL?{! EԾsHS=ǭy9W{q+s$Z)΅_zPP*rM]Uϖb9%1^`({KDא&r�]~}=ҡ9�az}j֦ٴ_s5QbӊҶfEi-֭=! NMAڧ9$tqV |b.(2/;Czާ$%9g+3R rS۽;hᕗs4AQ5 w+.7\R0� &R:pQ@4�={~qE'Kx�ej�0H0T%N8ޣ}L.�a$w4F�6ڛ*Aڱd2tMIٟ1JùG܌(…=6* 幫0Ϗi2b{u>f U{ka!P4P8VaoI>'9ǭ4`c5 4bRm#Qs֛a0dZ=CLv$g <m O$, J2zSU|:O2 Ai~ˈĩ€0;d,`�"xL\r+mZCДpF NAzԧ9jnT< i447֞|eT�O|V<24M#ӵtG}F~%PlRLF?Ve<v&!|l0 dy l9jh8d֊Fn!8Q<c g;_4N+ fgIA>lU\:�N윳zUwxe^ Uִw*aU1C@@T H[f`>y]|F*7IҥF[s17p=)cc'�yaHd&OVۂ;D`/R}i} Z�6NFM( 0r}j/B �\p(N.dno;9n~2ޜ `#�#761*Lۘs�ǫҐ\A;bK#R9;#: _A"eo-~M TB6�f7Ht]V�S捤0ezQSu�(dy:nN +Ҟ-ZjւE\9#ji 4~l/Qژ l SP>YlsZAѸ l9`PvIoJu?ZojF{qY23GIy[@zUY!0͚S fnT6<S�))r}qؚcE=F:{տ$�$EISP k:RRxh eg q޴Fۘ֜ϸdrGzEF�ԉ^&,q>&ݝrGAoJo4#ɅPܩ=N 8OH_dh'#"mG֥J cs]|7cI+H簯Wl͆ .s[UvE[j"*aG$1'Os ĜU%RTv�<rG#�*@H>fk6Rp;@9׭kX$|sT"ˍVTۈ,+}OҴwmv�}6Y8Cr@Z3Q^Ah r?Lc2�rKsǭNe޼>gcK[[y( ؚ|=;JUI*n}M;22X`kcej*pRJ <s@ỳ+?x! %ˣleq!<D 79޼Hi)*Lw* n�Qߘ6wW�n�ƑpwcYb{,2I)S& n�UF`Þ0X呝$1'#pi4*�%G,7Ғ9P$U&(jYN_2eᗞ};ʎpdީ%pw.q` ƼcXM sTZ|jv03+[~Ro.䎕k\�9,`rNyrTy͚V,I$rkJOyYNt+kMD;^HT6G kSw’.-Y.Glɞ@='*61ObGhsј9`P&HUW7@[^J ;s-z3q41Ė'zx]m1vezafD S_5V("sUp IO=r*lAc9`?3km98w!U6j\EDYGof'�QJcBr>6T/<\F7p?.bS[Ö9:]CYDS$.!;;o(.A=T> 7N::zDٌEH9B:e�5=nNC~�*O�ə$YKy8zU)$F*3ҵ4�YR9,m#ԞV 3WG=kH0M]; s4(n?+^,f)pMxL:%ַtܬpE?oyҧf{D*T1�aR<1J1ps`xq1�}z9)8 DrɖT"\  $Z)CcT Rj"D<7P<˷<;f.66ՃhXyjTd,Dᑰ}j{$M+ I&v 'f$OzCe{l~�f:`LEhuGW"yqQ&0IBK±�d`Sud8op3Wcu*Όc$�Tpjx?whrя-RA�p,suu(~5E&?HUvQjIH *1(u 9<4Z+OGښ8sN8Hc8zE t-L�is·ι$ש3�>N$6<\L8NO#\`=ΩK `9sҤdb::g'|Lԙ:@n8#,[*8<gCFfIVNxS.Hm'>5F gUUrS+ᔯb94E��ԃyUnWgUr# A`Ɍm'qN8oAI+䌞´R3IWF,w>ޅ ZC߰l6z1ڟ vy$ NXN|D^HB !�S$NjZ8Œw' Ն x2a>aai 9'9?X{}@jRhؐcަW%? 2% pH/c. #ګ ֞*y8֚dXe[4=jd5#bA �H?7!�Z @Pgmÿ *B LW<54�9:*9HA2Pf$del( [;@ϥ+6N{SG-ރa<^ȻPQ<dR݊W�b U�)IHg=ϡY[<Y([^*9bcI[ ugCcgWhS~2C!ڌ +&E/zg5QpTvAV=:M1 j€Fhqs)N�i\\%BcRxS al?cRI�aMۈvG$9JesLR ` 1T؅X%@+ֆ8jEp RON3 {ڥE*0=*&58Q 0b=�M c'qt�elܓ]#drz,.b;4D�u'#V2'*ę1S0&;ۂ~a.rw`4�'PĞ:.0~.<ɕBvث7<sVy.Bu'T2 L8�Z@R2Np1Q9HLr;18Uǽ3#nb1^_^T{' ߭Vo)v{Re$:{L1U3\{>oA,ҙ$`d6M`ovoq=괲o תTJLD9ɧ÷`޴-(* ԋ t QLD<}jΧV9#QGoQ->`A|zpŤ˒F3V#l`GnU+9�<d4 {p̥M. şy\7o-�1=+'PY<R 1Q&irv l@Kֹ+uX!G5wXf2A ~(9&vSrӫm믃KOkiv!p�9{+SlaYgO)1pGCS lDB$׎i2)іŒ3]4:fJTS8m:,Kw[N*1 {V.ќjY1"ˢ"V\�0{(zgmmDhGl-g= ?SӮF,JF?a#qU %'ǜ7șT\Dழw!:ff}m!2?PjHb#Ҩf.$XLv?6ЉO޴ }�yku1.rJ$RT_6aUĬD*ri%KGH#̌ Y6mI1(`sD1:ʤRzVZ&i@T0i :ו*m;)C!ɳes y皫,Jbh>E(Ÿ;~OU|9m=f |To2+H̗|J0o0ִ/T?::+-kk5w?*. e2A O+^0Q&n:"�E+_{kc̒=*#*܌eޡPR VbxUn{WZoa[qHisꔌ.a\,9IQ=j; HYwnn`xxXȂ=tTŬ˂0xcsLɣvYzm"煰zs0<zB<v$`A )'9 {e\�v"%m;wbV#p^r98M"D 61qee-ћߊ ,#MQf=ߑ8cn *LA,x�h1Y>E?3u89:/'. A'>�(R;�e 펴ޣ<Q*3 2@sPʞ[ bE9[o rz)XG~$I$XD<D(29NGRSi9|1Z1gSW&.6`TQíd>M'AP3)$JϽV8Z7+ ($; EALcSHlЂVAŒqR1 �=jo!\:SD@8ЬH�օ;gzS3&ÏhsޚKuTG1˃Ȧ.%~4큰2qҠݞ#T<R2q898G@/!{ ') n^1+ڨ[x`U ~f=�\;D8*v`4jI<EDq=]8v$|<CjFq ܎Ÿ*L/-T2&IcvI9L70=+9I$u5:KpM4hns; n8:-$R|g+1ZX$gPO&+Mfz#dSj>X q5<vqPY}OaL[im=4׵*9Ì=izsOU8E4 � SsCsZQ1?:6DpZhM97) Қpi"a2_ǭA"1﷊x l'?CCE)"!V��:H Wl?J4[3ɣD쥻**q3ބSa1Ä''ސ8<^s*8<QzsҘ䉊 qNTpNLcmO#`8(9=.7pV#mL/84̹^zRsҩ *ҞNF4" Jd|'JP=T$/NgM\Db+H+$w>|աs+,ʫՇ͟JaXX.6 'V<uF 㓻* S!'Zc#8d "!Gݦ[r[ '<kB 5K(}QzMPqghvZ8`ВM<ƊS͒H_Qw[f Aw1#j"?-Xj�?jͲЅP$m &F9'K#98nwE!2 'k`hA)X 6 ÌmI $Q,F?NYd# sy z}*X nZC$o65Idfgq'LnrP9^2%2� ZFIHrKؤ|rV%e(sЏZз݂c�QBl)|2>\ dNF0"hC,g,�9]\ v"IhB;ϑ5rYb C6Q%k[~XKԨA>ʊ~w2n$c^6W!/47o_΢<e -0l.ܱ9ܚ\*vH; 5O<fhZ�zvڙR%h\J)Z0 >Eu$ͳzUش}ͬ�� &ƃ-$ HhBB P*e$Aj@j/|2t?:.R%qQI6U- Ke;GL* qV=*ҦXȑq; h08 c(0U\OyWF_G+P2c˟jU #䪂6 ԅ 5*Y00ߘ%Q B֌1J"2c5uhL>ը#)Su4I@TC3 q޵.# Gj͖,Iأ>bFѹu Y/ZޜȜnvv$܎ƙ-TGTR<| c*1]Еԧfmh:basG]}țLyد.%9ں..btqdHg]n;_,s6zVW;'S!K=jr;v i {5ot>V/֩ѮʳpUqF�G1bOj7բfR�P;dֈQ<oZT<wSLh(ǥM F *'Oe Qur[ȱmt.A 1!#Ҧ/F:VLGl>hr8U&2BO[' 0: F(/V �zRX^X{~H&?<ADD7rv%zF>Q 2VH' ׄ#-Hg#)XO,2yQQ pr>%�F'p3u !q2I44\Nا{arё ܑsS/r�Uk]8դY˻jC&۹z<(qÑ ;O_j% zй mqTV<Y4lnXc9>j6d8�cwT|R0F1(X0٦+|j'n=2iNOLST~tH@۶OZksb@ \)r3MC)3^KCĀSj:ޔg 5"\IAciB�矼Ơ3'v[AVC:LU>v*h0{V �Q TWPysU1yc>Gr�66;1S5 epBzԊBh `GA )hʌ �2TfI 6֝$c@c*q[ߵ"G~]=)0i:h; u 9�33`xiˁ4 qڧnTdw6/J@3nтI✑ZUV ]�x@�0*O>8 9Lrg +׵(;O!$QȤ0Td c^,hA,qT3D sڮ*<= D-ޡ(3�pz0cZ�s1Q<@qm 2 <֞>�q؊#]NJ_Cr͐j7R͹>Zpnr0z!g$#F8"նqRg#ު :׊M(wNJ>?O/ Pqv*<)l-0wQʣ֗v=2qYw &_wrzsp THlہ'�ԑ�LRNwvzyH }ePM1 ʴ8=jI':dqU|:jR3 Y84e*i-';I#%PC~vH#m=Y6j,j풹 @U`FWqUXyfiwYgr@'֩I+a3})&!e[O08�$AVYDG(9p?*|6[v9b7uyKXp ^{ hL8UU h<<m }XTVwtP]8oj#6M қfY隄(d}>)q#T'(qw$Ǩ&H;'p%{V-ĬgxҖ`;YOZf_* Ỹ.:~j3zJΜU'ztK5vLp=X]&o3OqMԦoLżcbǥtuFOsK{n}+^�=;t08YE&89b.3iU0sBjs)<$W0 EM x'ԮZE}ҧa) TtPrVH8 eg~5ZA1VPȠ*n9'F'<UURxsK%Fl9nœy(۞Ԯ+tڑQ)צ 5Jb)96*rO#YsTrR*5FaҮH<7>Tμc=j ^o3IWlmZ)=1T95jV V ]sUї(wwϲgzfMiT!X5չN6;+{a夤iC8  _C+nĈixTX'RыGI ƴ!+ՆOU;sWe !2$R{V*2N93‡[Jֶ,xdͰid2*rj<zuWAJqg�<3ܚx g,WSy4hd S|F.*8%\r};R4]8c36kaX_;p `EptkBTF`7Vf2 <ǭW[x=}J?&� ,Cd'8|c#@]I G!99#�ӑ9'sҝ`O,z{Ri<6.dƒV#1Ov'ӚFM}[ҋKcXyϥ)d$v$R80`8aXWe`2qzE?:,Mdzզe%cOˌ1㊯4!�Λmv%Q';6C*&b!Vz}j塹<*GQQ SOzͣU"s'U U;Br09jJܴp3R$@b #=Oj1U9SR)žOr*5!)8#;$T&1W/`*a `3Ǣt{)$gO*o�`OjbElLjN=DxA�vChH1,CtJ2TC>4袝C6`(=*ÃXR~w6$�oszS<rT�.r\ F{i|æzR7 =)]F(R;֠+($`LS]4=(�<Yv^aN{"=WzX *^\p:T~_8<&0 �yJ\m$AP!$ޔ<z21 h08S}�hQ 8{T]O ;զR�j"*X4`r9A ^7%ZsA8jzT /9@C89Gͣ #'@qM�yLw.9U `(Hlz b7:Ӑ4{qޥpW#5hcGSCI杵pqu'5hLnG`9"L�0}+,dܻ}g`d{]2$uR1UbY00_ K@銄:B!8�s9XsX\pH�sk5I/)*UdP:]AfYMs{rRXT8 =+N`XyZ@>_`+9gʮ6rՇ&@ GVneensl3Y!%N,Uxk4QUPc� cNI EmcUIec4m@cmE#(hYccQn#:œ;I+R2@<>rv (3=U@؈s<{Ukeܻ{u\p+X&Y2)X?r[=I g�s"!k(�vQ2`sjȷ e!tSF, [b J^9d s 95[wf8ֻ EU,) q"s=؈1j]uz<`-GNڹvüOu6hJVzT~Ҭ D(jʁ*Ju^G9T{T3he|G˚V*KHODÌb$*6ROSRQS)֘/#4Vr*fGJCF|IBF9=jG5RQ=WBzQc#ҥ<Sw{ڣsfج5M!M7jpy&b+ι ]:ԶREYNzPp9Ԓ60{2@4DN[ǭ*)M8rsVV:,kOY .+X=Hg5r_`1T+jAkc+}hY#3[厸 �! Zʫl g֭lܶd <Lj1*c±@MjMp:ֳd3FVׯ Ҷ- qݺ$#w:㚻j 2=5pfrGIo9a;a#q�X609 jԷ8LYYJR2ҫ䍪I?YU/ӞN$4IX Z&B50ISL\oe f5sy8'aBjO_Cs6Q#cVL'8`zҹ<_mEbX5u.Ir׭yd #ouY2QG"N-AYɝ=NO lt֖`]]w19+XI3tdd_9![&I-ڨSD-‚k֦V82u-Q0(l22J GL Uw4 9vθ'q5> NcgjiH:Z4PbJc^M@8,zq,ҩr$?+j9ކp G'!EdjsC2_[6#<v@MF9mFszRިIsm;O[sXXnt5 T;8,6qjUl u5,NJpj5-Θo4Hrw3i<TzߌмSH FM;�۩�du&kdƞ2rNy }iÑO}Oz`])eq4<1=H&e?7?슎6~QԚ\=}h-K^Z d~aI;WV*yW1<W)Q}iʍF9c`IIr&N9OXَ[I@TqjM23N@Xtޔq4=ӁR�9PiFI) 6ӛL C@CSS)ZJdsLCpqMdmQY�{IUO8`@Km<*}3Q9'iX8<nj+HUE8<gCeJ8}_**7sjPaPg=Fxc<}*I<Ozh~y80 6;H$Q$MJ2< RU!1O8iB#@9֞d+DgGȷ#.҃�⺋ <vGֱ<ii.X v¶.Lny�k2dp%lL=)YJsɒfp}0)ͽ,rxdK I+zR OЭLTI[#$sCe^Pđ'^w)koV1a6\SA/+ұ5*N|˷ku>* BK* ]? |ZiBc/˜5fȉb_aԓy_L�9qoZ`ɉ1 ҡ_(l chsDQe')dnC INpq2~^n*TqЏjDڎ0}f2 |Ãʶ#ym Cc@C<}L 1ۻRT*TCIޜv3"6UB0@jYڳ`o\^NjEFKwo4eR:= lNAƌ?]%BP�[\GBst8 6Z>CLvs-G]]Tm}kѥUt}r\V.UUxFFsҭ &t/=j`2zb F:5 E8\y 2I") EXD>Aj6lsQ=6n.@j3֐%}S%jܽϽTHeI8ȨO9$ɨ3R1cާ}*\))G??To^*ZVg 2p85<5ZY3c)n3Tr$㊒L5 ʌzSHLER9tΝӵR2 �v3Wq[B*dk5Cuݟʱ벛0]'5mje V#&>lc]b[%#9}+w#͒ &'rcڮ[ ;*{ mcʮz3b+LTLq_A|1D8[)HҩRٕ3{giȼm9YVXn ϿjЀ1[e$i f$E^U��gp8늷n8I3ں2e`v=ޞ(u 6b $Հ MڴETח(4K5�8;T5>;g@X:xdve $?U2$DNy'ikz =ۣ^qkDя/A-E z"#ToP~E=@=(uzWбY݅yAM$Zns9*}i0=*3a1U#p /wsO(f,OFyѕvPrHR\.G-Ҟfc5}s=A�ÿ'ڕp"h9@~mސ(nx@+ES ?yOAگd\zew.ݒ832ڰNʴ`( \}ͩ˞GA 'HrGCZngkha8' bi:]6<(+V2HVEߋwJL}m}/Tv1znјgʕuwO1@c$o \<Y<|-$; RXc9v&+ {BUn3�,y2=rN1(#<(ju\ cUcAR '#ǒX)r78JxK�C`˞pz38?1ҥ dác?1<u)H�dқ zzPxX6GWvgJ͌�Gܒjzm0x&u^szo;S|4ϙ@ ˜Fpr1֟Z@s$84Fq.N3Ӏ)Gq% #`Qgǰ'֧sJe#~n@;}N0?Z*:vc^H2d�AHkc8a\⍛\aޔ<S6#o nZ#AҚo*XadEcҤlJk,eF ^F9�WX(Qgҡa*X62~P2 +3OrHM4 Kp M9=j7<YGT'@Coc"A.!?_F4`2Y/rkтg�r`ՍG!Ď,}+ G;Y'M"26r< q>b$F;<@܀-JdW썪[Ձ:v"tBzhMcK0(� $&TWPdǩ\cW x,#`rU[�cDv bfJ+,qο1T,9 )�2zgw0eH�.榌8w @M:  �8[/F7FBH`: 9 *#dQcPʝ{Є3, q[#)1*\ԾXx9(!o˞V`0 &=j!b.8"1ڴdU5wʹSgOb@٬ 9zַ@2Uш |nݺj4 <Ͼbx YV\Lߵl9Vc h& #Sr{*N*F+D "DqidzPSQ*YB9Za8y)@%�cE!CO#֣ Jb :U))8*x4TXSH3׽B�**A皉I$c'#*IǏz٧<l_*:vô" fK\քR*Hϕv皅֦sEO4t<<r(,@5 2't˽Ldn5pרjfG$SP7UO૑`b0#Z殆 W;pbc[53c!;S`v6*rsщ9:`{/$ J#bٜ 0LS�Z^ 5Tʞjpe od: �PG8 r15v36k&L1YBp~R)=9Ն9̛lNyLҸvq랔IQ@Avߘ94̽WLP0M88ךshҀLPke{r�WK{Ȭqra8's%ž�iQFA%=+{_SyP[p_ҹ!2ǩXpAa}jW(-% q:`#jí͹8$ʎMbxTP>QAѓfOd�,WA +BF|ccZw.-o:g8`hQ#`gn+ XCo�ݧK(| N'^*9�*zzM146D8g@TmS#cz�*6k'cZFFrE(Qݒ9T 37T o N:Ӡ, c>zXiZTǭjXép$cҤաĴiMYLT*6y?wҖu[mfhGjJLeth/N L 3DoTw z # Lc`7|m0`8V_=T%UAG� cM1mSQqJ펜 V$ȧ:d¸9LS�3uLԨ~^rI¡l)$L= ~OY[5gzadEo.rH\.{fLr3M\܏JR0@g*@øj4 X1P0Jh{b<֟!dzPOp:@⥊2rH!@lr)Cɠcvp:ޑ\vJ4�0QqHP?;�mpj@_ ?:<ǯ=6H=6W@ǰ#A*g2�`�JL6Ay|;Әۈ$әNiXev`bpq ~fj 0MKC)�j=ͷ;:b*@=85 9W WtuMFCpj:ϩ& VFU6;ɁX\nzȿ%dg>lw56 \٬+ŞʳU飉w+֯_h9 ޱLe-ED 8-Zd.ƧW?{siKB0{Ē@?ajwG2@동 R1'C9kpfDzʙMd% $HbbH8YV 7dg{Es$2zH@$n'%:I>ц(xɬ5L3gC`?SFQcD& nǢƄGz|j vOYP#*N8(Y{T I~Dr9Pq߯R$eHT2P ⶊ2l`3Ĺ ;1L ?>S󱉙|#h& 6F ѽEic;x̙V1GT`"HspMHYړlL>Zd\QG2*bi~Nַu!C#8s0]dTpXti|T8JBKOjx1]5zT,|GlU[Eؙ*LDJ<�jYi7#V<'X4BJBOEC c=)XiXdjI;Җ~#ی9N0xC]xsAǭCUc*+f/i�緭+N!<' "FjW�t'p3TsRљ%[ T/Q[r\ULֲhn?zf@&de&*FO5#hz:SQP2zզa(J\ylsץvqڳĝr�+zu,sԧs]g EpHWV96бvIͫ:؎xwױ<)Y"Jcak)0|8-aeF j@Qv֋F'4v?QUCduG5,MG=W#\ ->O~V2sڭ򀥳ܚc~`+I|;3)պv3幙7};U$4\iYd亓q8[Ʒe(ظH}(BO<SkҴ 95Ň#҄pCEI'�u(jEK(axPn{VKhp{zשYG*9z®=95:fW<$[ �af� נñQA\2n ̄MrX|*Gi��ɭ4K#PUU Ѻp(&#tB3W$M$NFO�Չ py'*C'u];kϗ~qW6渫9NX=+~�#j}j,4ͅ~sTL ur=zU5a!$R',ck"9<'#<Zj c$ֱ: 0n6W$+ "bvQ, Z&mܨ^p:U4J1-]QX)1Uwl&A*}2@#QsJ6PX�2Mg8Sve$<8\TnA1+1⤆}W>QacҤTjpwZOqP߽=*BҚ'isA#dncs7s}�挐đQH}[ Q04RO_Z=cb6'ބ&6A = 8}d1OP1URh)+4xq^p.$cbD\oteA4TdW'`r3ց!$qV|=ڡR4f1օ8;Ӊ!A@!I8'c]X0"kg:@ VT;hQ#Sf�@[*5Fu<R9>M}@5zBw I# `|)[cmDS9;*˵::.(LF t ۈ FsQI>#ژ84 k0HS\JC`1O)"+<$uҮV$1J+6�]_~)s޹`G]c&;noA{UIqc<5zwdݞ6R:w{bפ&ei-j P ]VH)5R/8\I>dP#/P[\ 08Z+#)9N\IbvIDs91,PTKf(8ӝjr=�±f iC}1PȻ��D�hHE-T *ثd\t$XM-N* [;G,OJcPHAΦønA,^!01U$6I<"mQ6z#&Lm؂–X֤ szt>A#w-ZI ,ղF-w!z- v*8S湙b:POre@K�8_zJ3CkZU<;\԰';NFѺ fVʢ湝^% W ~\ͩS> U?9BrwGJ=ǍkPЭZwxNWma{?6ƪG=Muv5N/._JqQ&A<cu# Sڤ�(朊zG84XC$)ȪKv\8zzV|R&i'=*㊧,ǭVi6ihxԱCn�ѯV6*V#)INRTT."!ɟxSH9vm$k`kH|3J#l=GboRTk(jG~Oqex@ L ڹ5w\ֵD5cUx ^j(N\`RucޛD\A9υR0G=jFsߵR6-2Jr{9�`p8XvG>*`IR9Jv2x9i(HSN 7W!q+g6PҰ5-N9kRm1n98̖2r;UVy`G$d`Un0:T[+J+dU e$g2~zcfdڶc_�gں,`̆{v3/NsRRˇ;{D?)W=sO \.X9<NI04;3-C \J Ca^R '?2O _۟tp;v1p,-[ϯc}5'2�$ҥIe#5fR`p{!Gg;v]$�:A2e H+7Db[=p+"e"L?7LUiHyv�vOjNё?sYLVN= -r)B?V溯x'dyV+HʿN+5]�D1N{/C5(Xs0UxڮA5)ԆT<qZ6n_"jcQm6f?9$2n&<dtbXIW(?5XModOiX^<֭[ɀ>KH�\ t=M=JֵS3q/_F0 ֜iʁf^rc3>֨6S�cZ$q0(#ɽ6j:M̙'A{Nzn2!<Q۽LѶqr w3@[EQ2q 4w{BýCevJ<V.@nSU4$:#,aqη}WjA ܳ�Vll 0$%EfuwEc�5nLW)�}S)FIҞђH8R3B�uzdT@dw=`O �FT{?-Až9cR!scMaX{tSg]dڜ ? }haS#� N}2o=L:ʀrj׌4݌xzӟ;>4/`n�15sKsL`qny͓aO,$pE0Oiz1H ^xdU\ӛ$ fY񐿝4?0& Z`!+�x`+dҘN .9vby�QDqEJ d5Gr:THpp8Lb4A4�SM4IiHPmh?/֞ 9Px#D==kDe4~pp:KdzíLjn0j˹Cβ s[.p{UXmJ8#Ewf[GYf ?y$֛HXwYWZL#_;C9=Who09{R]>+W5QRB_<58~_\ԇ2YzqS0y1 �9")ǐsMْTt>(v<a\jCvibXoXqc*81|F yaIn•q7;u?ֶlб2\: ʳ1]H[<#"Lӱ"񝢭A玴 Ҟfvp70p+[6C! g#q:*V\y)^ج{wk;&;vʉw,v$M̡N/xq7�HZ#�V%6Nc4uR \$`c?Z!'ׁX֊wHz]6@p+gѭ SӠjDW3=zQG`犖,0sOV#i$Z ~`MS7Lӥ*qBY &ZDW|n<@sUd8*F˷VʓT.7\9uZ<2:+!"$tV6ZqVN꧷l[hܰ+>zMi ϾxKCdgS"}C>xk{g9Xl֥<M–�u2-5a!�z' �y$ +U(VedGl땑qifɥpۙB2q/NMyϜj4H-܃Pyf'͵}C(5Ԕ2OLo.6%3ΥI2ozE[U!G5 ,Y^#uuٗ4*JԻwD}8E BPc gֽ ĄAxs `]hȯ5kR @;Һ >$di@H͗=*CN*P?’D"OC;I LI�dgϏ?Zah8Vj7e9~Ǒi~ǁ~sRV\Ȗ0ǷqXAʜ\5$|{5}sZQ救-zm8'u3#r+HMhC)LdJXYk[x:\@6jqMr�l1'ϙLu! < ע&&Tfu\ \$wdepkԖG@H^kI.Tp8;IX2KBL s(n@3R(ϘXck 汑x1<rvgo"ú r"g^sj7eR z3\sMnngB$t5f ǕazcxSGu)a+� 0 9 zVBq9P=0kkH%!ިj;1ޥ> x'MM Wm WQ ?0ݪӱ Fd^¬�K$r*R,lp{-]_CgCu 9Z\䞂,2G|x#ҬCrX('֒r6\JrT;cfEࢌԷd'�TiQ$!Z0A[ʬMiʅ΂yU焓GRFj1N/晝P<�?Rb" n1M<F<,0Qi�a�~4Dށ469:9&㩦M*�g4ȴ+:vҵi[]+�=F .6i_%嬑8aמ:vHQj'7nS5,qeE~R.y 9IPqyLw4aC9#1d Ҵ8ۀT4dy:qڈUa FrIZ�r6߅1ҘԞ>�Ǔ[ �4N}@�#|R�vY6N"mJT.Hޔ2wIqmJ^i20c3d/ZC] B')CކO~€+l s ᛒisc)ey=�-]炢 65x%w+:<aV(2F?v"1KzW=*n׊ٹ[t\[jdDN$O (`ka# vM{UYݙw p+:L8DZk&lN6xc{Ԭ (E,<:DF<YBZR'^qS%cN7�].$fLg 3 oݞz[J!BEÖ\DF4vƽ<-F6rHCZZM!``{V喅Wp+ӄdP݅kz02(r/yNA%u(ÏB): Ql(+<ܱ-|]ԑ۞B¶1d,xϵ/jTwKoO<FM3�<'ܗ+lYnMɔGa+E(OZcinT=S fch&5տWtwE֣*f98kقi9<b{Jr:S$$ 1T<wՇIGWGP{bDSpQ<j<Uʏ&b&k8hm[p zfD劯YŜ:Ɲ7"Y?ޔTXy*敊LǟvLc'ֶ\\({IU;*m(Tc#ҰI؊8P'V65`g&�k> 3UgcYIc\΢玕ޒ\$,ޥTg#�֝T$yS4ǞnEIm:jDzb Fsֺ#2%v<j,;;V 0jJkNd̚2±B>$ێ t FA=iY3ԶIH΁8/[%p2jL}Mc)tEFmc^23Dn=><`F i�)PɦK,!<v4qwJ|Ϧ*0 @U׌� &6ר趪QNk 6)ٛ(vCN`dVqR^"yse^*`99 VfY-)Ȅa^ ,<ZUN׽`xYN+SJm5!=KT?´dgn{}xnl?/>� ?D;Sq5Ǎvߗǚ9(@F m%_ i#^Ey9S r[ Y�V`ҫ)9pZx^e -REp$t<WAa*w޹ 7#@Y<l]J:/F[-Z..8gc_Sq�Zu0g#Em++;B+{T.Ҩ TıRr1S `77|uSMBv*#jbңh< yYԝZ5b=އ85b i z묮XlFTU\H�R.ϖky-HjDv#ֽ�TmmqWxkٳ΀e 9 zT*Mtw1fzMn b[Y lw6 <raFŷʴcܹ�MRbdy;T̓'4t,OZ,H NQⶬ.wCƙ9/=m.褄nS&:3ZX4N;�=qQ: #=:bЇ��5(xL2�qNd1: v֣R0)H> h<aӎČ~Gಅ�qI9A`.{R@ig5^mNd|z}>OҚ-_zo-&0V!E.1d7xJ7+{ևNc({ H3D* CF)�)c�0,z|ɦȅniXsӨX89G!4UT5)�&~Qwn^<&L)RV�ȦAap5s0†(�IHrP dy8Uy68X]sMFᗐ>ZL m枤Ve.+'"�QȜs~K|Q1b6eaO^+_'JFr+u{i訢"We9kK8PjIFT8S۝\8]Ȳۺ)!ǧZ9LkۣI,qGn?\!(cپʏeƁ~gnZ_`@W'qIUʐ{/5AOR81*NX2eC4F߄)?.)oߓ|9cS"sYvç#t�QZA8KX 4]@K"ZZ-\zނ=wN3( *p°j.bgf[PՈj2(b2~;9QiKGOH.kuCRGZ]u3Wu1*DlNę#vxW3w YI\yPUy=kb&brysTGM6R@2 9#n0?&͹SڔHEbob-ĀcjZvI q b/'cjqLoYK #T8W|+rI=WK]5E$FsJG}zj9ei7Bh稭p QsJ{I4T;7fj7c59PHz.iWv26}*㻑P{&Q3BOřcG`^ÞisV#ZDsVpPZŔ�F*=K3sC$)tj)S<+VT:+/<VM )鞕^߰�7 sֹf4F#+'Z,G5qn'aU$S0*g<+rkR x�5i#.EG>zU@N4fЊ|;'* LvG) ^R6SH?j3RX1B�lzCiXҾԊ4ܑ>?Ze=Qj|TzÁscQ8[a) s+̼8=t5a# oNR7mX֭Qj@g>щȱ,F9;V'$�EuᷭmJ@9^8!"? �OJ^[X`t�ne Sѝ_В+&'=+Ow% ~@>ƾd i#d7]̣#ּvVmmBER0֮'C"`y=ө�HkJaک̻ HMFCqV[Yʤ! J(Q6@Zқ8ѥqᇂ!qjLBJeͲD x5xn]Opj׈:b 9݊x#f+Ax&㚲qXX/Od=+fHğNw)@ӽW/q3p;Ot2 H&k XN֛seZ–!?: 1\rg٣P�OJ5<[*A֦LCk g1 Ak5}.LҔ8B/3?g >GR52&|c{$Gy�Ag,H뎕c>6WLJJG\�خgr+#xP:x, 0%(/ҷQ:LpwSLMIʝ,jm\dՠa7''Jrd"g,Aar.ژ'Z`176h� 8$ LzQ4\#VR $�*QIU䊂,pÞ+I 8/֤<<b8Lw/Ҁv֧B_e)2O1Ӑ8h;x895'vGҥ _Q;eAB pGQ}:}+K->쓉XC!ʑA- K' 1^yqmDx(x>#kf07'KLe`=JC =:Q/=zVa:}ij<dT#4:9W0'sCH/48?/>'ހH:R M&0G`ЧLBARG956UUq֚) >]Ն�柸ɻڀ(8%ܧ'MXu0*Jhr?֭RBUV>|Ԁ|U (�qN0 o5 nDdu?,B:3*N3nʂ*ܰNU~az+2wep3ҳ P*$*ј/z\VV/"*g&F0ǘG6ÌqZyŽ9:Eݢ+5*K-kdz߅Kђp uJfemzV!{Q[Kސݑž{>p^}i$ƨ >z;O . LnC'}H(#%MzFl0zF珎-CHßz]FF?W&=S5u(95OC\\O$tzfxv}븵:j3,rX}"ȶ�sZŘ8g4icܪ:FL�uuIF(kjtBf<Oe_ۼO�;- õ` ƉCJS!r3z<chUV<O8Is W9%u5#עԄPy w -鷞[Fncn>osPw-M84{ӳҨIQ=*Gp KdcHcMBէ9⢑3zhTDFj̋9F "iqmJIT/Ӫ"ڄT{+S:ZI!F)@oUT&/5_|Da(c*nI jqflӵjjc$Zvە"28.43C%SՙxKeUI aЊFLoIGhiq57݄CKJG/œ֖4Imuւ ;Bjqtɩhܯm?5UU9Nx8h 3TqcWep1MHXqɤ�ќU$g!EJR*3 sB1<։G=9eAE{:oz (v^AJm�^kw%w^Dx8dt֏kZA kvd+3FlkGu=7�j�?dYI5v3ᴙͮ$}azP5ApTM P31Z~ ,%$m5 FYf+9"3ǚѦ`HS <@J7$o-fX\2޵p8\qYҩp؉"\hcfE< b.{Ub$ e(HH~ǮHFq5^$\},ՏdSױt8g LryF}<ȕ|\̖a)']�Wɺѭu$<fgt;NRF8cjՍ� bIIf`TzUK�XvAϨ>qq8Yp0: ͒?&cgZD{s,e8Zĸh`W]r \m nisqɩB sަҨZ͹W}Îκ3ͤs譞#"#%eU½|F0x ͢ V^w;wVs&l UXS,\]7 6xZ\Mr#(ѷk"6sZ^(x)ݶ@~9Uӌh=H#|�'ҹ.UI˳=3V.^)H>#G\\C�o>v+'@dNN|(-4H'}:vE�kj\gn'9S< iW듞ޔ:]䑑LG!: c+aC)"PB�f$ gIᓧҹvPsVQ =E0hS.!Yxd080prdQ~*Yھxtrگ0c<rkWŚoA_s3Ah7#J'ӛ^0vPԌޘI]3?V\EtnB`=isEerSz(PxS"J *0A(_ǚ!8H7�^ dh2�18�(dґ\q@0݀>@\"{Jc S tBmG%_NCz᠍neX q^iX�nsV+#K"9?JǶ_Y+-Ӓ2_ pS8]*^>$c{-5ęY7ezV33>mAlĊ$"[#ӴKM2ԑRϧAJ+ ЯUrZ m M굋.}h+:^ zr+d+MaS4q)=hIa؊qx$J/#0בҹ� NKFH;W L,ǯC4jV7RSӽ{s]%MGPAko�Ldȭ K7m8t}fĪp=*g<V(nq>5y fO<b&hd @kܪ4p w-!Ǧq]n`] cWrr3D5]B5 {-L2!�W f *�+JOAZ=ŵ`b NdzgXHmG)GE{eU,�{ֱn@3O8\ɭNž\xlikr5m\"]{Lx#sʐ5#`("mB6"1\5!f{8zL>_q{#<*A^=ͦo8#׮i|FA5RS1GܴHAVI㚑S&ti9"v%eUjc'9ZV+jZ!�JwRZ)&OjĜU,yy!JV )粟t|OsVFjv@FE7T]NvֺclKe)<Pۑqmu/ E$,Otc�B6=NzE*[{o!H=]7(;vJ5MQ׸٫$Ӵܓ Z&p0NBą*wvWq1re  ޤ rqӵ)#H#5sj;)8$jRCFMTgKrrzdz&n `94j3J�Z$e"C{P~S#~9;U$f6FjG~< T^S⭠:In$AlX5D#]5@5KCĬov ӷ7$vW{lw{ OvEr:(bSyM{UU($O0S!5=۸<ںHOҹ-ll$0HvTٍ>j&>sUa~E2=V!#y6lr&|8i {u%Tc WhUkˬ#谑妑 jw 8fw&e4]AY@ё0:UY��mҦN5 #̛,QiVm-HzGlp0+5)6Ǟ繭_]y@cp姩xڊUtr$akan_)m\5Qj*lص۪F.[=sy<7*; n,㽷ky2}gNSr>0HҔ $I6;|53888c[8OZ. ¨@5G| m1W8ǽl<Ad`+SӡڰuU1P@UZ]Ks[+ [A#~#a#U[gM^7ܭyh&GҼsGV: -1>cj]>i6rE stZs] uW<? ÍeL$8=+ϖ\ӞҡqhEi$/VbF[<UB)- :SɔN*(8<5~D'>#QpS޺K[$6cءP$F 㚔ܐ*hEsnaנ;\OJ|NT{T|2J+ ʠ g-5">RA'qXc{{([ V)s{ذ�pV ~:Mζ1;V}s\p֗+!+VאFE4$Ь20aJ爜 Mz&zV>?K@ky̑?3w <utIgi nzЍ�sҞzcJMđRԈK攝#(r82�$YX)" QHe?Z`ߒG"89Ȩю� s9S|I^}i8$`1sҜ~v vR[8֜&ޘ@n;`qR%^>נLFFx=q\ρ-$'8 ǥt8ێs׊#ϵ̷rknLRӢVWRʡF~`6I.QTnyAQ4 >cZ8#43$%i<H#֝ E#[4b99> _-6#hf0SQ3U.цxdc;_޳ '&'s|~FoBr*ܺ+C`+u5J"?EȻW\ީj2 jw 7�VD@fgدkw}F!>`^Կ3=jƛG̃iorAkϥ܅,LMZȕȏqVlXJi5RL8Nr*χu_H[ IV[!#֩j!bBq,ӧi2z&hOW;tz#TUX+9G+}͏Q -cVswp,] ֒+?1"xXՍ-K;S{ll=y^Ӥ7Fq$l]'�,QkJǹBW#J߽A6 ͝oLw1Sr{Tr&2Y3*�5FHVV'Pq�5mҪI~E+ T'Uc  ~*8Hl5�l&W$Ux,3YcTg҂g5Ӏ=k�{5ND"֍+Gj, Y!Gjn_(j$MaܓkBMI.[ QVJֳbͅRq9r!mEZg<t.Tc1WLflI+:W288ۧ#$Y3tCpo`j˜/CU}!1<N$R-΅byv2r1NA2<wQ/$*2lQRx~8w9g\"W sOn]4yxIB1V" yal=hL+oc5ʚg|r tÿoQkȮ|Vʯ"ItG]IyVL3VfG[H tɅS2k.yOELp=KأʞSdY\2=^.oV)Ld<5V�5gXeՙFI}km|gwFy't-I,e2Hsvz+ҶRP+^KͼQ5=륆)8;JjO],sQ6'*嘱r,t&SXq$Djr)l4:vQW4]</WwaampO1<]|+ǑǨHn#$Zev`".+2gVa\A,H'ݙNc#5j.�GZϵ`?'ηb .tV;4\+y+YŸ'$f- 9'l1=*k> $MC+7tpWp K88㩭}.EWs{v*d>쭴0eZ#6u`/ -V֮Zn2~TJg/#wh۸&ؚb`g]}0{sS3Ҭds[PG}kѧrO\`Oa7;LLz( r:7Iq *“!~1T);CGHŸϘu ϙ( 3^6HƗ?ٟp*I^*',=yLKL߈zfz<rjQ՚}rc$΋ɤ/*T`zWK BMs?ȞU *܃<f8Ž rjRcܑ+=OZԷ o$dXM9SMY=q٧猜g+ 2rOS#9$ީ#c a$穪L :[WbAvھf5kzC*#3ȩLܐJ<V`s?{6ףhY:ղ28LV$c]&te06w/*}Esp e6omp2Ҩu##d(FY:E/~{:~evry0EmN]y=rvW91y-3U0ȫ/sOiBۭD d+;QDAQH=8Ĥ/9# `8S)n=zр806bFI9F$`ƚh8:[)�9@1 �6 t4tR*iTåMnD(%T$ 1?{jevڥ %�QxUA$wN ᛻-YEkWO<_ tze >2UK *@x'R,C8_Zq|b +Rwc6 %A<=nkͥѤjVqL~YUxj"Q;;ƴ+:R6$l�Fc-*(r1S�hUhp�٘ٵcv޵NlM!ÏNނ}a�}j,`bFG<bb84'\Q'� ś"[a`\-Bn1FܚfU<`(R(ME ԗowj/sޗP8bӱq EWM2*zW\wٸ�v蚬nK�sPՋRzmўWCi}i*WAi9TWl+{ѵ3PsռAc.X4kIٌ/܆8k]¬x[r⹄F'kp=:m$zLAwH zT%r{i$b4>L Hcdv'ht&bC c\*|Ի� sҚD6C&8P)sk2KV=ꑛ:kg)winsav6ik]+׶9n]W_y5IoX}./oXۭBauݞjNdAeU,dG/Xb˳Y#7ǩ&-thAOlN-B&mn;ֽpjDDA>W<Qe,DYF*sJ`>s'ۥ2ɥg*޵T7)iX?cK=ESNqZZ=EfMFIZ:c+U[9qVz$q1I 59G8+'UlV$ȣB:}:P$sF?e% ,lƊܐ95[q[)1VjgҺ"*t<g`Ii =FLD(� xT[G0&=+h3έ>kYs.8V]pp˟N8}C6s3E,Q5g3/=B̆R e2F1r"X#&'+qU ˨6ې?QZEbTH6Ǔ]]_%K h;V)I<}k4KXeG ɭnc)XY%OW#iR<Ejs <(7O w~!+X)a!LuC71IIQhȮOW1&cQ2V y˚ʮOTva7N<W<\1N,$4[aO8c{.=q]("kztETbg[SNtDKl*v˂?JYf-V}Bsz ܭt`N@$mw1c$ ֜=mfrtf3VRW-;mWT?ghG^Yռh]y 1]\6b%FOs\ 3vی˶=:Q.3`B5TDvyF;b8�FnvӮ=+O]Ðvx q$9'p3,7{ '"Lh'-h~mǖcO 8߃/i=}ec_wT �NVb~_Z73ڹ Qc Y6e}8vH<{b`D^⼣A1\-zf81qҵ!SK�,�/gjE*fqyM\Fbt$$ҽVIYAy#UE%rO8B2Av>8QXrk!SG\W9](mVYO?xަ#:O&%ryn2jյ�ɑ29~8<bNt�t#Q"m.KgUk+ ycukY\yL#I=Es# mg�‘ך͈ꠙF;յ8PsXVsCor;�kC!SJ,n(=MHmsPd)R{p R#A t֚c ?ZrF�q;IbkR0d"ll<H:ř:1T jEnix퓑ڴW;=T̚:MI[7/+Z9BpAF"qL͡5eeaEp:ޞ1 z;V?tQNEK<@$pEN0N+*HCڴCeZHԐOΔ�r=CH'>n \2@*>_֞9Z�RG<OUFrN'ҕd;A_ց&؁N ļSD�,0a\3Vd[U<$υiuh)u'5z uZK�ʊ�*͔w5飁mu:J|I�[RAI ;v֞䵁cQrrjWIJ.R i -|^Σn oy8z(kAiY`W;]zDc#7 Z< X`+Lk u5bF#3|uT՗?"(eݽ{~T"#lZlS> ߥc+L"73\ݜ3ky1soC>BQu+W֙}Ka\D9eH `h\0Ļu2`n1\ѬiF_pr-4Ӵ3n.mhukME]ÂgjS<]5P"\q]v#yA8+Fi 9$ӸN>VKk!bpg$֖ x&_ YJ:¾cb3cGH`'֓(q؁n)nkUMv^+;c`Ec]NkL/hOƻ?#mj4�C&/9\�AU`Yh֓9Mq 드U<,! o+tKXB419#FJRf<Nۄq9|?.!fK|1"tȩqjˑul %Pk:\0x;{՛{#" 3Rh`�uzJE VLLf rM 1i=3ҙ'N9&qK\I&}�4ٛemKQsr7Z㔄'j&{M@9aԐcYmrrrk]`(N+z+ Npr .}j!M99,g'4@AJMUrz‘77#A. =bKuur*yk>h^c!AX O,w,ۚjfĸ%$*Eb]:H)cf;E F0+BHCYQy\Vq#>ia[-]<8"/a:!;ıNI.qҴncP灁Ȳ`{Tǵ/>,p�Jg!QO>K N�Hl�;/3}wtsZ&RyisgV`Vi"⮥UĶI"( u 04`WO<iq3D$;bBU7 @dֵ8;XHmj n`jº޵\m 篵XĈ|Z]qR(*�G;+>wT}49UORMs>vkՑ_smg&gF4/^zet+HʤҨEZꢷ^0*֥m 1J/(Z,u $F_ZûH^�/�J״_ i4fYaʌ' z #IbP:] 4z_uoڽyrA; s upu/_z[L8oJ5M@3wڣKg3i-Ѳf'OZw$cL$>*'ڦ@ 昷~^pNqReO*y+ٝyy4ӦA8U#n+x2 f٬bsWl-)9'ci^ESw%"32q]74kv[Eu._ ru{O=]2Ҧ帙vLe>'ҰZS ({+tٯS[T<(WA60G99HL/h=RR<+oаG9*8\璻7ijsqH)71?ʸvYЮ\Ehxc&VG.ί'mlnQ\o\AZ4 u|99;G5X$�R1c8frZ寓sqy68<n,,Q(YLbNk.u�ع\WldׯSNGRC)eV,jsi 85$aG_z|Gssr&9*GJ_�)s;=Y\8m^gfdqY]B<Ų2.2UU\cʱ[W}ʪۆ *GGLf[dV:3Rx%Ӣ`FyTbi]|F}kRk*:X. Չ̛B`Tsmm'>ZLqN8e>0lXpHSN8jNILgH �yǥ41Ԡ`?C=.OS4:trAڔrOL-޶mcƑLk_N"oSVuпɸs]w!$rZSʚױ4JkT$rpi)E6)VhT?+ xl|E `?w'#SkZ8_ّKҸV{y>qpԚ&kÌ09ACU spjP,X PHc׵,8l 2OzPqg=CnCqLGžE�FT]]%(O=xk| C1'WMyUzp:tw9|p`W }p#8cD00Ed@#Ҹ;,OEr,qi Jé5rZ]5a($aA_V?5&{JS*9楔d8 T&pI9j;b�G_Zɚ)�bn*Trp}oe#EvA,JF>Uf;d `t};GNKVqE6�TjBF mh1�*-EdhcȻ9*\@kUcԭw= ƝMs$OJI뺐Q:p .;d.R3VݭHT#eMۿ 3ŋܣMѯijvp@NsoSLX4x7#1X) �VrwkS)`}t&[OAkJUޞY)A'$SUBdz kzω!g3Kkm2Yei[5a.P?MV浦~+F&/Su$Coq�U%.Q C!VԖq~@5ey|>3)/lWa7*]Fi3k@+RV4]+*WAekLGʻ,vw2݀, =lJ\)3jUhqn<{M(zZ<�xyb|8or,h{A SFprzם:mEG= $Gң;ܬ*Yȡ{9U$dW;:=ǁ[{\aJnE;[>0[694Hrx+N�b/q[B7*ORz:C$zp"͡1cL'ˁҶh6Hj8J'�`Uy>jJ{UʎGd [�gr *`Kfj1}({ǜQ2iV6/3Z+&:l@j%Q0rFcR#qsC)nʓS f $p䌏CI@Ra\8Qs hQ)?JiI0xH㫋KDe׭<&(a"BI1[VَVl/oxd y*zV9kԮeX@*{b ^1MQz sޝ]t\Ҵ.c:+VXD[!E9 Z~nmSwdu�b*&vqP5Fx5|1rKW𕞭hQRXh>cб�M]Ϳ +VE*nU ,4I>oFĖ*M0]cw6&_f�ݫC2.ol nAul{7|ƳLb7:"?WLYFMcnw=kɯ<-*K,.Rc\Wi'ح 8n0쁘:VFn'd1{?p1Q‰;nIK-ni&Ee:Ӽ9y[_.7:ǡclI"Ɨ^v8cXl*F�үqƤXCeئ-Z*SOԲY�gWd"; REI�$ *nE'UYJv`s֪8 85nUzɞj,3.;rpyEm~mqk"9Ȥ-elizt�UZ8 ~VFqɯG�xy͚ =r"Fwf'LRѮpI?6=k*)F:.nU vV;F# Փ`:i 3tϴT7}+fi Q\W,BkVew tƧqil1sCxjr:)tϨ+ɷ9Jr99Ĝۭp͝I?(FJȃ&B{@\=O6eab<`GAUcxc񭒍4g' :jĹ|yZd!zWU}vwkB�r�8/b>S{2¤=jK#e0GݬwTrMOJNfrpײ!fm$•ZV�08:[IB>| KK"d<ϸ"B) 铑M,GSHgޅG+dd.GNɢ{w9$2##$:zrG :fUWEkvcGq994Qɔj̥<9,a[y>V֓,r:V_BD['sJv\G4e{vK]<⳵e"8>g!a6(GJ&I<kkZ,j:LJ>`m787nsj($0*gufL cҜ^Z3?:-vEw[x"&s0#z�:ly5mZ(c^bT��pQ|0#^F)֚W@!QwfU<SmUd W5\zFzT[ӧKp#y<'ju5ڗĽN #T7.}w62\iJA.?էrMmL%<zV$:`c-(>c}j:o9MUլ|CRoVC>Q8jy<T.�R5tk">qi{Z@ d \3_!T¯ii1 � y{VmOt.$d[Gt}6=AnI[S)Tw=E kQ2NyBO֋c\>�tkKPia帉[�eR!3C+Wp)9>c�IZ|e BX( 6m#@KsWo/ ǦhHI⸭ RhMy8fmFkHLj^ HI H#VH21qP-˕TsSIc*W*GC޼YҖ pW^Z\r+׼?Ḅ#*,i۾#*+ڴFYY#!�GҾtMՠ!8B5? lrݎV} dnQWP.,)Q^eMEgʢ~LgϜ"BJˏJf˨% |�nj2ZJţ5AE,ŧ`Oz2}EW+Jz|Wo vn#] .y'tMq{o^#sXiG݁+w46uS#>õhŞ{ǟ!C syZ MIYpNiVܡ#iSwGښ:l|OGp>s�֯Zli!!PkYM_&ى kTRKST~p*Wp3ڼ5' :H<Zt6T"� i�ך]>[Y/5.αfL HU I!ϥJ܌4K$f/֣Z (6r!f^;Qrۛ04LPL^Ё֦VZd Ud`*үL4p)g`aRо7X&jׯE\H}:k5+yH�U ֵTNf]L!<LO.(vpxwJoXKp~Ωy! TьSQ1%ly??,}xܖbpjDrʼkE*>ʁ:VcҘcGgszPZ4J8@rbK2DrW+g( Ygֱ^#e�ekw"63D̃*9W #q]v:Z#89D㠮 `MjKV|A-o0iKL OaYZxhMk$NŝckYBH͆rz <zYauϲ5JI}̱4v/S\V5NnP{*}kqʀ뻞յbu> ΖT%>u\5 K[ڧֺ0/Mx"�0X]BO$WYz-ݫZM's]mĊ2޷2v:=N(I^u%KhVu�2QNs>Æ_³OeC$X$ýkc>kLt[5{WVt_3<r+ִ!Y-Z)c=\?K`;V޵%VY^a䍥$7@cGru5pyӑ3 jEI; "U{$m''ZVЬиd`#yZLʍOP[\0 w8LUPЕI|z�*H8j@ֹOj^"16��qɣX1gLj\n;JWodwmMCEҸFJc'6S{a#NYRU,#wher QLTg=PutWw 1OZi !2 ^*�wPpBsp{'P}p@[$c#JUdvW# ?Թ'E7wcQ;hUDQ4czW5lJ59CR;Cf6'�]e%Zge8͝!+|Jl6Ht,\/#q]FU:QʴEyRF=) Z N?x@ǫڢ'C:b 1@zTO9LssڴLq؞ib9c7BNkZ.Uc=n0bj ؎}ȸ$וsƫM:PPn:LvIbE9'M ַb6q$ywq~JyW<`񚶑4<VL^mܼٵ�g;R>2>Ѝ;k&c {sHö8A 9GuP \o '=Zv=)ӏƔGl�*@pG'V8{<ZsO͂[kOhXr q+RjX+Hq~"7*Bk4eOw3TRruZ9NoqWj)+ZnN>2aCF#Vn 6╶Gze)9 ݻ4[vIw, M<bikקP}ul-)B!GjmB;&M9/hRI5Y{߸%]Pdڠ9S3\C{Ig%G=kфRZt$�_W$qm  a|a'R�I� $_#s[N�JS<Hs#{$1N\͒(mRF0j 5vR2 U)$٢(.:qYg%b@ӯ֫}ƍX̉VpXJ) +]n5ڛ�!*t>$ed'h3ڹj!mv/RƮh!n~ϩY W#rrЈRv7ƲW K;R2G<뵟mrvs*#=$Qww6W*s,_ʲA ;H^p=MxXO{#�}SMk:s"z9SXYv:r?YKrqGzt°$ uգ̏Һ�kX 8<5pL?MBKY`5Ü8hn?OpWFI4NaqJ ޙIjg+*d}+uiŞ=؅(" >?W]5a:u,Y)&53{6ZӑlhҽYI5M_f�sv};$.ڲj̴z.}Rk_1H1,7{Vyd[:Q\i1<9+W9E({t2yCAU9Kip;�X/qs/+{X*�5%`sƓpi:P)֔7 ᱊!UbT}kXr+ɛw=%cN~P_<8Dr} }XBFFc}~EI"qZҫm$_x[,vw}9ž#1Z<|?hO?f jw2w3X_[yp]A"U8g)E8^!@$ B)#v 9#tu DJʒ 9ѹ<\qʃ٪ p6]1WE2P׌ⵗG#x=`mY]3}ݟaևKԣ!XwZVYH?QWY@XtB/r]jfbKcu 5 Tn �bNz֌]]60h8gӤlb �v4mL+c$ʬgdEn#έag,Ʊi9m:I|3Fν!cU^TN?JPcXϩw e\OM^] }A,@cױ4Hz[V6f\*LU2|)?hLlr)5s�5jb�8kZmL3Iۻ1cU< ;@H�*Gѷ1{Zu8]ղM+JSzT~HXHҐW5mx5,Vራf2ZԣdVTܩLc:0wX֑.zEcXҺiLq7XQN8.pf%�|i7-rzq^b(C$ċ$.r18o%'vȘ*}F+]1^Zs8immե*+KN/)M }ŝ!gÙ'hx|1)]6r#y~EoqVi|-W֥ MS[h+21 0)V"HpGp&1.ILer+X/aUBl8ϥu6z՞-ܢ5}oSkCQ]r;[lKkF; jjkwh(aoJ|=y|29\#ӑmXtu }kgCL'f1P=+^yIUd/G_oݬSѫ;6S^QVĹ!.Qǯu/.uі`0+/Ğ֚5LwD?ȊR6:)s &mZy! G] >d=MG#dԶ\U'YUhTbX PMswm]\Ot|)(_-'UT;FMcxO1AP2OZ r%3_%+R%�\�+'>AH%u' VY^Sߡ5kY±R޺7cxfZmmo; &C+|7lm F?䬾.ص;W$=xح�D~ U{DOl@̬ö{ԭZ"Lr0k a áSWY؃XTm v.xP1X$1bsϩK;JۘUqָ+)/,wy'5k32$m{ zTQi6Aj$ ұ݌:X дs&ǽR# �{4L�`9K!VSߥeB ~2FzZwN* c8hڭK埙v[1L}XMvK[cFߺR k]c3 qsVRB2OZ輦tW�+ 1F8'j`ۏjCZpTP%,tCJ+295$a dAJhS99SG3vm 4Ri{ xӳ?ƚv zCl{RcMiT|Zݷ@&E(q=h&ĶrnS]FmaW)ri c<wZ"%t}j-N 0/\cJՃOLmiCٵ qӳU X%2x*Â+*H'-M4LxHyy @ڛV\R3voʤ`܌sO&n ژԱ+">9]~tumi8j3$S߼ʏ3TnmF wh; "[TJc\t?S^Ы.ң*#SkYkXEtw�@y#͹Ο%и[xa5mXNW: C>q?�8Ow*DIUPI$`K"Xٖ ?0irIcM"ls+εkп'ʋco&[X[R(AԸ3zFҪ! v꽾gֳ,z=)'Y=  papZΣ=<I! g}5\YE<rҸaiG|{Xnc*[ W~zV)KwjCi-XUDDuP]**|2~3 6O>yꌱ�ޛ<"d +J\֭%%wWD=GJQz_+IsݴM+hϞu#yd`_@.U$S Z3Z$0yo_0smx�T5,q<Sö ?ȃ5xC챔ry&-]8o-�MckZT֭}&jY,[J'bĀ[Ҷ3g@ux'TSp^~Rin#΃6+sÞ%Tr=S \Δȯg<z W]sm$ ǻl6ErNT&sn$TLjfq6)k<8ܦ CX]ѥagc;W8/L ŏo#5:ngm Rrnkj5ytfis#z?_j7F-]E'�*^?HT$~u{Bm6`ul+,jZUu5.2T<zG[MV;hPk&g:dkoH(-TIg52.ɴh3ۊ4pVen)7Vv68_H\C VIe?zCB98VV�9Tz&ꐙ$[_iV+D\쏻](CPͧ14ԙ. "JwRIu9#*@$*U_Oj9kX2"\M8 �]ݭΕis~cVwV/"2.Ai?Žتȍ>xV[.sW4 v8kyr7#1s\Ӗ,/IZI3>֥n#;+ ϯ̺K/һ/FGKw,2yl ㊮[]bt-7Lw7>#<Ȅ0e8}@<ZXKrAj\JGc"BEĎr7cV[˪J#`ڹ}=kV]f�v讬lDGn1Y6ktGrjvV<$oqR>f)wR0twѴ(?xquZ@#q\3xbxFFj6tG J�=kFR;k{DC*=58lt4sou4r?$0H; ՕH=NLƴ'1umJ;Te}ְV9rkVc;Im|h$z՝dS: Vu8 swx\�k<JV3TKjd7IYv-jӟ$c{X\ܘ#_jҀV{.3t+N71- / Tm^B׬%(^6+9H1=kt7i[˶fܹ; לϐPNYSu4Q^8ncgBU<Z}Ʊe?|OlA^urNN:9t=L9P>W^ՍxP욄zJF2$際NơHc 'Qhè}*_&?ǵc^z{,w`. @x9S?tϥT(T/!FUV12/hRYբ!)7} s]\).ǧ5E8#) uZ %rTQ-!cڪڨv <\"xr>q #dvRq <�±5ePd�x<fndXÁ'5Uo"ElqPRkw%ʪG%R#n2H<Q#1�O>.e$Y3N]I_C.A'B\}:r)wf|p}hcʏlA\5"eCxƩ<sd T g1P=k) 9H.yy$\%7S fQwLFHad@+m(vm5n{R^$FL ޜA^zy!%ڌ(05N PI�dnH@)V2~۽vVdJ!65Fo2wU5i2KPkc%r|8 }*ݹCUUʹ TrqڳLf_/�t"@jTrFI"FhѹFJK+�x /,%k3k9CvqڠK3ҡV8r#Zzփ̌nOj(S++56R=}&#)H\׊ rO'9�VČ\9{)wAN'`G{y<ȎkKD!Hk<.⬮IJ'i{v/Qe~x'‹.x=+OԖyNp�δn{Ugk,cnsӁl䞝=꽬nOz;sVb=z!Қps>0SZG7 i`xڰGjAd, <% D s];שd)v6h*f\yTm)"I9-8jԲ Ashe" yF* yCjZh.m$_} W>! }2=g2EHLdBxX0LWɦ/٭)aonvQ b�Ku $p93;|#k^}F8bRliEb0zOzǡLޡKsұHO2yJlFyXdl<U8iXmH&oLB($< t~!0–U]<,zDRu#&lg)o_xVVk!y+t=79tW'UR2w)>V3FHbC pJ̺9m[#$5lNFr9ly\(^0/[ks돥w֡0\;TGl7rô\ K$qhNn{D,>3կadoBq(٫jI68\éq3%�3Z|;[:=wsng%h*-վ8 ռ!wolDw>tջkK~=tɬ"W jSRMNAQk _'k#S3H= Rbh׼j%{ŬSodVE5e-B#HJ�� /?-hpƗd%�*q${Y>@\&c"qia :0Fg_df}= Ng`M} zͽ2JQ.2<LԦ쮓ˑ:gVۻe_1~cTnWsJ'D]N j߆�Yr@W1[9FM DS$Wx=|*MMRZoƺ#+J'Ep1SExֿkTxѦ,JO^c#xF,FIkKkPQ,}j Ɣ!a.zBlQ\ZC՛~->]ntYA˒1Y>+T$qkhӹcRD2E* k㋝N %, [[�-A:*e sN NAw]"%H5N=i Ŭ .C^Gon߾urZf.\IqOҬ4&[rTsVJPr�zQ({$*J]CMIp "ҩM\!~:U Egѵm07G5i[;X̊wv6u;5"&ߛ8> +XA�t TNkw8'gͺ qȬgXಷ>vzz־E1򒓁ʷao ӤlrkWk[JG1 Q5L}2@ Y2^ 5[U8#֖dJ{f]Ѷ+/<̀O\-̩(_T=?)%"P7(�5 ﮶rR8ںwE uv.I^+arΌT\w[tX%ydsw_{5t=NI1KKt< :W-E|R:VĎxx-�ij,[�8m/H^+B_ذ1Ij敮Eg}.QcpIw1vz]՚Erjm'WH8 ׊F2{;f\[|>VA᱾?7\mztI�ҲmeFHSr Hru"srx_nKГҳIeirXԹ #5-prOfi#h`hJ~\jyc,H`Sku8AkpZw卹ϵ.`q3ee ŏ4.G< \,ߎ rU3q^OsSqR0ST|$S[C$Ǡ%V)I'Z�jҳ ;�.zcj >`zXf8+KxSgĘb-CS[J ːzSOuj Dc@毣`:Uyg^TǥfIK#qȽ+ 6y'gڻqҦLJ;c/4-dhu둊@^dI<[—EIlcZsg$C+SX `Y"u*T碅QޕBA9U9.wg+FmltH Rc]9sIL NHEBz4Gl6�i;2}j[FyTdT�SLrQϽs2ȣ99ɧn;Q:Zbʐzd&1ޔ8S+8oHKCYI ᳜S'$zT4&XZ(T 1[}a i{;yc-#*zͫFe^I9^R {P;�JW4Z2�yqMȤ? wۧҞG;qQ%;SOS, t5s6z@lH))֩2dOpF8 v'\9\c]Q9SVfV4<CV`FDD]�z (#@oo1Lu71C?~9 9" &ٙPIMڕ7Zt3a^G f! 52sԚfx}cnK\6I5S74ΞNA⪻m<rjwsw#9eHK @bXsV{|P={fçjy噆4m:;qI4ǰz8Y$TX5k~.ie_*td8GzEcçT-�P]]H@(Xt)X$M'ՙ5�{UIH*ާjghVI+HPJ $ƒ1ɩT2䀜`k+hd`�s-!ҙX ‘L2:ʪ*�nM<HeJ MO$jj(Y88�V4rJX69Wٱ{TRe8Wj!�%Jqak;�Z6걂 _zUvǞ 21&ЭPLja{5ML2NwSC"n27,TE(/Y11O41<g }&krK qV�BX=RFx_W|G2=ku/ V q^!cΛ~R>i~ ]>S=%`Vja b-;UYT2kz0�Ȥp%}`Q[`|df{rjM7dwuWy̥?޻MfM6U(.J^I/<Ty^|(*}G uk YC@×q|*vk]fY1&2=x/-Eq`}Ŧ0 :7*i8iܯؼe*, \8WQ.9{V^4Z8r,}렰ʠnn#dTʑP[E$`Rj+uonMFYEZ%`m6H#\V fFp�K1&}3Qq?:.Σ<'�kxhT97·Ct#ġB1\Y�/A y8'6QH]A\~5V}Jw b)ꥥ78Z7aJ~)gPJjFHb1K,D@CPj0@=*1wqA,.q֨GƛI怩csXzg.nsZ.KGezF-HY JoD<IןxgKM-0K+z2&RI!Ahp7&;wfg:"$zeg?<ڒ"|q(bhi̹یgiWM͇qUZ7h62G*;LѡہO";6lXX�r0Tj|*G@+.'V[�!ym -J=ͫywFxgY>+meAݖqՑͻ{X.@~|z.uߋJX/= [o%8uɯ4t+ (3g$gv|a6HT�FO@9Wj>6]nϚ=Jľ{ s0=VO5-ߌݸ[^yiAas~y+{(΋j@>zf}Pr@k :L5no_3g3E H9>e�WY#Ã81捰z=8g¢r3Dwv$J' q&w.ࣜ֍+ys3~He;m[J86:A#cPDH1IYZ.ʧhˉ,)ʦN081ABꤏ8F݊#0K Bi8p:v_閈%f<O \]7l)@ GZ.#'|gj1">BMWPlnFrqSTϮF:" u565P<My* IHf"`ʧӚL|]y۹1;|s7ݎXQH@l\c'8+ܷ[}T0}Ev!F~QV.f-G@'ڷmsu\$iZZ*�? ҆=284CH ~JGiXhD�.8S&eR n9r9v9K�Cqg'H?&85B}:0qw$bhERj8vv.zz}nxq+,r6@Q)AZb+1<fWM=C'�^r9#qri!Az֪%S<:wnQv^X�R0bI+!F \nZ`<+B 7 7^�} H3 U0{>Rs!݃;P"FA8>kx?v<VQ\[J]ˠak/PђKB1֡29R:r*{y-+"`8֤[;vJx.$f`ݓ@Rx4#(9�9j$O~H2A'<U&g(>\܍޻fjpFJ^K$rfv+#6mF1- v^j lw 4BA+8<Po<dqIk!+tUi6Q ӑGJC@܏@,ς@ɏZ 8xM;8fC 75uPϢ>2yhR`(%X�qyfia*@VEjg&cSm.umMdQs{<Ei1sr�I�#qK#F^27&3gnխo*t]"\IA֩G)[,PoΤjEqYXcAHPqґRrCgvc>#t?iۻwNƠFh*:Po ʌ*9T0Fxm(,OQH A|KZKyo0٧N*Ā:Q\(6~_z;zȼXÊtΛC~brTqS܆2FV>6ؙZZ%Tθ̤py*VB`9< "D "(UEq�R /*O/1ɫ"׸7H.}) ϖ?_aAeFoPG8ql(8G$j"dN}Zf$v,?S1ĭӖ>&Xn`;tQqC5E6#8g_`.^'NtaB8$ZBX2\P ] WSX ̓eb#t MV{ &iwڰ`>l\ yt:Ljl<Bc.1LJ=*AJzSV6Gִ!/CTr�|3sK=qnw QϥgxcOңk<KS@ _0LEaѥ| vnkNԊ=ͺ(ezޫ蚤r<HBc5)1v1S,i'+iŀo{E=eww5#Ef1˃5vo,G5[3FdmOs9x�dfkf5Zns ylfQ2'B>+sw%i#7FBLfO5�U&xXn+Ͼ>;qCZ[XI1TiY@f楷 E8HGvm q=q]4h<k!Aqpe:VH Gj.<C#˴zSm>@Q}B9OZyx[ SUZ0̼pTZ|n#;=hIJ[IUHQ=_Bf5 ÃdqI36DqU0qMH !OJ\c*>mg"ƢӢ;dj+^ɪH9%|݀zd^{)d^ VG ;Urj[< R.%zp+T\1t⤋÷#~bS)ARںEffP7uR\9`9| cE J뺰/|/4lL?0yxӭ3!l1{m4KiIX}M1Xɭ."% o33H`W{! >eac4puV43ŬA%s#|U+=$M9\Y+acJQALjk_˲R.s[~skdL'8UO:C ߸ns 7x#v9/la[1RQ~jN<Of-bR֎wtUt//羑..\JG#85͍݉1@B* 9_[H\31;f$*Ur~L AV眎j+�ǹhFN`ZjLW-,8U'I hd @õtzv,qd|`z.\̽ZMOϟұqVܞ �1 ՝dEmCS5%˸�6AWVuϵIqJAҗ(\Wgi΅s*o^lSdH%CEDx#Lvr>e=j6P9=6Ln=M Q5*7jClR)6sU@�$Q<�: Qx5?G<z~-<}'V9'U8zL,rm .9H` L!X#w>+ `0ȨTwqpqq&YB⑎2(}ˀ:S["z�h�Vh03ތFOCՔ :\-ܜ]s͹y錱AxKEG8P<vKCٙ-rGkPCv1P͔ q)X`C4V#)Iǽ�$gwJzS7T*,0׊Q1Ԟd|,K g$�*Lq^f]eߞw~#6;fT[FKInS]ki&=x$l I4Ddrj�ƦS98#)maas Vg^|H0 פHjSTnyWS[$Nv^~!YQ}f-\* 4Hs /C*6I@LT`e Q>jF*'2nVyzE30Q[�PK*++ڀ"S- aWk`N`I8=�3عaL܀oȫ 较WRu2E7ڠ<e]9ϵ\X㞝)O�/K*]&Mڪ=8O}!cv`Fpz,&c�VV22*+mnI=~$Qu s{VE* @P~9MI"h nI4LӠ;ʏ$($0GzIźv9 e(sXR;JKolMKEE..3uəY۶#~0%VU[h1^`8RyMyF}jNIAm3856;0lަۥKm(+BNq$ʌA8*Ir1 3*aY^Z$i7�A滫?ڈI9}Rm#*Hr3;\FKǢz}kޥFjY ZtȎ�+T$xK*RT2<t}X^fXQ#\#i4+<xH�Wb$dx!+j][2$>LԴZfdJ\4HA}Esi �&hGT wrڜ59R$!q*-;WqjȻ&Q穤;kLّ*49pZBcf A }ڝ�,ʁwދ4eB 6;TR7H_j3E+ҒG+~aNciIA<F7ֳE#$ %ܨGj�ӔQ܃ƢpA5ZRrsai;1>\g+@p>fOcʤAj}IhJpet5YGel ^9jca$DP{<,ɘz |$L9-מS$ɷr6M]ɰ˻$h9+֒GXDAN�vKi2I$YG&b,1 ϗ=2Y"L~8 wf"\T78Iԓ3E$��Թ5$absH= sfj9 {S&1©=**ZiǃEL4k[wdUir�gcT΅PÅAN1 &f)2\b% [h"U#q5geء� 0~G-,>qsRBVP"-E57O63֫@<ѽj'ifO0.HpWҢI*;ɪ,n m#KE<F+Dbvᤕ€Ux)AzUD˙9EM³uk13Lq_IF7#=)\,!frz☸<(�T8pIj7.8s."1{m B9xaܼ�3Ҹ^0rxMha'Ԅ`g=),POٚHX Үft@Xc)~\Mc~ F6x\߇;Pʧײ:/ , ֩L,x 1:eb>HBZXqҩM&,>@9=;LdP%Gm<C*W E1`#?^rYidmi98+rL dn!OA8�o[RD6!\mc\.PjdĊA"1iRz2 >J#fD[VQ}*1%_V%ZX՛E"2<9SޢeǭYQ>W sD a ;Ͼjtw RPzMK@n}MJ% d5iNdqlЩn4 gpJ*J2l!Nkv`xSXZ/=tl=k6Y2{Ow 99''4!c#K<rvF3I blvggJr iF >CaRB}'5$ޑDJS<zb$%In)ѐ LAx2rIpP4�iS].ЍpqO`HV)3{f*A= B8$;H"(>}jKdBxMR3TsGp8>9 P=3z�Tyco@ݜ~U�i1z G-阮4-W}fVUH/ð95]]sK+ߵqW*yNb 8RH=k̩vOSVM+=[F3涼/%Ռv?ZShW7e|3LHUXeB>fjf?Ƽ VE|yfsң|7S@<U8`}jWoIj_t 6�CM�Ӟi�t I F폛8\ Jx㠦"m8�h6Y<*xhUpN`4^ Eރ! 2sPlčਫ`n$|lB@QvTc)ȸz ԡ6FJ*MU叧j,;r~IpsS.2cQJ>X88j,v�XOdhq}Z6ʅ\=\:0;7L$xkS ,ykojM 323zu5ѣx>ģ|dM@?y4C9jB\DJrMI NEA}B4ҫ >cs8{2_p=tX HAe7<'TZINSIknzS!1/>]mi#;d; Zi_?uTd-$ @e5pGz[Uc$b)lo^Ev"$ e@mA㱪օ &7F%a2)) kex#S/^Iaiո{`X# THvCBy;$i"<2ݙ0ʿ*]Ģ$ (,];p%O f" ]3ޏ)'iC�*uW+%*M vޗ(Gy KI JB7$2)^*pT򏘆Tkp$$5]FR79+bS܍ܪ^OR=]_ [ϾGnj`#*HU VzZ^Fk[kHWH�p Xz<vG4buoO!b &itKl ?fR4Er$o :S$i Yx LYVwmt1Myp6/E<qNO?' In؞ǥV<1n)ӈDIX݌ɠd`C:L\c#Uo]yM3ufMy% oԌkii᎞MׄVЅ 7[Z4=rN:RrG{pc|YXF 2:Tb1DgUDtap ,4LdZ;,qfbd"quٝxFA5F)!jC%}DN0wgfIeْP GCN\<`ڢRu4,yבqS:�>3QpǽEJ ȩeOv7g4Qa qڔ.TT1> 9Geӌqڛ,Ka|R`I+~mls3 9SK#m!iԌqLv,vGj%mGp: vtJ'VbN=c zpE3q mҒ7#g4�P!p*YWrt w5Dq}i64@ڣigy8r00*x%Q7ڬ9؍"+xBN+`ijuj ƣW )8�any W*j'vq^=1LkG,0�r7c8nBPXc q.9X�:/JЉTӊ2! ;ud?~2;fyV zT#܄N" nC@E"4.ӲE KeXcxo);rPF) $J0xaHFۯcF|~Zz*c R wm#ZW�#';I!HR`#>P'!2W|GUI,0Uۯ)Hg36XFRAnSS&4TN8JYT`M ɩ"S>Փ-# NH 3Ȥ~lOjF<rjn?r O#Fb%@=GZ`H̤j&$:Zn1׵I/'�FON#@4H`I<Q܆=!8NjOiAiB޾. SFA$iTpMNEr{R 驖nΙSZ`3 8<bWx;NibB 5k%Kv9CKn7qXRB3$T9ר*$`*gz$`Wõ$\qkӾL�o &OָN <0yq!H'w ]8wiWXWrMo)LFQlr Uew1۽zhٮǃڼɮ(p7)Q}�Xkn_shP-#\ԯ I[Z9m*d"Lwݞv5-K3J4c'<`T6A'߿ �' "Upq=}*H�>H8@}9l�>1w€-'*$9r*\ǜU ]LT|9q ֢pN.dp)!L=* L bqI~S*2)RsӘ8c3S@099N[$q$�9'֓ڷ=! m\g>0 `�ZkFFs׊p dp($'.O\)w7/�II@@2݀\q|#=-b�4pv,f4G>lg%HFA4ǘK6!j#'9 4ո$s�qIkK lfNia+M�G8W#%{S&_䅍:{R!Xԭ9Yc/$/CS#w" $ޫ[\ mڠS%wo6M ,Y6$!K}B\O&1}j``C8ۢ]0*G�3E%܎ C2LuDI }5B hنEW̥>& �K�g7ʸ&LsJ$R-|Yy$S-o:O eqw($~go43G\:$w3Jnڐʱ0~<dSi@ȥg֬4 _',hS�D2.p:֍cMt1կ'Ȥ Ǧ(Ro25vҤ ]:G.wqV$@\gAN2$g.w4HMz`"`FR2U{M!tޖwY$@i_FĔFUv/QI&`LR�2@YI9X.G&Uܯ恁sM>&NqIw\B#XGiMtTl8=) [1r 4V?z|CBb9M y0x@wsMI-nRIWeR yh*CIDWN2};(is)ٲRqQ!%ulIwŽ Zs r9K(X0Hr@Dl11%rٟ{+{G: eC!G2ax|P,)шHy&C;Ԧq�9z:gO!1GhX\nIfAzu14>0xVgI$Lw�/6dޥ 䧐vASPTcwOpY@qv$z<oBL@4Ej(d�;,rNMK`ڬ: S?#+'zSUI:"IgSQjzJC_U ?@sxD2nҗlJprcdReh68ztqCc5bb@P$ipN9Z{G%_z҆cT HJF40It1"M?ːy}[@wZxwj1#?"A츄pumzVPHD憷hqөnhzgGzH$Y#*xpG"Y c?_zhEd0Scȓ=n"1ߊShc҈FV}%%ZC!�>Pvb~ 䩦6"P6dJ=~Ci~uܘVp!٩ zO5!b֝JÐF?2E |<ҀtTړaa�^1SpEFA-ޥ oCZJB7S6?#y8#Ҟqϭ0!*jP�̍튃OM*zp*7+p[bZV5jQ;=L$`vI��fs0*h=j n,F)0I=3MݑG^F@E& 8W#<c8+ @(`MR}(U= |\(1J2;Ll`>~ c)fHX>Ԃ0`IR0 et펔"VzprEhT]0;UKdvzD3VDxǥ)N�<!'Eqz#eH !N86UXg\ U9$r dԦ$#<!A1N�sҷÎyJ,㒳cB3fjdsQ x!I?AUrF_XjIUgc�uGؖ9`d`vBۉr}MHى$qҢ+%[PR"nlg54w6�H *�F}S@_j_/p׽C'Ҥvq1!:i'5] fb cK+?H^ا@-![H P1_Zkѩ0,I!1n Eʏ[D^jHeܤ琩‚͎MHsj $U^O; 񟙏AHe y .\ϥVIҎrT 3K֛R;ޘ0'+Ƞ =*6 !ȍ8I<ϟۼ(�X$C=oTīxy=Nwc%FKw\*<UV: e֩mVu |AEڣt5Pj+rT85;X_3�w]b'Bq>+%h| CIcJ}6`=iX.5C*ghVqQ\)q ;{|7$ VBlg! &'j.t-B�Lppy "xex8v*W; ø=kGemOR6zB ]޾;cAwT719Sn9 XԂp&2sS"ӦBec£2;A¨4F#G0%ڣVCBFҙJq1fK9m+ [dq'`[o9d s5U6Jaᜟ@> r&%[of39k:t[Utٓ5If8aQ76UUhZ]9Oc7j2g\,Goz"FLF^砩P#@G͎I6R^G]֐ QwNϵG,(գ|{ $8Xb299`A \N<-䑗DنMWa vp%s4Xy`�\p)\Hiv+Ԋ,Y HRGҝ=r[y^™tI̊6ԲKo+,1$%8"yIR ֝(Ҧ˺iHovb@ڝyw)O0 QL de$cHaQr)s21/<AJk)]b䤨sZs9#)Ӽlr=ei&.5,A#/84nOr$L (4849[^ |rmrIsJ Hx!!Tv:VҖ(8le$ B\Tk&Bj"K2�lWJ#aYťО{D8%{qrsz/OSP$iA tW=Ckozue*�9|Ӷ*+ ^<r(!}ר 0$sSQVV]2I(l%N^9?O<-/ /ԷR<ԅn|A}yx>rIn4(Oaj~)# h\7ySgkv4(A֖0W9{E*�+�E0W 2ᖋWbni+(u6ci(2HОi!22U1d@CH ̣pt6"r5,~I.|S2NE7p1{C"PaMNPA)iOz{Pd">�šޕ/0 'u\O2Ou ZI0~`)1S9rqR"#j�}2 9,rsTRg������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2017/03/img_20170308_155049248_33196894011_o-150x150.jpg����0000664�0000000�0000000�00000014142�14502137606�0027031�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 82 �C�    !'"#%%%),($+!$%$�C   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$���"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?��#N6Zsʹ bNW?;{؍ڐ�"{W4J4K|zIk_=ȋRѮIuPK:ИEzLdyŐKg 85=/rt޺w&L{YYG;}YXr2EKKP81G;]إ[|r y)+w,yΝG{?x'JObN&IԄmw-8dh�Sg捺0޵]8+޳7{jWܪp:2Z6~sD$۽g9$fϘ1CBdNhCҶ }GCPL 0ddY6CP)-M |[w61 ?Sa$�;EL#1R*{Q[sz9@#R"njm!GP^>aG-;úgl(8/Gx xlkmaV_ƽV kA$q ª\Ԩfz3<f1#|vYjc72Cs^5 gZvSHdz ǕLuO7$Nlnϧҳӯt\[HxXб_˵{*m UI5D0ڵzd<�w;O>87A𶫦ZRXN9!J�~9E5% W7x (R@ϕ.ޖ0·5My%#;J j#/\E D@$ol>o[Myk *@B:Jdi$Gjg];P6c5i2k4(\䌎a~i%tѩ�Elس 95n6 (?_zԌevֈ瑭T. Uh}܀G|�7ȣiI 22 {dR<,�^[Tw)Gq؎!g"sk f S 9 >Y>B0 _?oڱk'cMg\HH~XV8$dTc)sJ! jP^ nS ?'$k'e5Ra$οՍRH5iZm\F%ψ5U0?5549YOj> & @pҞT-!pI^ZG1yTNT0dpkuPQVGzų2CbОxCnƽv}"Hɬ*9I�ͽ2IY03.3P<Wm]Q;E JմQy^Q+4-N%px{Y7,$Ib%@9VTK,^\S}}nx/,wOX+ :ߗjfJ|A݁gY^[!|Vnǽh5 "%=osϝ6.VPӎT稬k8G*z9�<z /"ß֊cqҊDlT빆C{c�Z$|.OS[9UU 1onY3ǥ[c8:sJ"ޛGA\Wfj{V=Kd |anpPon篭/E*3!?Z8n?cv;2ݶw8:i.Ȫ`^|z* ux6VnC`߹Nx. _-FRXl>ְpcI:u#$s\p,5w?xj5u)9uu:Qv5:MW>YmstLY zukcèƛ=Mudz+ NJdgZ�<'�'$K~{�:-k6C":uƊYfz6 r&JУ$�`Wy'#53Ԍ 㢰o[.eMGOML�*˷xȫWf00>o)b_iy QՎW<c~[!U�NB0 Voov±UJ8d; lnOwjcvQ+ۏv*~6r6GEdX(FXַW�Zz !nʫ[ �8 1\ #?o}qօgZe]Vnǩ[>@zM*H &F>T$j"*RZ/t{{"l [p:qU_lb>\D,-ˏA^ϗI)ͮ-V94?3QGp?p9U$Whdw �kɖFakR"׻CO>[wI/][3G:3YEiF-J/KS~RãqGv.bшAFV%J=}s]54hMbth'sRԓxj\&}NN 27;Oem\ ƹVUԒ%|6V�Ha}GXXpW+xI/E)*9^5ƞi!P:gs.j&)ڊI>Xs@[ �!\UrڊW%\VUk,@֗6 Ցd/9V{ Xkn-dd>eYMj$~Kjٶӡ&#FAmM "n<.$ }*g B.O/QK<v5 c`ⱴI-/LkOkwS_,ҳpV.vf䙔qԐIf;S^hw�铊ta]le,v5]qMFek4?DzK�OwZoNeJ㜜׿I [Yw:Z 什)5h093].M_IHFEh׵Tev͍=<Z )"*OR(OޮƋr)>ފƞWHֶ&fԚ#i-^(. :^K[世&en}s^c'pdDuu  X9?zR 0/'4gI<G�#\'mnByE?u&ME.Qˬu=(Rȼ7 \objvַ�><Za=ط{ s;<y]G9MZ'98#tf/0+'+>p19:|{'Ό'?Jԯ"]4)ZR^ jTU +k9>VSA^|.*xU4XWֵ9b[F-Ucơ<yUص r@Z6pN~iı9}lcW!H0u_=)9IlsκsM &x% Ń:w(ɰ1Q?$ (@~|uVG#0Fy1Oc5+ؑa\ۓ׾{/tic+GL?z5džimΧi|G5* I-/`iQ]+I%Ys]-ZiZԒ]E +_c'>F:>mNg;kV1Zo.P= 6ŔqJT89$ qߦ+QS#&KƼ(QVxg{v6sH+F:QClJ+15+-KY9 a=Դ"y%c^0*ri9u_ gsp"{69=*^ݭ;` n\ϷӽԚyǫ>מ`E�zu|ȶ'K@󤁷9;dvڱ-<' [dy|J7;u9nqŸKc$0˸> ԴVe10 |:ׇ4{g]݉-!ɏ}zsֹX<I-H J|Ox}#Xaٖ|CA$�ql;jڅAy<Q~8}D<Ci-[=3B+88#qk<=kFgY� :ްo aCwSpZ4~f&xm-0ȁ7!4KvVw^6GRwWZP\[%fWYK Vsӯjރi)[xGúY A35=V6O y�1^N`f[V>rDVe F%#.۞kIYs׻mJ``l)  tŵLlCarS[kQx+a%ǔ9Vu%9v-�$`/ %_&[0!dr2}ye5mAhűc( }+-#7 [l#h'].KV墐Dۦ=zUI;+-BKZ`Hʎg|g'^_Zh&̈#;6ʖg9Y {OQ(o0dHRr>ec7J]F[iIfYԃ }(*;ԤetT p(bMigqb]|DұObX GER!38F:w2OKwj2GCXs &0�<UU@O \ozw�<Vd5޴*c E΃c<}k9S} #49 #SF<HŇ'5^-h9A#K\V 3SUy8(PA^}/6p: 9\?vc?ZûrǷVخbA+gƥ_sz%5l $O їp4l W,uDc�?PhHLzN2s?Ƈw]#[÷%ŵ !|8jX짵U{U/YwL ;}-rwY}l�GXW>lpwǽ,3p0 뛦R9k߆:u7m=N>+O6pjב cxn }h| ?^ iCdvtpR| >%!Q^Ċ]sᗁ@LϹk<ߊ(y/ؘ@sx�]pqg4QJ@1n$|~)'}8((s1]8NMSBĨlE!>�/ {𬋫fRI!VR5 FsOlf\Jv+9G˅1`d%H(M+hV$V�(S*������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2017/03/img_20170308_155049248_33196894011_o-300x169.jpg����0000664�0000000�0000000�00000026774�14502137606�0027056�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 82 �C�    !'"#%%%),($+!$%$�C   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$��,"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�K;1n?� OeOPG��k'CHɬHKHG&WocS(^tB8~ꗈ.ߵ[[ڡATuZ[Bܤ3.ֱ%öubpSoZ^g\6go슿(R+<W<5}%FO&+8Y\Pdۀ} sEّ$g<+5)c4j-I [B0x ZPxׯViж7Vq*/c{ rxz[{*8PJ-5a]a+OCpkRuخ?CCSk:Dzd�I XuH^6\GVox53T(wgeKWQ@~4kz2_d@7~z8&8t4''*O`hIF!1�G ?{W7mOknZ{yra=3kKJ,6p]%9qlͫd)`GqZvT#T0HUEJbhߎ~ըCXJHfyZCFZXiǵU`y Ê2GL }JhI0EC$e\Q@!HǿVm?d1N+r\GQPo:QTˆclݍGql n(g}.b zU,`@>c#6GP^34 գMaKme/hrɍjc #֡6-mq49\tq6 n)"<QJ+;ʘ{b-!fdwqԚ<]D=]D6S+ⶆU8QS :6Q&hçlЎ5>lܪ>T-xe#E;ev:ȭm*>|}2MF6uQڳ,| t'U/ۉD��Y܁p]ĎqaeosE_ Ld1pwݵ* #_F3V>ctUzǛufrZ^-;5ykjsd3?*$qJu. Kkr[k,p`qWy滏 |=Yo5P[B.z!Jj:͚"2`E-Ԃ3k61ƣTn&J\ic=܏kuOjv؊OP}5ժj*3/iQOZ\Ud{;9&Y!;ߊNS<Musirʿ.UD+II<r rhhz4(l |ğY Ok&Z%}}� )<YrGL-CJ,-eeKb(�#ZdG;N+A}j@Nd;Ǩ�ꆣ 1j;9⧆{kA s(|�ÌRdlщԯzj)8`EULXYteʟo>~YѓF.s7FkdʒG('EpDc#V8Ӟ^B&HqddYΠYE$WDg}jnͶm;h$Z0}o)6GqՄM>Mǵ6kiM_b化 e̼r+^u+iDp K >LHGFirqjڮ~eE"sܻZ\#H  Qw`F =A4 xɓƬu}E+m!;ζ-bFgW"kFRlylFAX_z6K}Vm Ei5AtnotGy95(�Z2QÃJi\?Hd]B<pHצ0ik=Ӯ`a�+{M>mOE^8kJYhҫYgoj/XLB%nzX$.r3ҩϬ.�\Τ̈́lӊ|无\rsaj-w v+:ȲNeo\>˛ux{#jkNG: Ga&GҰ-JU9)ץIk'}k?T:m&drz0y%`pGH,# C?{/ZC$WG> H`�>Fvf&][MfF@83]VkMVOO2:ȓ7G%xOJ(r*Os׺{P异Djw\{^Y>�b?$z_BxWZ<jf3GN)jFq*# =#ru8UGF$P{\+v�U/G"QRxaaTˇduO4C/^ڒ}TQtQGjCYFA^]ķ6~a V`<Tŕʤ_\(?{2j c<N3Vfm4K֤GW"ʬ0qL{≹4ǽsHGZcx_m1S ʐA wG:<T&3MZ4ҙ5iDEOE.A2'j#l<C<ΪO}+F̴k[}pczjFyǵjVUipN{]8ؙ;tv]Sǩ_ңme'ho\ԟ?L3OO5vaZ^_Wiu 1A̋2?rGLbjXBY 5&x-`}֥<žrH T&caz5ƻu&%P};Wh$VjT# %gh G5ۯLW7ws(SC&f\[[s(5Ne]gbG9ug5+F I3cLn QN3L9#]#ǭs ǽdrY\50ʹnF;V! wG'r$ڐ3wBfyXI>dz-].b` &Ww:fc]7}*K)vr�)(o dc˭>-F+jzʓʸ v&]7Ɖ+f=<uw"T 8#sY C  J7_M2p}qK5C絜#11J17˪a pHU&XI~awQZͫE"b�e9t5P6 ?nv>ԚeR^8qSAN钢 b�{Sd㊱YfjrFg~$T#;dR=jRqNsӊ`U1H*pw\OZM\ SQ]!u&f4!v?]7`cy WA{U_j/7'S<]u)kSD]ORϥrzЖ^M:kSsO�RY_wN\gMg�=kGSCn  [8A2v!o-Mp*_NJ�JҮ&x2q5`vq\ol~e22+RQ7}M(Eq)S֩2fyƤtKQcBqX b\;W#}\~Ⲗ ݣ~u`5|Vu8\>--t2o q\\&S6H3Ls<Cճċzք]ʼn:kQowSxZkyL|'\i j�Cps#*e<vmBadFfבʸ}E\ӚX C P5'&xs�=]q9$YƉsHLlN�VtnLaLv6OWiūC:+24VKG?-Jp=v5Vեi>,9Ǔ/<̘$km>sG_EtUŠ -cdĵ07F=?M]%+ 2t?UcQNPKaAZΈW,CS_Uϕ9?!~o5:&p89. P^xh_Y&clgzı^sk×Vc�QYvʣϗOcPkЏi7i}j\GL~ :Trqj9�u$z40*Gx^OrDkм7ߟtckl2c]$%0>15xP0�5VkhX ݸ}+Ks*0ݢ>+xdFQ?K,b*LҦW8@622krb)Jtgx堄V ?pO7q]O,r6wWZR:d W!-4-Aꇡ략kTva7AkiQOMGiii\FAW@EZcby>18NTO-?Kʺ".w}3Z1rG JR%R4d9xs^ɪq9yri&f'̋[dŰɓU.V}�I]&J mAVh'NC^ô`{fJpN1GԦg= =*�q^wd,�LWG'5wNMGWjh,LQP+ W}Up;#^m(O׍/Q2©>A?Ζ/ݧ [KɏXRPpH[jvpFyz<}j}{1id+fb�tFވʾνk1.�EuZWcoE8*x ֦}󟑺”Z^G*jK4CxDx1:wVP<OZS{07>Y.+.u8$G A}}c(..W_1>~r42pv=a"D<w=,cԁWr?*>J*-mp,~e V]_K2?\JB;& z ;yZ&QFH%?OQj@?LԺş5"W_[㌊-jJHe>ԝ=y?NJ`[4 m8Hetos-F_�Uz>3סsa7K;K>mgHLqʽ]+A+׭Ky2%JLr0MkV*-ECVEҼWgsc34WQe9+ӷZϾF˔ ʹ5p3J>'}V?8Y䞌U\ɨuu70IlY jֿlHqqگ"`5EqbX}G�ZGM*Mdӧ]xǰ?B+܎~c\;B(Fg ~3^|m%8!ɱ·Z-0 PO 2fQjTJoiQyZv¦0};M>MdUc`]&-*k�p3<W;˫eOp.=ݳk5Uٹ%5YBP/3"u=jN TN: 1q$/jVbp*-^{+X'iW FqWJ.ǰxKQ6V1)vwqH>+HGJK-WYI-xhrOu|Nxt1 N.YYcV^){>JzbH,'\)&\]s+JO<~sGֵ!c$B Q t#g+;HV)mgr8ʮAba9EY9uZpv903ɭo صݸ!\�g'[xJkoߍ;[x,PGcG?ae%b }9/gs|%U2t!s+bGhZ3^AraǗF6r3G5g\v,JzU(;Z%1N9W<i;xBx jw6:c|תƚXk'G64DBA8cb1C�:\- Z9K:`C$kƳVK{Ѹѽ y/SaccaՀ5ڀdZir~*@y=ܖroB}=ku*>H޸=5Ԗ"l㱬+ӂhF5+17}W/o'0V|Xn6H*5Oz.!�zNU2K�dq֢ IOLL_vL[p?\}QY6 I$۠vH"'$֭kzBipܡy|W'Ͻ{yiɖ)g^42hºz*44�/+#q]Wiz1r n\}ؽAD�O5kSKc$%RY�9Oҹ~|I%NWm qIc|km�d96{w|5F%¯N\< 7R2t35K4ʱ rr]K(1eVj_i/-%6v�JkH_uv%*۹J-\;ZᵥO `HҳD]*v7 Њ`] U.7W|w�19lsWM/KMNxtYqd|{gg[}= c,_}+svΫ;\]֫>!Դ׵tLֿ+wC-Gu*ZtGHzLl*bۭg+|mTIo)GV7I3 z^E2&MΫ].3q:ϳƾz  ё4U8),s8F67nA,X*MfK$ZF(b<ZDi\z-yf6PzⵆUs{,oko=U2˖$;+JXmSXL$Kdd~MzVB#o8Ϸzק ~m#u(;3͜EmSi:qv4Wl . g:׎ηA4\*3:lM 7ezV^G&=ƋnD~)'�g8BNǟ8[\\>ǽQ|7$@Jdeg;35j2i6;fvl(Wu6=7vȰIv1bKcGDc˷Ky#t QGC\3)EVi ^Hs5w:uᴊ$C}JZ $&e3r@dnZK6vXyzH\C ߛ}=:V|wQ&C{;~5CRBBHX +<CQZA9jx2rw}j$®#[~ScS\uNqySueMv (e ^eH띿ݫXY8_)~Ya ȡzW;hrZ,u֔vFe.$d]hu= >?w#35c5$nXt"+s`vaLEijهF3�}Wk>|Lؐ6{[!T ̟WE'vy#wV$x78-<sV 4Z,Bm<S<qf#)sDԪZ)#tWVR F㡯jSlykRMl'_2"2 9=� u "Pi.lWn܌HʌgKJbWMPO O^99ҡ~А'# &/MO=5F%i(Tr@ڶ ?u(촕:N R {>o Pƞ_MxhPsC&+&]|t{bxB{ 8&SYqUu[2=<]WI >ұnE(4-'e`\�r81)pKCIZL-рz]+k;_.tԣH\yA>+<=k{ Ul~uX9>d!$+G7c|ڜs˦v9_5!#3N;WtO y{o*1VI@!ч~oБA&2@ms~hVu ddgITH9>ovҀ61N >{J̖A*𾯪jSZư]鳶sϮ+h7k)d ki?@m*|@;GJ {a%ڢ,nY!KǙm1"_N(%Fhf\o\�<}kn=R016@T_ʯo�\)Q=+'Aֽ.B/#n�;V 5+xk̚ƕ4PS'uqMD^2FW=?]ԶQ[݉|`rR5O,$xPܶ1ETZ--͒+ܶn1'_CXCxYWI5'\d7>-vC|a$(ޝ*,HO٣ ᶑEi*rqk3xH6Uٔ�:K6ww^Kq>_v��" zlf˰|ݩ<CBM~]lJdF* 8+721*Ưcf/ 3sY:{ռU=a1:<e%ԚV%�95xh3u1{cC\u!yN_E7-kG&61JcZ-ŹUZ1fAZ|/}dn(AY7t9&o 6HXartpmT"|qr:li$c&!f@1fE$M;98"pzRq)t̖I; RD(ñ1'=1֠ӡM.8qT849$EzWkxrN}p+2{rvqc-I1HiI]ϢM/5HoܡNI\*.ҧh�!Sүi=bQBAGF*ٱkڱ-u%{$.\Cۜgcc C?*WYcP F3sCI6� }k O׌xWa9_Zs2H=?iH-6JVT�suhBG## Tp"$9=4mNDQ9) n|)2r�)xsOglcY%;YӼO .^9cUiO2M>^B *Ж;dī~5BArq�pԎ{= 騂#,EާftV8:G9dT^F17$sPLd(xIp1׎֗\KlpޣriZG˜~Wq'5=Y7h6zuKbx�MO\C1*P(\+*krob=}+oZ|18*9V-I� ^pǯ{ }xi]xHif1V%Uk~*o^nOv=nv<T4W) \?׳ PIx;O+ZmN rQGc<wR%)}r1f@LS,{W&K!ʟQ5'-ıDg;@!φT-eRtqo>zDːw@ՂЦqWdQ=x5(lIkiNdܠ5[(ptח)mlqs){'ch5̓#&p}jOi ׵K4>}5M Mo!%D)ڧ>B(ǦG+ T��]�B=$i, -;m QQ"( |ēz-W� pl6lv(*�sԚY*du>uA UxzbGU~7Si^2#ҩMv�r}2Z:; ,dv3\̑cjk}}DO՛eDzE0N~~zJ@Q@F�v�QU)Iu##lsϥTy9!�#3N?GWƙ j b@lmJ˞ YfgigSt�UTA:sO֫M_Vr)_px)[6�Sȥſ_ZLR҃m) iCb *IuZ>�fPD1G*72GQzTKCQȧ:~t�) H?pPiTR�?3oN=o9?JO+'v?����mosquitto-2.0.18/www/files/blog/uploads/2017/03/img_20170308_155049248_33196894011_o-768x432.jpg����0000664�0000000�0000000�00000152341�14502137606�0027057�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��`�`���;CREATOR: gd-jpeg v1.0 (using IJG JPEG v80), quality = 82 �C�    !'"#%%%),($+!$%$�C   $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$��"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�zƠ@ԏ%P:?J[X/沺4iq(DRźZ?gK(CV:ODaﰬ"V$JhĨriX0tW5-D+:}#RqH&d(?JH;</<v t�PG5yy>uQ‘UPvJg*G1xrN0G$|Ǵc?IҬo1\Vx@q{e\ Kaǩ !Yp=ӥ=l50*˫dbYJʰ 1T7asҶ '9sڰ]3z}==+dZЅP[$a.AyCKX; UI 'PuT<֪%r}*gs21{3FdY8O@qxR) S.c5ёp)m{ve=ϥ#5ܡ<zUҥQ}*AnXt15s>)qZӴr?0 ~`h0*ln `IcTK`±:Kw[NAatلm߲JU'La%fg:^Ň;e^wVq&5˧ pWi=kkM28e^M\Ђ niz/OJ}bF>O $zJM\vi'ЊYc)޲A/!&A%Xp9Z9[KY83+3D$RҹwF\L |4+2=IՔG =R=\;4Q2Hp~LJs3vg85e-cNaj1D20 Igڰ(隷Y#<|ñ"Ej0ɧ%C*$b}9!9�F2 qGY#?)ǧZ6bV#s?+{I"MqOe+Jfm덿ާ<{%DN@Y88BRbemKq zf`~aߟY䃐w(DJݺO|*3zLcõRj7ꪰOH2lZ\OYyj֝P|Z;_U/8=*T7w<hh9jПJ''�z i9qEWC+.V^%|�gҫy8=?) (gPH!8Jj98h>0I =OҚ#b9c#Qy{&F! 2G>B翢Ӱ) =5Ђz߹S :Lr=q z= '>i;šd"XۀW5wwȭ85bd�tRڲ^8 hp L]F*3|c֭ʐU3V'IXЯ#p:'5%܄뵉+8ڞ$<z6uh'W2.PsUnK[=sRS<I,FZ$2y]&-G ;V<M)Y<yk9;{)8ۅ^sQ'#Pr2 랾º=dcN1zT]mὤ rG/O2{:qҡH 펾.)|-x�ԪpRHR9QSOW}6<YI3U3Ecïڴ!<�֡> O]6h~;?F%?¬LՔ}Ҹ#ۥX=k&WEA+V t'L,}>ny,{TŒi*שDRS0A޲.#iX>^@ t(`j۝s_i|W<+N@;_3ɭUEARՠ\㠩l J8AsAlV,el=yJ=͛dB_ZƞYi$cG;w=֫o v<mZǢ-9ۭQ]I!‚{;b_ֱlWbI ItB3="{q# OjFlL=D}F2 <> CH4x⹟cT6e]Vn;Vfk)Dz=It9D9 _zm1gl%FkC%X-$#9{VMt$olf=:E8&NMAel?*vܜdޛ\�5f86[H"e{ZHf@nJ#%TwAI9,:Rd3[Yi695$z$t%+(,ˌ p~sb� V1eFؒ?J|'I-ԁX1_%g$nrD7qZ)X ,+<d箭f.Ϳ)i*@8uI[7qgkhJ6*Xw|'}*;}1EIs0daiIƑvdp٫IOQRӖ2@W2 Eu~uia~ǵ2xqTXH4l8܃%9SJ߹�㿭sW'8"fS\毥>dc:Ztm9w2@|~E3ʮVc.aK8ea֙S.^۩_ڱ?%b)[ka ,9Zͫq<:R8#[cS\yCo<> =zUG 6?շ�Rћ1J3ʮYM忔IdnTJdV䓽AK-AzT4JgQc4.DXn~a4gRB Ң,޴IqTX7!<SE;)ă ZFVZ%z�:7VPpjUm1Xy"N:UI=SS-n iA>B$ g#`@MTߓ=*AcyG y<dd VH0OoRHy#+01:7B; ƲASpT2D`RJH�Fmp8lUGyLtzOEJM}I,'?Ӛ �#(yP2C 4~s=k@Ē7c'ۥE0aLemȌ{qW&  jأ{SC׾ R$˖8R5QRH}+wJ},Czdc皀;OYer;Ij14gUKErwZPkA)S?BS!9$E&sG`T?)4A^إ;2;FSүaGU.m$2zcCQrg\h&Mg9:U9> F=*ŀc:E$v0 a ڣ;Zʯh;3yhP8�8{Ԃ58$ .ciRގڙ`Oq)LGUAt\rA]R[:oPj}h̹=(r=*Tb}*9)LRz)=;ӾWH>& jlgʗ!#*),zeT`q\̺;sq\:T n{IE>!w9cJNvIlF;&xի}&t ·,-!77rEIuu @Mk}*u [w&=U=*(-֭f�Q8bFj9 zںF >I�d5Q|oZ+j…&9:}"1ʢ.ꞛ%͔{06�}*vb,rFt]6�O|̿MjAPL#!)ZLAD>[kVd# W޻8d3ӭkX:a!o@n36v, Z c8QDK0pOj $bٰthNJ 5lО0 !ͫ!O&@{ ̢[Y 7FB?CM9iտۯԌ ko3;:Lt 2"D"@*Bt8i\Xse n*8ijTΙP{uϵ4&01hbADY0%`@)$ IC9\'fDtvqU"]yUMJ>6ҧ4u+;rV#h# ֳuM0L 3UG6 ~SpH憁<ˤAS/2) 5},H uK13nǧMfixSnmDTV�c-E]SXJ%rZ2êuhLC.{cRx\ȃ:tуG1op҆RF=X#kS<\qLnR}R7Ҷ{j6AnaObGO ?Һ, U\Lm4i ]MLьbacֻK{mNԵ"#VaJfA3Tdw&͵jGQZ^,ʎ8#!GvdQ8 hO4E?Hߚ;"%eSC1roFf )O88LFF�xt<ӔզJ?J|ׯY9jX98!2C� OP*?4,$¦VU~xR&x?xqMm$`e {$`ßZ ށ}q@�i/�9}=i4htʐya�g�V-RYqñ>2ڦ# 4NÍu$q�AU�SS$;3,ιܓ϶j4BcڶRI gU, En忄?ƧCێ>?Xi"lWˌsքBuu 5'#вr7L�Jhy5Iƍt5҇G\*Ku.[~SR6,xh_i 8~҂qIЬ:SC>!(j yM0@8QM }j{DZ5z3GLJiabkW~UvVONJ[h sǹ5RGŢ$gE%Tb�]*4::U'E!mlzfD)4cH.=xe*zW>E#Zڋ**8Ȥ6^iV=zԑV<2,9qfT-9t݊N~U,w+:G5e?iM@=GRLm 9 UKH9=L(vs\VOHە_peH#ao)ZFU$ֺ#-Y#k㥴V8WLNrwp+o6i'YB5PKU=7TǝtؑEt௭A.{De>[3}0)X.ixrB2 [b,@ C*Sznȗ*i 0?.z�R Z_rXϢL>M�d*}*[5jJ9sZ^qVV= jd�8) Fá((?άy\`84zԛN6+6U {hŸM0s#0XU823=*n [2@ʏ|u 6$qګj[zD@*tɫv bw'6!"ۃSil(䞘RUOk!W qF%@ Vկ;Ge9]$7!zn�F5J,dsI␨`A:&K)f][OmIXȂJ?tz#UVY<OU>hV10 XcX_h7d?']ywiIuʂ}kO_\/H�*/aIpȪZƒ\k}6%JiA2sT]cȑxF?wUI3m>Am1-я-<cIbׯCM6-#OcY%uXۀNGcU/,ĊHTE'jr '\۵g/bj:wE(yw!ee6F7Q74<1Nxހ@RA9V^[$NUI Z*<7sF2fJr+=mTn'J8WxP)�P;PZ,)Uti0z�*{|faZ4h GrE<)UU}!azY,W-~_CSlc}t1QGqPЍ'3u_1$Wr'!:&l@{ZB͸}*I Sg:/0V#4*3N};ԪM  VvB{hqڮ@ ZD2#*¨.Anb7(0x~Be*2rw>Ҕa=&� K*}[�=) s}9�c=O=}MIHA\q߰<-@1*yj2_j�`Xd @R eϖKq% ~Ve޸IX+ycT(pz?ִ!Y_꼰5IRG4\er+%νOR%=ɫhu}}U<�123.AIQr+' ~CX a�Sijp@9#<ǽRd3qH�N?,̀zvb=7{z mh^AĉA\:go8=[9*xfѪwl&=IXA~C5 N"ؕ8e/.LAҩ4<t5dX={~Ur;`cӵ! d�hQCHO6èS�à ;`:Fh#i'\,;^1 ]3r=E+ƢL97aLs y#?֝ÔziLץVIԜR >=*w%U Ly ztڮDF^øc?╮ )aGor=1UEW9=+dN͖%ۺ =<gQ%)c۞d|>sHo06qҳ7ϯ?^KU$ |;i1+yީb’PήrGDueޫ9Z%dJ܏q6(*:,VӑFSM=�V-1GJ!~sXƔ 8 $Wҭ�ֳlrKS,N�#S*#p'2׊O+9<n=98QH@;֕b8S6 NT>(B$p8rFaŐnr3=9.hXhS$#,sH񳜁0*`]O< ړ9UpR�u*paݿz.7MHOkkb8cQF*Q*= {U$psjݎ2�=ˎx ԤwK"4# HFvzfdqPZ+xsGfk6RyɌg> .Gl#'pLY by8K=Msf$lH;7 }VBB6¶!f�<,IZ,SxwC2,ڤ𮮈roV;hc OzvB8$7wH<,E3V)?i!p]QMDrG>aư;m]21?\殍L#!s5tOOTQ�4],U#Lݓc(2jW ޣYx ir#]/OOg@9Mikhi\@h7o De+lw!fİUg)WWQ!,jn>J_wȪU-",Z�G0eq#ϼxqHx(gzEsw*Cl?Z{[a֭X^:� 8nyZ1 +!u']E ЂH4YOzI#(]pk: b NJWO>p2<5s)F'͑OPN\:&ѫNH~򲞜R,\b5iZ9 +*'(p*>c@ke4`x� ?p{UĈJ7\gTcZg�Uya=jecC-G6ӏz5A.1;R A ?>V zANKcg@ tb9T4ɕ�0=5 <(=f q]b ۑ+YIpW'*{I8j�}h3'#թ('?ƘQ\Q%wd3V( qFH ;BY9$vtT8] 1�VDg5ibR]:4krKfNa 9?eg'b\yAS# e{$N#SɉڵAVH#99ROt<c:{Ո=ARz'^Rn�{~_xeFG\w'y"*a܄2l ڦߘbL?5{TXJB_֕`SOz&1ivw�vu+C<qkh玾Y׷\ ^OL r8�=*E�zҔ ݽ3 ۸{7HZ- Aޣ1sd8(ϭ=L�{UWP=Fr{Rw#9psǥYtnEbԈބUȯzUszTDFG&1X/̌6UIX9�zV:y\Z՞io$9'ԂqWcWdO e2v#w.8zS'ҕH<ZDϘ͹(ˎEc[/>H>w#jZQg1-}sS@[io^I$/,;Xxl K?* [ȫlNU�zP97: ޔ@p'i\V,ہ,:tS}3T1�eK*@Wp&@1ӂ*pz�h?2@+�ӝ[iNCrI9^ PNJyA-3V#SJ!N 16_ΆumIĄ;Oƽb(!�:�ax#LK-3;qڷdr+ѡTqU"1c?Nm⨤YϯWt/1ư@=Öq'V5na�z}=Fe\h9z;|>8,}r�@3jƗ#KE?ZpgtYHՐ*Ilb ۚ[xjdTa~_v O{Ul"(8 P$U'z\]) '$\}@eۓC Si8B>]Af9qw{G&pԞT,7qv oQ@U|ҫ6n13XWp#M! :'w<Yw9ӧz+m>690?y⢷[>DdGF˺Ե-CtFGٌ0NE�\cTGM<3ݎW$Pr@^%ˌ\ܜ6)5y`TRu \:U$:$`9f>i6z}*>R3gbX~TAQ!*iE,;AہΦƨ�71T1[Ha'#Ukb6\E';)率Θ;9᛿:8&Hp71Ocϔ$)?JX<*{xV1 ߝ9jEY�&6z׋Cw4XTr:ƭqDymUǭhYa=M6 8Alg8r3ABU_Juy.ţ.tWsKsr�3rq&s"0 _Z T+luS{)bl2En(n*b}Gn;dnP`=S{UXɖz Fܰ8ny"$N Ry8};."qzqRi.UsRyv7"DݟfsTn<瓟Ҩ6^; j2HÜ#ڦùSH9J ],I'>ی׻Rq)1 !�g<0#}~¤cw�qҜ$ݎ?֣ʀ;{SH } P D`ztj7#sIn SV\^1LMd2tnFC6ACOX* ?>DS[EaҲm1G=+edjo6>X}YDN:ez3YwN*?P{SN3&PLSZjQ 7}Sb ;v^A c'íhs>VOsF*2N@y4#?tkt$dP ]ȃdT=;Vy?t~j'!}j,]x8j2q9My}hp挌p8#sBoǽ9ey}8@HߠS5d'<gݩwp:x֤<c#e+ 8T6, ht�qNE%#=+̘t;yh*kdnvVL stU[%GjS͎&'՚r2<ҢA4`Ӓzbax \g2v֏@zzS�dI l㞇X,sk'pPsZ=Õkfh}ǭU1yŘd&aܹ� ?04Ҍn0}* S)G*@Uc1+s@VXr.r=j zcHc$?Jr-&=169/;fd`4POuZ 9�RY�qJ#ۻ#֤R9p m5�(+rq1\skN7v3vgi#ơj`<ڭqEf3ݓ^Um_2e[,<%]#8[6UL238m#| ZW 2x^5]K�$gFd)ivUQ+jB6`9%p9l(8A:RY@[ ׎RkrsU.n؂#pCx T.g#6t�Z/-zT p2N^fI|1>s??AHAbN y#i{�%s 1]@_$qFY"Ǡ>׼,!2qqPI1W. pޕJD9tN01I+ ŷ2zV 牭 9 βMjx+F'?`jיce)XIصjw[6csUzt,|>VZ[^rkU/SdeQ F:R}lmb/~,mA)9A⳻-2fFFt<ΩlU͂z]*]N�jMe\ZŨٷJwn^ŧCn*31C%`:SGxTrwpy4QHrqxuG\@l&$4�(Ib;?xjrpQ G�&Ir\8�µZ6dU8=Ii{B320NHD3#@t:&j5ʐIZCe;JB<9ZOK)@y%t-s銀2IɌ ͿeӐ?*NjdOEy-Õ?kY+ԩ.-g$9#GO�VǙ*v;IN"L[pZ-u2],˃1ȫR1;cӃS1 aڛ=X.yk*1YFn`ѷ(iWtedEfO^>e籭K}D2)"z�`.> ,2c}'CJFx9E$$^N8沁pGZ+rr?<&kq:{a=V|9ZFh_!z…�֤t <iMIwp*_zp{s* t):�xyT�:H'd؂ozU<�!M%u� ֩39DGl*˿#<`/֥c>3M@ r=xHqf;NAIpyoLo^xp6uM_`BzT@9~ªFzUX0>cqT xT}h(J N=O<ps+$z8βpp6^: x!N�.-HG#>k�(I<6w*Q*\ӻRq)Hԁ@tӐ;AWnE =jZ-4W@ 9=dr3 MG}:Zz5LKF *R^SLd_>X #k N⬫r8éq!#RUs϶@N_cLLgC;M N^< )9!m(PZn Ɣhf$ir*@ۺv~FFg�{ 8j}xdm=xRHϜ)qLr85گ<`r*6X&@ԩ t8t)ʜN3R$p;Pdt*]sZ,=9.G,YjuH9M$R@A_Tlrl;FE�}Cʁcn]6E=RZK:sӽzfkxb2suP1-,Ou6νF[>՗#m\=Ӗ8OV-H[Jz L�#ҶepVvM .~n>q=&$nKN#l008}fv`w2α۷+:Y4ٸgUO))=}j)&@Fan7{UmAzp}j[-!L#T.FYv9Ϸ2w)sU !sPv%_`m�ʩbA,|36hXrkI$1l!6#+t+nxךE rJ'r;լ+`Yx�g۹'+T`uE;†y0;szΫlL.@�9:m ÊǙ}(n uQvz|,u~)f'ҧt䰱.}kjHpF{-qmէf[ݿ?]Ma{f�#k[Mo+Z}*R4 mB�Q[AjqluT Inh)I>ۻR0?ZŸ%X h%|� 79uRg8$�9BJ~]\R10b@0}@ B9"ldnpEB\aޢLdZ)�YcңY$zA[ZB|0+6i-%v(*c&hw)-i=֖"Yleֲm)k&NYЬX.#0h䤁\2)FyXq={zZ8w�<m9qT=?Hנ#moϭn~Jo^1e}-,lW/]’;�H=?p87O9DzT}}+^628h_sOaBEm_Zy*dg޷ �{flYR13(]_܀NW֮A85Nj`<St2t=1SpyVQh=*խ�29C$"`{p1VX=06@dq@' )\dF~_0dgw4@qJ8��OA*Z-12rz p=?1qޕ_N}O0$c vcʎ?ޤ1',Gp{K0q*{PG/ا|`ch3zd/ fy[95%LAjݩe<r'r#m9U;zv|�0&'<ZG Rg#>�NGJă}*a &dd(F F_#8=;M`ZOp}u[9sOp2;թ8L eW:~52N2N=[  NܟVd٢@q5^XO<VFS7je6#IP9Bg*`NxUyڱ)A8^MXs�F3R\E6Bnpco 9zzZI if>QԡUyn{`9X8*r)K7 ֞r̀ (g}^F;Rc�LH�иRqJF4H9Td�NyǭW3W"ȃ#PʃEP�#1p7q&t<VmLsTN84m z)0 }#D9*@<%pELRBl84S+NV >"ar=jHe (�۳Wܟx u=e2Mu(+z/c<W}erť~ i23. *8? \G8igN [[C�8>;~=jc/Mbu~L5enC0qqT874`8C=3Yɷt)�~f o(UJ 5#i$܌K9C&bܒ:ơ$ːg)a[9] (`r9|ϙ 1:еځ۸ck@Ev\ >fˆ*e䓟ӣ.fn뎵2̰8 뚺ߑpF_|/a.IPyk2np:Yv5Ngٝ8٭$dK *.ICE�mpMzԢĶz_$J#OKmbA ;l-c=3ZJx^+;QB')ѩRpj[6Q+:zzUYcڮH0OSAEKf?^ E\HgWU*4T N錌$l7U/lsڣ8" xNPzRH+-*t4S60SZ͒!3>aY8P3"F^Es2Af緱ƭ}TB;WE)ٙԂ9W_ < -r*<NBoUm,Zֶ-owu28Kl.@#z~U6]+^79J-Qť2�vb̤"'ߦ:,dTn5J!rp=Er(3ֵ('w5=5rJ 8d\y1(8ϥ3)DQUY` =sh; '8�Jmxv}}ϥ&6h=cڟ { :̧=IU<nt'q1=(f08=%vrzGL¤N{qϰƆ0p; ǽ<Zpx cқ IgwSxS@FT'a5r+kE) 7?ī۞J$K*6{l"!jg%sU$^Gjܮ9G>©"x1tQYD8jr+UkTnqQ&@ ˃lc•`rG~¢9eږ3 fX�׃֕NaQuUk@)ܼĚUn�sl}Gj|iܞRnA�j_à}}unt= {f#Ձ ݏR}*~pǠSd٢]?HCOn#Z=#^mR_QPz Qp1L#O0'H(3Ҁ" ZirX ?֘g88<;"&`JF!ȥ `qN*ȠW7� ֡t!0TULKC B :ƭlx88zH.Pn 3%8`G>֘0 R01AGV?.{yh$`c.�<zա1Ԑ簤 gzTy"7;�#;@OtU%8ڠsYR76^U7v,6'^Ѡ/wR޳~�hш IaVH � 1\T6"ïnjs]s|+xѣ4s~Cc6kU\ϱ6Rr:.$.QrSL$T�p*zV $Aq!ʳpTc Pq`N+ `d$2TbLpOfQj% %A7?j[|2�)ǿ5F52zn�V`cVFr$G23֙#W{S(NN3v*I,x p !V`ry+Ա$3x*'`r''W_1Zd*XVvGN]~j$1鞾B+?դ`:-?(5҉qpZ]5gSƽ9lITq9VfO5\皜ޑDL%J(v$g·:9?iJ (.֠sSb QӤ>;]gf]35zV'8A;zT4nrIZFB9j} HP?Z'"N&sWrWp<A]UK~u}3^x_TC8#ֺ I*Hs639V岿]=$m#0@>?֥ m}3XR�vnj�}=}+Z#ҡ; n^حyOϧZ9<VգN3޺sݍ;BiG:mt@'m'dtz'59mB[2:dI9Rlck%ei�g5xAjy"�PGs ͫ ֌->U>Aߓ^v$p2{Cy4d4R7ӞNTWm{Շ#s7Zx=MtO~18ԜPЎ. ?7V㐳~=fC.Q!*NXqxH!Nz#pc*4`� �(`G?t5,8d08�tz{o\%l'0 9; Ekr�ă*q2㞦ubu'Ҧtpk[\;ƺm (O>h~YSxI{U *}:Oge(V,x=L6sU^_-Gةk3rg?Zf9!1$y=,ϫ83qy[#(gaޕrvyV OJ`{4?posR0ǽ1)N0NOjw%xc�=cWSߠS~OO)â@ҍ>Pje�I. p{Ip)vҡNIsy8c P"2�= әpv)( !#<r)/94*֝OzSg~)1O080C~Sьң#ךy"P2Pb:�FG?ʯ0,U&#CC#Qy4裏zBN<F'sښ%US9 jv︜tgl܏J| Rg1ϽtSWf3vGUxT)R[4`e#9 }@n`I^#<yǯҽq6IoF'iW\#Ɯ`� `u!�m֛sڻgo/VZVW8RM+sWM (G$c\䬡Wi*'\fEy6X8hBbYwϙ؃^PP(9ֱ6D T@P\/Z�+�sjhA u�ZCe@7d'8ȫF1nF�l|'Њ&�R {(CV6"_'6tp8hDʂ@9'�m3%3--c+t|Y99|F^[y*PcGy4.x}ѷ1`~ }}O[=hՍqVQ@5jH�RP R;{B3sQH4ݜE#{2 )zSXj2ߝ)# 2gSչ85B=AƊs0ݏzSlc5楃#ÚfSCi GEc38ۊ.G\dU 'h�jYI"X?КS晜HH˻EFwSRW?t sUzv%l s!Ґ^^-̈́ $y#]={՘zepiR<I3:`AZ$J(VZV<VfrF,:dsZ0;I�YP�O>ntlϥuH\x< dR98Rvԫ<jnJg u;S?: <\Sn ƽh!gڸ/x7o4$2&{w"kRɕ]}iX9!M3#?ƥ)^ d}Y CutCO`,]8/ÿ> J=]$ >^*GS\⋋b"\ݜWWav KfNOOP$z/AjBrx?.6&@?SYG'{vA0#>kHQ*ܖڥ+qEJ UU'#,p [W!1WImqTj5fG 1U}26aWd1j6snvMϣ$FpFOoJO!O'ݍvǭ[I�øje29$_Zp*׎D?w'\-piP=Q*8=9 &zp t&A<wW)$dFy =�CսjT#n3\ zS$}=)L11!j^!ΕErɾ!sDYԘ\OD<Ǹ2)g8$`P8lfA$.jP瞔Ń6h-'*8}O]zӋ!J@ >nOlS<|#9% zXtFUAy$rFz8'<jʱ00RqҢNL{UA+�Ie@p 999n%Z^ǜxx' w.vMzmϣn=]u[b[>a� ÐFa:xHJ`1eW?XhQ9Ǡ@:oyv&vRg8oZ5 %nkM[۬Qm`cֹUURpXGa)1$f`CI9Ur!:{օ˼ /ϑ#d]70O|W4(/L&p=jĹPB 0Pr(95sVd/ޫ<j}0zj0KzmO8YvdfB `pGkMbsP�: gַlzVF2c0Ƕ@<sL0 {faܱ;BJ�!; �'p}"-;^w6}+_|d',z �:Mڤ8P�+~ O,rA<jp;> .2q=F|VJҦVgBD} nGJ* $%VqR@<cAïZrD'7XE)u9 :ZQw5Rf뎵zlwVu-YNX'N�*btQU!Оg\EA Q�w >FaK 8㊥2r9EXԑ�lS4_S<g+Hăw@ñ(N / ]4[s;e =5xgRH~nmJգ ;Zm/+$+ Սv6nyU`= pSs0�*A(Zh$�r81q3V<}EX?N~MyBc5v |N+ Hnִ!:gۙ@iO[Qrv]Q\[nN?XR8>F6*pr9j}_cc3qr##҄.0;TgO Ɣt5#Gx5GсA޼XedYO̬0sDzӡc^ p̣qVZNƩ( ':TCgǰ:LpJc3Oq>E!9ɮ~fdA8yjm- %XO{ n>A ?vH '*|@9{zz`֮Ml}jbLqNn8<)XV�psjA8ث\XÌ d#EPa?w$&=赬dc( "DP9g5$Sx~:qi!=n`kpx>zxE( t)V<.USܸ?.n1vNGW|Pҹ3/7”eHpO?ZH&{ ‘i�&:uHN'<POp=i6�hRs5oj~AA*lZMM0j:sJ4Vr>F E`tLȃJw/6.,psz}qM|'$;u4vF[BV`zNJP�a4 `mۜ'=H wHpҒNx�џp݌V_ p:ԥ2�sbQx)'M�I+H�j@V 2';B:@Hf'Hu3lmJcܙ'„l#0R=광c*0A [>" #z mJ#^oބ^` x$ҺVa]e_]Ppr>ƛK=ݱ2vcj֡2"HpkOI\H{`w5r\~?:z5)Y#4PN�=ns(3M<GPqԨ|ɗ6rv/ m!o$jip�<*�Y H'7Dr.n{cV`\ rj rEVe}9hw"/#`}jݢo*. Us*ӽin�mO<}JqB4;�Gk*l H�'EgB ``` oUpYxnd/D'5w{ItC.0}i2 #bܚu XX,[#I!]ZBcXA܂w1niݗnF~^*֗ Wcë? &Vp1 %r3٦-*DR{Tq0Z�l)C#lҨqSΣ<n2OzkM%PVt p=\#T\5raֵc?:h$Z0�*lR1lE)3zڲ&$Zzۚ5+¤婢euPn9]@J>c\]BCfRfXZ!(ךMG 2ϴ1~!k1<ִdUReÂp}e(DnE5WF7KJP'=)JҨ<(G yrִxU<jڌ&k" Sg&BmXI=OH(1oGGRx�knysv GlXiAT3}OchdQں(-`bN售`Mh7ĺrz`V-27ڟaȭtOZރM#εKp@JPŪ)³zt{7+cpnl GSAүE9%8Sj:F`"�V:\KZ¢zΛJ2Cc$nJ=15 ©BOߏ][`FrJfQ |SDՉtQ$S 鷺W>$bw( #;V D>pzZ]NYkIϗA�|=*4.I ֹ%d28e=<\C ΜBiY6 Uw>�iA A2G<`Ф%vxҶ,XaEI;+U)^+n99p8ǵjs>VȦ; 2vw 3 ��=.`C�U 8m lWR87T'à 1[EQ>c$\S4J-$ SVq,XoW֧-n6!ަn+2v+GdW!| =r* )3SCg?ʗ=@d8=)�ԚvpKcH xO~x*bsԡN=j]Vdr=k3ZptP?xr}i4;qj"f%>w:|k@i#:nF_JXeu8H;1�w/@fi{�}>WPۻE 34V8</$!I[֚ϸiT�+{cKH|H~^$ʣa*R8'p289 3)59($4 !9 Hl>e$SI 1zż{ОGOyl1fP{ʻpNj%n#NzԻ* `XLLbG}Ud~uyw~]딿a$Ltmjw 6_P+7q\Tl�/bsJ/�1݀{JiO\S68g?J\ btdu"G'jͦy#|�?+|aP<u>ÁBzxfk<- pXO م ;5KBecڍdȨ.yO&yf#7AYǙ1!$`v9+2!|źmsӞy�kcPH'Z6"R9*[L <PLja]�n4lm<�|?>03 3wlF+U ҙm6H9⋉fĊpwy{p֩ϨXCPx ͚WV<qRxO3Bk[mHIұH۴ @'殛OVtq-4$F|^P�Jv[jR;Kܤ c~po#>jQ|5'NRsgM|[3+tm�~SrWD&)FQ6xkPJ)kI޴=dœu85ח&C-Ơ9z֙𽓟H$ҏ i6]5B0?x Hq'ڶo = Y9s=nR88Qg+ S �4rZ*CQe2G*ylM8; >ۅkLU�\]iWזdA#�Z3qBYTl=G4YTY8Z㾧3ۍCUvto8 @5Т{RBLm@W퉱q[-8g`UIh(> j619 Xrd M^a{ AXgTPَS>8h:j- W8>qVm$p'5"6p6l>lz5Oͣ<F w[ ڷ'gVze(b]=I-GԚk%LÑYwŜ,[:Gʒ12PyGa\թuFќMø I`�?Zɗvq8Hc`�ı\R\0# \QI.7eJ/;$b=N&l|85�_%ZHŜ4upξb0R2[I6pIQݏV8l]FMt,Oz3(=ӥ9OPM�J c!4V2y.vօ2Fҩj%0ӟwm1ٛ$>,qwji\AU䌃zPrYQwDdgiW�{zT{Lv%Z�8:@U )u?ʚ'(9OZ``iw&dvu`q]>w_--jciQc)Rвz55:19㱩cĜ@#) 9sj+w9ȩ%I5F|':S![ހG? PE0p8z l@JYySC)2K8Pi'ߵ*I8EyCґe^F*Y@$w®*X)FhZG#�pcQ4NF=m觲-B]0''ݫQB݇:[FL'i~Ց\W05s XU W%\o+(: fž\q8ǵeL~v?w*UrrMRۼrFw5D^{LA$>{_z[~$UvⲱN7/\1Z#^O/50VߌaW= - *8-4F03)IdI4twLR}k+XXyL8+HhRA@<RQP0zֱ%f XUq+xɻfj]3*a}T[JKW <qV FO96f {slxgt1Ozr g[Gs]pR+h:?tW=Jzz)lz fFFMS'sW.zX<G7?SV<Nq=iq<aJ:lي^rpdC#(8MU �(LIl\$b*mL*#&(.b쬂zֲFsnUG=EM4Qp3!oYGQV`.@#S^وa-VR*@kԗ]G9rZ+s\rFqH?=};Ȝ`:tmf(S[D msChrӎ0qUc4g Syex'zRs!RT8$~5U=sU ;߭FaJ :>V SQ)ݪD54Dmj 0=pBG-g|-odciJ5 <<mcLYczl͈`=+?]~']GY@=9~LiZ۷ِnA8obn]A$FH_3+жnP:NeRWGX#;@%�Z 9 øO\"=+>E@d>Q2OPkRqJ硨ٓ8&PZ P[hZub´Cơ,6TPm10en?Q^6W+qr9]W8'ۭsss`6b:sepl^&Oj() -Gj3ҠҹjꎈOax%3_$Hak3pk.uN?*vĿ Ng�jyˣ K1ҾÜc\� k5 ˎ$SNT4|g۞0;C Dž湍[7-ˢzA~bN5 Zg:ndrx2c!a"%w dց\u%т8+Cp'@zډE" zԉ 8AOeݑT)V ԺNz#9nF2@}jUQ2`9#8cϥ8=zbɎ2kCM6(AIjݒ2q4xSi܎FATu9u0SQhWhUzCDy唦) `B+AG4e|.IO83 R=~(n(GQL9"#X/˂)isn1= '. 3iVǩ=N3O9a;vqKsH.YZ=2Ug' )-瞴S Sq*B.�M0!@{I0\ߵ d}OCUN<NVW:64}=-ԎX֭`p00z+'֯An"pIݙ"w|6P+.s Ho[2) x^Fu_Ɲ3]FA<`c@-w֟iB=@BB\з+1woQ2F0WM.?E9j tYW%X=+6r&zhҷ`Z�1Ũx~Hb.H5(�O^Z\ףZ 1f<g3)9p"FO<5xZCqi<d9 mFT�5uFİXu0q\V/YFCo(=Qzg&6Mqp^fA⺋m-1XY]ٜ !#6F{6 z#ÏZ5ǐ㱮*SKD< I?Ã]9"WF̜|50uȬ=>S[$zS6\2R#`A"ȫThAq#Iw8n[/ %+n+oH`h!]:QTK6%[1F̟Pi3\_GqUM;Cmh[%H@#[ ֹ_ `WZjQ$Y?'f\qw7qr|ͣ3Zr)^0G_Ʃ41 J-JۛzOy+zP�Cm2V!ǭ7.k SsZ.$*zrC(gyb4q*'?$!OF sڙ �:E?195F, Ly$iA./F�|c<kĻEY#ҺA5C;s^77:ԗ**"H#B n#;NS᛭֞SPKliBRiip<sb}9 eeP?]\*'yǏQ{~"V:0q檌?*j $ϩr8V$�u^<Ě3'xYbZxF? ͢dHkm@a4D};p;WK{fE]X5L]j6PQn-nY<\/Oj7l+UWwFq]xE-;W,_n9(6zV~e#F7QY ؗ'+ؚ /#iU]/zV3}fֲfqWv"TRlւq\g,J$�5uFoB OIRx%+|UbB1 ,d{¨`1ҫZ$\FG"\TΕƙիwm!68[[vQޤ' -K= =8{WQ|SȮVX7f b"lqM d!H ʍ'ǁZ)]R@W#I${Yw{c=*l׹g$k"NaV=z韻Ԗm'"C҂v�4?8ڤ<w=� NE@'S^9UG͜#="x('W\<WICWK}$P)"}^j2DqSks A胚U}nI8lv5,"#Ab3si .NLӗn>^6}C@U1Lh1N,<~`ˑzPVQi0=?~BF1Fr\~Q}頝ldzWfb=Gj"pr�5 ]XxsXci.2:{rcdp1֪iq\;$(5ڠ+t=:7k &wii8E@Mn؎LkH؉\X >9`֋l;gW|S7Rh/ [�c?OֹCFG޿z a�X~mq۷WPՆ2<%/C&4I9OQ]%.cq8-Ӡ+U/Ƥ^Lk(~i57Ʋhg(#kU97׋%I# :yNiܚ]Όqчb)řw(rӲ3\}(OSUK$pN9S6\=r\V*V8,ܗZKm<lxb_(iKYc'qX{:ᇵfk?g>FOe^ճglg}e#G5V@�W+O 6#y6{2.{msPAR�T3[|GqUpr{|A4"]ߡӸd&F8>Ԓ(W! 3- Ѕ@`hqed? Nq[3*V3Iir9㓁tϥaP׹,jZYLvݪqb^gNlm5 8Np: +:TM Vށ| ɻn Rdu<?ʰh"aMG9(sy4џ˭8ęK1M�#j1zvY8 [N"g '&t{IlcJ]tyʖ봩 D9H.&;{-ĊޮEG+�;<= dP{⦖vP2V&xZ1[ʊ]ڜޣ!QF]^ |IZ*$*zzT6V4[ SfGoq>bI9ڸ>߯ɐD FRײ4; qvӬRY$>nYv;m0㰪aT0#b#֏r,̚ 3 Ҹ9<U75<6RrsWq(ZA>(+>cY 2)̏=m#E ҽ,x9%?5<XV\tO@*(I� &8i25(>J*;ûq 3#ֻ8e Ў\YJ" h9eiUȸT5boڰy~a:]LcUء2+K]-Ʌw:E2q\FjEF+O'OCF)jZEecisP÷J߶MspH8u}>X +I#?|hhZsr> }Sv#CMxg{`3۸?ZCKl/Q Q<OUf!ZEj [`:bT4qjkmVCo8eyfM:!~TWSUn{|_1o'8)`*�7eV: >HjuJz|lQڬji8KV?/cZ.Ns\eܧc] *F $\lj)�29mٱW�' +>2 T)`I9w�ÐFAڕR@Ò8>wh򛆏Rdlyݳ=C'AZ)n@Y3-cn[K4灌 H�nԤ3`z ^)03S(x4k;zu)c*s@ ׯzx!} Լ+drGAOAqjbq4rH=)3k֋w@Fǵz%k 3]u*rI]qVzؒ p?ykkNӈNrPG2EH ֺ12HF,T&7.FwZEW2+خTScb_1F?SZmlq^_oQ6z5"% - <g=I-o ݇P&q<߁QAs9Q�&LQZ=ϥVV#L]>xe.0*Tt*{q*J 47zնWh5$ q)4=@ו巐5"3; J9xgY J4Y)qw$۸fg"(W{WBʌ'ֻ.r4>3Z] 1Gʟjm2�"k՜yI°OU-QtU-7aim8!0?yaXeCkz4,cenBڞgJ$cfm9:W;=0v3ޫ' jC0TL ~:z9zxqS׵A;!FmYBlF3ҹɯjĞ#4褎aQWfCgyǥs i"m1ָrH]GX, rъv*(]d{^YL1ꐽnx{{U]#Ğirw'@�ZsoH +\IeV/Hߵ\fUULgdݲ+ZȰ3OCW0!޵*$Z6TGRMZaJ3Ie&"lE�Yv h9$#OAɬ&y.EhkӬښ?V-B7 ;WT%dygm{2 3EiE3H%bE$EHa{9bv;Oz5-6JW+#񮘳˞旆|h-mԯMcW .FP,(V-"~[۸c-W$>5dbѣx!DܥGKFY[k 4{NwIM27;8Mm\S_7BM9ofN2zVINV4ɕXۖ^"ɛFBktN%fb�ZT=9GAS溷U"U0?JUmdRqWZŴ;jbx�rkoJ|$M}vtf y4˛r+zXfpsYMrk=mZ#EsRIFIr+&6Q ⮩-,5;ǙZ9&J[y"c=s]ucRa2c>\f=?'`ZP5MM:QAT<K`n_ \:;Uw'vkڝy.%]ny]U<ּHFHG9p}?:4 1&-Eżr r�`a`FF+0n 'KQ-&r2;Q(z'n5ې}y_vIJd�GR2~=ɮ- e*KW`2d{WP2I+duW8>׫ȻلKzrb'覴9{⯵QE$>^+ٓ@^xӬ 3&MS,[\, sZ:U5r n.tq QCBgqaz z2PO<mpsp+zC7|tc`A{TѸ>U_#U}d0<je|cJǵ8`Jw-β]#*^[K״p91++:8hQZFF2N6(X8 \$S5h7h<?4wV2E4 8 / 2;OE_io_gK}ːi y9[Fb 1JrA=i>`}sJn\) c{bw($3@{)ƘĜczs!e±'ML&w�{_2bqڲ<l-8'2e=- o\+E}Gyvn;aFZ½vae1A3>TԞ0IHrIkv6[�,H9H hI7#p4Z-QO/" PN䃃+)ħq!,yM1ܩIQXEh 'sqYW ?۱vQCxq�'&sPo&Kmb>X >*nj <Q*[JKqHaa"(UALdJvrچ-M&l.Uʶ;gmͫB7p{ketV_B3N#@t`\RT/nyȮ/&s l|͎ƺrm"2Pr3\lJgCem?ʎay& I^(8i 9g:aEuV( GqOk),d�<NLj& f&[j%ާ%jFB`>ZE< F8R\_ho /7=j}\9 ӷq] x:9&5㰮 #ҫ8,PRl=o\!±x}y";8GMY=]3fcrJSGE,Bg,{ j2* ܾE-󄘎Ƨ,28ϢUI]?ZFSIQ; ME\h氵mDDr9Ĥ\1! 3)HȼghfTM\:|sMniDqj-< zM#.f9Y4Q);#Ži˙=w1X$`=`iYp4k 8Q}AsҞ`�F0Xzxor9ȥ{'n ZP bȦ;IzT27|4[cv5V?JMA$wgOD# L. vBfv_Pp85l<jpFMbj(I7KH#er)%A3RʶxެH*{/oCDKWсRsK!rOki֬:U=dGK|`G5i.5 OkEpԨ`vbYLȿ$c*׈5+ᶝ2{�o {8*@-S]omeot5p͌Ҵ4Σ(nTwЪKr}t+ I)oҼ�K41j:.#$eUGOҶ06/uyzl];ϥni>Kq.m <tAakwIbnC)ȫXرʠEv/rOOCzlj<1xwS{U(Ler_v P1 ?ynO=W7_j:zF %GA 8*j/7gJ< 8>1} (EC-s�JՌ+F`㚙mg\򲩑NjL,DjA=EY* saMTird/)?*\PfR[䡕zVWO(n&QqrPgE$:iPk?RЬ[xtVdMdٲ]ĶZ<XH'~Eާoؼh@rx%k8PqUSrQq~N,8XV;9zzؒ8I؊}c:궍vBA &͕8f15Wlcrjp('{at4ɴp]lT}8ko3VFRgnnϻ# r s$8I@Guvq(`OejoD㞝: ,t[W=N+-+3Jo"�\\a#+мC=Va`qdkvp28�v^%Hrn|^ۆȈi1NLa57vtHTRWtpHs_Uy w;T!�}ɪ0,FprUVUC(FNhyWz #$jA6(_snX]dg\”S#a:ױ#H$tsB[V7gXW�$sҵ:ԦQc<Rp:Ob*Y\^{7.Y[{(os N~,q]tOGy#y_ZLLl7)c#q=U:S`-ppG5H^[IӑOJɭ; E N3*J{֏tȷa/CZhlnnG~`v9T zw!<Ojӵ$lUrGOpr(U!sKoMq d1lFrnxR]CY7dciM]z*<VpANHkzTjf֭FN:afX њEF،f[[hcRk@̙eֹjWH(#|1OJasb/[$Y#ae<U./ VK)-%P:Ozz]r.%;TW?\$ֻu$<c<+$!cV-t.H *A]"IAtzNnOSW:N+PbukI"0xM2(N2<ª ΍FMVL.O*Ŝ7YS\]BfT˸dÿctZAuv C [Z˸rgUE#ވ ѡyk3]f1D܆zWjk@U2,hyNuhT}ϡ\i5Q#Xp��>~.+G5_ͣH#խ z麮DSbsvˎb$ }kҍf4`\5_9{v# )w4Uk3DVڌ-gkF·n|wekXhy\7h~$אxFuY+,mCZkj4uwt=3Yj{<;o[aՇ^Eh-wbGA]*.Ǧ\Ҥba":W]P_۶TÚέ# 'x+MN0H2z4Y~<s}\KCVVi!^h\x p>TgOg_|Vx`PVe-n9.=NSp &%3fBVXk.L2;U搾0zT];\Ubd�tp*K)Jȭtej1OyUrşB%Mg:ӏjܖ% ̞?vʢ*JJO Pdu7"AZF9'Y-vJEP~B4:?gAׅ ~f� _܁%v~g?UeVWS< 0 V~C~� бSsWv,{�jxa3+"#?2Ȯ[5i c+9uc_/םO\ϩ>ONK6n>j/Ȑ*a |-bwc `ҭZtgRŮVIZђhPC Qkzo|k@0n\oqY+0'G+޻o%{yi2i$Fd$G0 5OLa%+ -8;Qp+y*7nڀvޥ�b#H9kmwsn5orWxetVˁMz-+'x#N7;D\g/~5#y-xgUo7#ֽ'I yluxmD vibnk+!ۊp�> �duC:~m<s 4JĠq6.5Uyw1O zԵB&==+lMÌUvJn=M܁*Yh>KeOoj3W8隩* ù>jOtHE6'#<4 Nݹ+!s!�])(=V3vѓnd�:Gmuk�3T%:nŷGִkC$.n,vE'5h/\d t"t]=mU;'޼Y Č1㰮*e$r52|k'W#鄶xav` 3L�Le �{ •ooJIe$K1bd~ԙ!Vc8=yn8cjڹ7r烞+j1SX/#\#Vj[(RKo1ڢHv:;Fk~@Pd`We1V$Fjޱ9l=1X1.I 9'֗Ɠ$zG#'Oޔ08?Z 3>)KG39ҐxRLMfYbU= z&êC2By*y tZ&*?2udsT·1<N20Aq]~ED9 2}jߴ[Q)%AE.1I&_ Jښ,zSM&x9�L20? ?*i>1؄w0C^k<i Z.�5׆+;"l qs o+0n\\ApH$޲:twou^]>x8Ѧ՛hWƶ/^K c̒H~tV۽Z`JI;޴|%=΃m`Q6E^r1\^ևXdX[xwmJ^_jΝ#vo" ;QR+*rOjӕNvڹO[4IIf#k5xk;`]9ş1i~!laG*< lr U؟nxJeDcwLci)[DDc՞1\M73^]Kw3==FΘEnQVӚ[k?YQ==UwY>#kH4qaճڰ] V]\0-% j�?@D^wH~fk�]C-vcN)\S:̺Әbb k˧@"iD4͑fѬZ3%;gK\Kzcwֱ𝭅,0o|ةz:5pZ s6dsKƒ #g<}+)B j5!#xWtyIJXQyflvHPu2[]E4HJz涥gD9ɡi:ݱyq?Ұ]UOj|j9F9\glmb@ˌG (BU  o$qk}%1vONוq\mbE,nx?/pƫOSTWVߐ{.~&krE.bqt+4S{81=Emi~"7M,ie\w \cδ s^ #`$5}֙ovYT\^ J(#KT;~5Yz3n<EjpNWSaܲ#"/[]'w̉X(ίldU,^* t]BJ1|V\VH?cK ᥉) m1^9 *:-) xîj]̧O0nv>5<d^&m#Lo@TS")b@(i7 _-{e ,~E�jxz˷V4 ާ']T6ph6@R7Hq޹Q!9^5s:Sɰ =jz"Yr2pjq+k/;nYTSU@2Y̘ &$hbU",0dAOҭEn`Z1ttF)h99%}2H|q_X\$8G�]t0q}}\>% 4ed29c8PgxoX5Yoxn1ר,ssF$=']v0JB!sWOиѓ=4@ٓ/ wYLk WNKykX:"c;kkªC&r 5&KR=G*ߞb=}yzJn$}�|?J֜7ڴN5(.,hN|WJNcN 1ynW\QaeR=+N9M鯨<NWM͞{Q.)As+�h�bO^Z8?sһI]΁wRҋ#Qt cy*\J߹ބu,xYoGr7FA>6Ü)|ROl% dLLkVXi-q('C f)"GCXdVzDm+ 3* 'қw&1#9.dIcE,q'֬6MnwEFUr �/Ũ_q+�O\dH[q;3Mts֕siAuUvtdVn�$lbkSƖU/nZ}l͒ʹUӵCdIw4>w\[,; ?RD8wm$Jahm\I$~`]c**ڔpU{dڱQEJ"ZG-M.-yvR4rMTݒ}cv(Hw�=+2rqFT]Cb ރn=*9nfɸ �8˞yV"8l[++X%[q袰'3jdL}zkS9>Y!)yQOձh O^s9X(�$M  ndL�֠JZUu�c35 ̐wrzr0C VFEr[|#hCI(r`"=FI/b)G)ϧq c#΁kA^8rmswE]eUw1̟uz]\VB͌x�O ZV$.+p~WZtziz0}ZYREv6P]bD1}}=I_Nx@ ATyk:ӑ.C<�x"Z1_=&u^IOn]n=bܜ G\"�ͷ$[Џcڴfּ[-�T ^#X*F+wgy/tۻ 9mț=@=미pPҬHvўƨ)8+EXp~Ry zTOI\DeZνjڲC <]WKYCp_[6[͢LsT'kеk!ŜyMEtSdJƑԥ|!r6{$pI H0݉&\{z@Vmc&ɂ $ Gj'hhey:ķ^Q\kPiVҴ1Ϋ'=3V.V'OAq֫/v=Ew*j=CqyONpi$ >6vW6);8c#99Lad ¾ I@mf;du׶xqJ;&$<!^T|)  NI3Foip͸}}+.LrLؗE]_A[sr�x@W9-f+>p~r {g<g[_9�՝fPN82$ Y:oє6񀣫~5bi <w I N7*ZaVKM N5>(^r7JoqʑҪ#V)MMd]maNx,ZlR9C#pI6\Kgaaf9lS-izuu7G;zڥ/ole;sDOb1hj #aH[0\s]qX}KsGUO>N!cV,2r=aD�׊&4RhɸP=zO #G~n9T1t A8R047Nվ8Uq N$vaǭu+�P?UT.)mwJͳZLcM92 O=.?RycS#VePk59O|5XUO?)5>:\[m6+heĖ*}k96J)emjwwZ4ٔ�CB<9GlAqѼhv9y$lՍ++4Q[A1v!#'>Q|t-R G-֓jg^sVc$p%EqYIX* >eeYdXQOtrLR %]A!J.[dH$ÜVͅMBH gBE=\Ԥb�Gܟ-`3Z{[ I5Csޡ\t`*vn4I 1 QŖM$v,3z隧j+,-;[K Z[x#a d{ӯo)J61<F}䃚;hH$:f'Zd,DmB]kWS`FsRcl릸C ܑny`{a 2qRkB^+y #n1M+2ǩPl)_@udus#Vb#9Z47\!-kTS{XD$Xw4wbԒX®&۹Nx?iqjO)6Eoq2By2.oVN #R4P+)>JNFC�)va*N0sR@[hSP5FsgS˖W)㚩?-f" =3O++-CReK׳f|�jv&3�;XuD1s=*'-orAuJP:d c"`8<C %<|\=qjj^H4ś'޲u Q;9uԣ-_[,NxXܱ`y^s1C|ޯv~ҖqU̙?>+9J"F|zf@I=[_I#9Tp6mRYg\FXxb23^=\0FkԼ?޲a"c<+ru|:fDrYpJ˺ӧ6G^bX9HzR"ګ\ ?(PI _JKf*$Aҵt [F8S Ѳ1AR0oPx#9Ih;9`2@9uP1Yއ9oCW,%! (�qX<F+X$ h��i{f$uw6eK(P#_iY$+8$?5I2QP568�<8[:b--ֱܽʐ}7'AJp<NBFM z@qJ'j^+n'ۉr tڐwG3Ĩ$H1˲ +z*?kH6O5Eㅔ#V޼.=쉐 e �T6>W~nrVfq$dP Hk*J.AQeE!a뱂vp PtSr;UGcړ(|{O&v}yN08,}2sV^q΢M22)ϢwJaaޠVmۨRMG{[-0[߀FF{VjWd=)ji sLjkwGe 3WIvEhr/Zß ܵØdi di>F)%gB sֽ:Msjx 1(�U TrڴeyYY@+Y*$ 6UG.Ki44̫6A\JgmGNV˘><SdWYh*%N"W7|y|Qx)WQuHBKQǘhmW<.NWKdD<nD`k^A$}U~.XZu/g&dsBJ6[{צ[$KiȎC= xneόN閗m`«K7/9SҠZo?ŎkH�xZޤ�sCCL .b ʺTV{X�BI {qJ˼Up\3hL&Hѱ^HkWUieX c\4xt9 ;DJA5O6^ъ1qd{>;ԼEkeci 7<\/Rӧ5ixziRGv^ >\p*WWw1z-faMBȠ{FN'>G&q+NѮ p3nw%ͤRFQ Z 'HN'!y 4w:DI; toFSwd$1:տiaX3<v|(5[amZYn]1q;Rsx<�~5[G|T]2d1RQp VLF gڳ5kZZ:kH>W r>kIKX6湒Pz)"&PQ̅V.*9sq<o$ܟZ썲W'MʤĎܫE;pvI=4|=xr1e]5+n/5+pBsCw5]wi."5_XZ$Gq^+c%")$X͕楻l*0+sbk4k9=[aNl]uP^i|"]aIRe:-Zu4 77̝YПM$F ovbw$ib־iʁfnI<~]+n4&Vbܓ5>:GQ z 'PAbIi,Ьq8 Se�e&Zrb25SZRyKxr)\:Cl~]>HO/Sּ]~(ZMnm#MΪEK)otrIgUCO^G^KI h'+_&ǩJlvm<~cJƢ Jnj[c6E+ǩK-Vj#SJHl":nL ,95^<9**�̻9iK7^:d9'S *YUps+J[8=i'yQY 1 ˗Z5S s'�.yUou;{$yfpV5_|@cXDj9=+R;'203ޕJATi'8ijN]:F1+r:;cĮU: u5B )hRԿn R%aT"*#hF�ǥ`٢ESA<cwfθc—-ϝ z(=ed]֚@s‰ș}=:Tᐳ�1fԘY*>Y{i)gRF28 R=00:c'uATNA1U- n*OCUtJGaq$Ҷe]%r~�xW_��F2}kkSDUYvKTצ1늄�͒3)ۘ�4C8$1iҚ~`Fq)A=H8鞝/Q֪g; jZӱ\A-jd?,LH>_SY9`7Gk6m q(҃אhUۊEF p:OSvӔ4bݝ䖌1N޺};PGE;!+SxիY^#q֐f5"zgp8s^=_;U4wz$�;O} 6zup:妗g 90:=q w4sI{;7U{]=/&2ʛ⛦i)ۋxز/w& R,HTon"FYƮJVFYipu#lȸNrij�yKPqyǵhj3iL#P;OcIK "t * ]Gs jsZbA =js#6~7y}zEcyk7U;,BQ$c* ;-0)y^h\C}9.A9gI+9prx=)p�+&Gߐ}=齆ѹIU$$X qϠcE 9Qc_((g;Գ#%Q|3j{6dew^scýBѫD0ǟYCK:+ԍne 9J4ftu me{ilGU9Ӝ񭘬 R8R@A֜ʑ  6ԴZg:-@@1ֲ5O Z6q.FGLWY"$h 0Ee\�EhGQ}WB0M &Iqlʹy1*\VѬtFX�=f@yl7zyer+Oʪ}sTɵ[ޟ+EI`3}Kg}8ܤȥ22jaW=ӥQ'zSք,?~WKgl8O�0uCG $a MU'U%FMe(C,mG~Oý0$M Ȥm# BTдAemm:eco ypAIj(@nb`94%5+)fE \|e 'Q]660\bJ04մSdzWDb1gDW9N2�G'h#mB& 2c 1ޯɪłKYw7èOۚ1vsVƹn~}f7<`c9{/#M(cXqW})asvěvV~tx3x  V"fQd#TIfǛ)#ڢ]zOAYFvd1 8ףq2eb~0O\UsF( “޳.V5Uu@ܹ1=eˇ<tP;͚VM�o3=G&$6V2JO̪*+;P-Ȳ:S[1-#�5.Yry$lb-"+[Ep Q4<Q([ 00<䊂FiGuMcՕc].yu+{yP™>mVX8Oҝ Nۇ{P[OJuѠc 8$#o�M0g n!1gFf=b2 NkڮiS+0%v8%TcaaؐFy櫙 ǘ/P-GsWR%ssEu? 4E󫲁]1ou2>wl߅C PKՠm`Q 2I!�k;QeliePCp[k/ ! -Zf($3�뚦>#^*-"Q25˦jva[YWJ?Ҭx"u3n"1,>N[   zg֚HLɓҝ6u` N"f4CĒ:ft݁қ2\:{+Q:SD8 }UT8O݌>1-AWn6 b$*]LcVGjWp>*b@=+6LaVqJ'9Cf|taT>]}�}B[O>ޏ$DsKN@*n㌲)yo^j,vr#jXsn2"w )HnvJ2>iSҚ\c4S\ALӝ[n^0`ӄį8P�9zY#SRA3;,sz Z2)TNQ4zc>+ݧc]!鎆^Ts\zss<�2j%8Z>?Zc~Ex$ھopC߃۱'zJ<M=qnwM{XexegdiI|tɨZB\֬̍PkI%e!>m c`[CVGFԒ%9fxv=5^Z"{iU_}+Zv#^U@T#�cc7r;H($h01Li0�71ڕ`:s֍5*:cRQ8M1',p�A0@| SdPp(qרPrp} cu>ai <Mܑ?FXj˱++M�`8==/%譎CF}I8|TZ9! J;UR|?DpX)26aP@YUٰ~ڬI>v#;zӄybz8ùV\$G %f2`~rq>+jH*N3b~( E&swwVds'tf8@8 Q>& O?`͔e@e<6OSe猀:qZ6ȭ0,I(0sG.8pҙnn]OI G]:u$zUobXrU`fWtQ,t{ZH �n}֩G06�0ME,E>H]ib?"fT#/ :Y%TĪ�֨vƁD�nKCR#)"XPɨ^B0s=3͹ќVUگOY)9@S Y1ɭJ$*~bO5QnR`-gOªby>SSamVi06I|+N�*8&�1(OD.2HBi-lH}h`x#H/={SVzvǮhHF _|RGNFX Ivĕ8$p}xsv)=yCij2+13]:Gb80ސGpVI!B_\~5KB^xwTgk>} Ia~$$!K`Ϋi#chr@*l\/͌2`ƫ`qj<. S~`v \͒ƥ|-/`J=`7cJn$�)?)xʰef aPHC!0YdRĜ`Y o$;T>º1M9m,CÌuM,XM�sšqzS #i6U*`p)88|Qp!s�uGp2An1CK '+Ý[ycQN0zb.HzSbCcҸXkƮpAL94ڋIcqU � 3DX*0G[ Ҙvr}cԾZeH7\u[6'5'sڠŜ`{*v<{2.$u㠭Dw2y.2-Um… NXeIZ4%[SU,bfpp;Ki�@F̗r[{Y"Pr5SD@P)b3rӥ[Euv&G[W1#x?8U?poN~ma=c$hKsQ'=X:ZcGe$h]դL9Dg#�Vb0EJClr #<�t`*Oiӣ ;P;Ү\pP3Cr ̄p[a[ KVE|t5G688# SN{�p}9-R/׊z� A_$=^;~TqQasAB7EppMS?:bF9KGN(6]+\ĺ {uk'4Mic)J9=x}gB?Rs Bz/Ј/^.c{�w"DʉҽЁ݊<�exoXo,5([.U?0\όTѥH$ӠR֒uZF1mga!$Cсf2ȺUbĚ"M PćA1Dʼnv!m�(*Pf#wz=ZrOZ�Spj'@J^HH<s֐l�1敓rQrio!ʦy@ȿ/CƌH�ޢ8 �b2rzT!}(Vy cq1J\:\�pv@o,jᰂK(\׀=�S tWs :4)Q vX+=aKO Iʆ$c5"JۀX#l'GأZR 걇x�ICd`2UlD5 Hnw )YngnZcUp Q}ɞӫM0w.H~XD4BAp#*d,`wbzk/v�JB]<1@MO3 e)eʪzUdj4Ay.H*id<Te$l|RF EpI#Ky<i1ߥUum)u١dS"vqRavY\1X$=' Hͺ_o{b0ݞvAJӇG58@m8=*Z.We@ɰ秷5rWJ&�`�OW,UQH+�Tr± m?jr0sO^iXBѤ*#R~{չgnU!/!l!@øpxgG�Se1@8 QJP,sxRؕ#y^K!DQy?J9Xa?0 C];{S|̐Ѭ0[[r:0T5;Oj|Ӊ#`@!n!YeZ]Þ1@rsz/B�{TSKrE&]瑧A)٤Q# Q$n)ΌDl@7#3@pTvɦ$hp�Cp)eMUyIS\9pyW�J�w,R/vnrFDKsu\\җ8`w*8 T A^�*lEhR;[+O9 %ߐX�J~OJE`89\K(p`F9 G^3P�6pjVXOH@aRH*$ڻ3qi:&c0ˆWi)ld[kH2msԊh\vj.U\yzAcR#ZX1~oOCJ㱐 rvBzmny\psM.1FqZV`ƽJՅ03OO?5qh|o0D�; U@:vK0=E>pQ'%sO e4/N)6;i 9F=鍀=S(~e szԀ*G^F0iI ~n>$r BwSQ\ܬ+)?,Z.0@>li .eJ\O|ď<QXM9 p1Ps Ei)@SJ˓#җ�q@84ʰ�,nSZDzұe#'E9X㞹O$�4b)1$P ON,m/ZҵKҩ 6)Ǯ8knaM @�Tj2OU/[MF3Y?\Zl$ (I>mĖ'证9u $`�q^qd^WxRMKLI滰=dޥk$grH&>=i4(�]W0"’;dQqޘ8;W4{-@ v^ ךd˜]3n \Pl�jr}j *8^ 5a%NrNxq?1mU=&Nr'9E$rǯ!982i    zRkvG&9R#_^ 8-f,уz`J(`ztJѕO5\B\Q{g1>VR#q�>2"d;X5Y+0J&|,]&FRY@$a`Isʾp8h3O̊A\n `ȣ{gKʼl2@5V+"F J3Ҫv2r=3ҋZH;[>Sl[{2IQ&Hg@oh-u<M%v8j,,BblcU%qS*GJ4 b&* Vb?y>XJc>@) vMUufqšFg1GBU КWu4 ȑDLkFТ]A֏7=R|GNԮ+E Q(¥~})(lQՏ\11yFOӹ99<ce]"cNGJAGBwƋ eEG;<*Ir$Q;`Fܢޡv0cE� 3tXĮv?*G zL' EfU,P;z$RLgv=ĒIq)$qPT0E #LB waڤ AjUFA�=? !;7pgwMv{P`ۅ]dx~e]ʡ&Q,wm(~M!fpo9#ea#&22*} >gR K)�EIF.rxIчRf&F( BȼcSrW+!(>ƫ+`K&iWI,sdk@Hnbҫr)ND`ۆyCMrLE�¥k#Sݽ*&` RrO0")P� I2s&ؙzx]I9jV|֟kfH#m=#&aʌ�#@a@lDycW&`FlZt0=Fn@,2#:C-c[(͖!9%XuIa^mawaR<f;URi uppT!Ic�y040PyIo(ohi $-ԋ[QY1̐,2!R F_ 4cL7K_FC)XW4;=z 2 ;wK:spǜ7hxޣ^ȐtbF&0z!=t)\ma"8'C(*hlhc 7\T0ySSH ۇ4ڐ ~O8 +�Vv9, 1iȎ2FY&$';Rf>XU�rOQ":?ҧ@ >G*tLj,Zg�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/blog/uploads/2017/03/img_20170308_155049248_33196894011_o.jpg������������0000664�0000000�0000000�00001246206�14502137606�0026121�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000�������������������������������������������������������������������������������������������������������������������������������������������������������������������������JFIF��H�H��#8Exif��MM�*���� ���� ���z���������������������(�������2��������������i�������%��������Motorola��MotoE2(4G-LTE)�����H������H���2017:03:09 19:08:33��!������J������R"�������'�����}�������0220������Z������n������ ������������ ������ ������������������ ����� �� ������|��� ��������t������|�����������0100��������������������Р��������������������������������������������������� �������� ������������� ��[`�2017:03:08 15:50:49�2017:03:08 15:50:49������d�x������������x��� ��MOT��uU�������U���� ��q�����0-3�q����0-3�q��������q����#��q����#��U����B���U����!���U�������U� �����:U� �����u0U� �����U� �����U� ������U���� ��d�������U������?d����NO��d0� ���C?d1�������d2����d���d3��������d4��������d5�������d6����<���d7����(���d8�������d9�������d:��������d@������dA������dB������'dC������?e0� �����\e1� ������ e2� ������%e3� �������e4� �������eP� �����,e5� ����p��������p����.��p����A��p����d��p������p��������U��������U �������U0������WU@����_���UP����U���U`������jUp���� ��oe��������e������xe��������e�������e� �����U��������U��������U��������U��������U��������f�������f�������f@��������f��������f^���� ��fA��������U��������U��������U�����B��U������f����D50�f����Y��f����.��f����,��f������fB����u��fC����,��fD����0��fE������fN��������fO�����@��fP�����@��fQ�����@��fR�����@��fT��������fU��������fV��������f]�������fS����QC��f � ���?rf!� ���?}g����� ��g���� ��g����0P��g����PRD�g����DO1�g���� ��g�����[�g���� ��g������g ������g ������g ��������U���� ���U���� ���U������U������U������U������ V��������V������ V����2�� V������������MPI24.65-39-4�TA39501SSK�1209600, 1209600, 1209600, 1209600�1209600, 1209600, 1209600, 1209600�yuv420sp�AUTO�������������������������������������������������continuous-picture�auto�auto_hdr�Luma-Chroma Plane�BACK,s5k5e2�00000000�94014034010DEC2015�5b8416ec�0000�011e�0042�M[d bæǽO;&/c/FS٪~hi'C%'`\h,ɳ }oC- r:Pb: Oqr5"#W2@;:RJ/H־}FF_MdM)ݬAPnC^^zJY\iL}72]uciXV"$Q@c$/8-Y $)7w 1Ti8$Zv9meO SiaClH_i2]M!u5Å~0KLg<7Mw"qPb�%/to[M%6c+\Y uy_%ORՙ)i ֣Ɩig2�+"8@gd`2tA볇Zgg%5 T�m|s-J{pr`co2m.q~ =IHGJPأR(y}d(5vD~!; dJM6Ir1x4g $M*o!;5-0~9B**=rZ<;<V&|dAlbyJ*4C8Ĭ7U^VozW0zt}6%ʦBul8Ic2v.qm {߷������������p���t9Qzԗ+?ŖSU,fcgٻ`%iޓK+UTq̇ƶTKw%,^z69$]-V[&mRpZi+reސ %^S6HB=Wj||F" 7W(:0?bg"ouNt * ѿݭd@vk ֈyJ Dv UuQ~yz9k#6'Oﭜ_d MYLOJ/ʄc &`*RPXK@UO RUtm墶yg!;{w悔1@v8̇wp\=N|����� � ���2�3�d�e������5���~��6�����7���~��8���~��9���~��:���~��;���~��<���~��=���~��>���~��?���~��@���~��A���~��B���~��C���~��E���~��F���~���5������6���~���7���{���8���z���9���y���;���v���<���u���=���s823119��823119��823119�����d���d������R98������0100������������������������WGS-84����������������� �������������>������F(�������������N�������������H������H����JFIF�������C�    $.' ",#(7),01444'9=82<.342�C  2!!22222222222222222222222222222222222222222222222222��n�"������������ ����}�!1AQa"q2#BR$3br %&'()*456789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz�������� ���w�!1AQaq"2B #3Rbr $4%&'()*56789:CDEFGHIJSTUVWXYZcdefghijstuvwxyz� ��?�C%2gJ2m5j +G҈y&�WKD|ۡ?HmS噳ڹ,Gq ֡?J c!GLt�>Յ rBc'X>^KKXu(9<էgaZn;sV<Ν*k2F ly1d}D.;FӜ1Zk Rۨm7xҪR[|- :U &PZgvl|مdK-ߛ^<b=+~X#$]ۣn` ekK`*cH??y> fErvRjaZ r#[e:M{oJmǥiE?z sܐz {P܆ě8NdY˜ֶ!g;{d|z&Glșl WC+tݚ�Z;:Uk8n"HRTnݎW8n3I53.|=ԉkSºy4P,zMM-eI=_thc?j!ѿƕ=Q̯¶$Nttа=UȹI#]=a+x9Px٢|=8* CCUXt"$�}).1I.i?f%ִ+ ׺X[j\RV"6_]Go2zGF;Ipr@&�U8GV&rcYigɉǨ>bj[c9f'*qکۍ(<dƦ`!1JJ*1t[쐳(GT-15rCbs\z"mܺ-y-(sW)Lzr~V*?֖pLI<}�3#N?3X:ộq$wڸ#qͮݪw>Lf90~D0'c*,>=*}S©lvoه ֔nF^wIגǁj[k(O8jR]0SjMAkm7F+Z INpG42qR3pw'q�֤L?:0Ann֐0GZbLg�!US0t(|wzqH`epGJb{2Sb=Ɗh#dn3f;U91Nk3$[T=XJX1!P�=+`A>W4H�N,j3WЇR5MrKVqݾ>{e 8<U� VPs={(tMa5آzMg[Dф4G XP�1U. ʓduJI7nYGCt@&n,gx1QdⳕʎgSKTѵ-)XO\VI@T}kj/Kxsд幏djk�x0ȱA],^]ŹUVF 5y\֥{]^a$ OlWzm._Zkl+ӼE$XK/R&XJ$$sQEuo{c|kdzQk;2oϯ*J@FzT΢8Y786E)\0�WWo{(OOm'8XvE5AZCm}Bv2(?vSGXnOqW9ڵ֥29!-zEhcSVd?J2\G9Itdѻ*`[<,ӝc.>E4r<7\^L|Am#csW%ECTL4'ihZiS%1󊩧\YOntwQJ9SGAol!U\rǦs0I;XX]LIR:N>%V``H8Եw-]bi# t6.kpOpxrvSJQ^Uw=K"M(V.|6C1mRJ[Q4k�Rk7 *~fr0?ZTQߚV<-VIcnጊt C|Mϓ/o"40jݠ{}:;.ce<#nEcEino[[.*mpH29~`#˝[Yx!@zgt-~rJIIHrHdՋi!__x=)K{vh0#xQHfv"%VOkQAt%A7�^ AX3һct8ޣ(}= Jw5q)ij%'0CNUxjd�]olP$*g$w4h4Tyw?!^A= wG݊9$r4"m<QĆ%GU7 qB9˻[ŻV~@G}[TOyQ˨ �5} W;^BYmP~UM)~l8`}x<?E2eG&7gy 3$O4,1k)Ùhva*rb9 O7#1RMp}Oqq3oǖcr] xv/v>xĪ>mØX 3}d ȣr] K iב[CI+gӭXM?(-4ۍ^TݘƄ 95MUZ\IApNGoZzxu6+eY IW=sW84Lw $eqP3]ycy^mcddXZufx|M!Iy Hc,ӵ>2 y 叧WaK:)FQ dVm>qPˁ>Xx=J[idq&Rkwϗ0ܹ=ɑc+U;Y+,8p{kcmvƐODces7-lq~7I$�t2/f%,8ϭbxkfi ,gYܘgWV;XIn5pvYv+4 gѱV0jͥO?OqZ76]0ݘusg-WR湂e ֩VdT&{[-'k}s N?79 ̤Q#GGlĄxRk: 4x#s;sr{gQs+ԞඓUȸ .( z2uib#|&}O~ZwnLvIt�!+GG$N,X8DڿtX[)4$"pˁ¡մ.8nH$ϗ�׸�&]jZ7 tCm +?[4 Ki1Q4if-\^K-'?*n8}Gj=/B7�KyV*I#ViVtݴ1<Xjz"J~zJ?FRW&KVDӴiAnPS]LQ}A<,upϿֵ#~EI 3�yz3V[eU�Dtb,ºOpOsJ!2FLi<j:E¼lI6dcP鍧Nj6B;}=3Sk;ԡ[Z w3Ǹ% r=ngᑦuU*>U^5[$K{{ˍ8sXW:>V$`xc_cT]%ucltd`({:TWb!,߼8^g� 1.Kuݽi2}Q0}d?SvzM-qJ *=[YH;X{ҸYFS�W[[bIYJrbs"�p<ô*vAlU0�<1~L &9{dĻ*rmH X2$Q2R.xEWZSG 6hWfc xVvhZ66:!Wx󫦭$9=V6\Kܚ:=^y!*0�NsUܮBzQ)->Mz'䬇`76WO6{Rc)d:iN;[p@<UIیⓂe)4giYʲ uDY9A�Pv)�zt3HG`0)r:mgh罉 vȑmy04jyJo,Уv9:~>F40ӊl`w-=j>hIFrz&4VgmM)+(�N9h[>Oi+.:] Y'y_]$VKo;Z)E�>̫t sYiZ¸L7j*t(Z\X 0,1.ߚ繨/'hT>墴L|Ž(F ^ V? http://ns.adobe.com/xap/1.0/�<?xpacket begin='' id='W5M0MpCehiHzreSzNTczkc9d'?> <x:xmpmeta xmlns:x='adobe:ns:meta/'> <rdf:RDF xmlns:rdf='http://www.w3.org/1999/02/22-rdf-syntax-ns#'> <rdf:Description xmlns:exif='http://ns.adobe.com/exif/1.0/'> <exif:Make>Motorola</exif:Make> <exif:Model>MotoE2(4G-LTE)</exif:Model> <exif:XResolution>72</exif:XResolution> <exif:YResolution>72</exif:YResolution> <exif:ResolutionUnit>Inch</exif:ResolutionUnit> <exif:DateTime>2017:03:08 15:50:49</exif:DateTime> <exif:YCbCrPositioning>Centered</exif:YCbCrPositioning> <exif:ImageWidth>512</exif:ImageWidth> <exif:ImageLength>288</exif:ImageLength> <exif:Compression>JPEG compression</exif:Compression> <exif:XResolution>72</exif:XResolution> <exif:YResolution>72</exif:YResolution> <exif:ResolutionUnit>Inch</exif:ResolutionUnit> <exif:ExposureTime>1/33 sec.</exif:ExposureTime> <exif:FNumber>f/2.2</exif:FNumber> <exif:ExposureProgram>Normal program</exif:ExposureProgram> <exif:ISOSpeedRatings> <rdf:Seq> <rdf:li>125</rdf:li> </rdf:Seq> </exif:ISOSpeedRatings> <exif:ExifVersion>Exif Version 2.2</exif:ExifVersion> <exif:DateTimeOriginal>2017:03:08 15:50:49</exif:DateTimeOriginal> <exif:DateTimeDigitized>2017:03:08 15:50:49</exif:DateTimeDigitized> <exif:ComponentsConfiguration> <rdf:Seq> <rdf:li>Y Cb Cr -</rdf:li> </rdf:Seq> </exif:ComponentsConfiguration> <exif:ShutterSpeedValue>5.05 EV (1/33 sec.)</exif:ShutterSpeedValue> <exif:ApertureValue>2.27 EV (f/2.2)</exif:ApertureValue> <exif:BrightnessValue>-1.00 EV (1.71 cd/m^2)</exif:BrightnessValue> <exif:ExposureBiasValue>0.00 EV</exif:ExposureBiasValue> <exif:MaxApertureValue>2.27 EV (f/2.2)</exif:MaxApertureValue> <exif:MeteringMode>Average</exif:MeteringMode> <exif:Flash rdf:parseType='Resource'> </exif:Flash> <exif:FocalLength>2.5 mm</exif:FocalLength> <exif:MakerNote>3010 bytes undefined data</exif:MakerNote> <exif:SubsecTime>823119</exif:SubsecTime> <exif:SubSecTimeOriginal>823119</exif:SubSecTimeOriginal> <exif:SubSecTimeDigitized>823119</exif:SubSecTimeDigitized> <exif:FlashPixVersion>FlashPix Version 1.0</exif:FlashPixVersion> <exif:ColorSpace>sRGB</exif:ColorSpace> <exif:PixelXDimension>2560</exif:PixelXDimension> <exif:PixelYDimension>1440</exif:PixelYDimension> <exif:SceneType>Directly photographed</exif:SceneType> <exif:CustomRendered>Normal process</exif:CustomRendered> <exif:ExposureMode>Auto exposure</exif:ExposureMode> <exif:WhiteBalance>Auto white balance</exif:WhiteBalance> <exif:DigitalZoomRatio>1.00</exif:DigitalZoomRatio> <exif:Contrast>Normal</exif:Contrast> <exif:Saturation>Normal</exif:Saturation> <exif:Sharpness>Soft</exif:Sharpness> <exif:GPSVersionID>2.2.0.0</exif:GPSVersionID> <exif:GPSMapDatum>WGS-84</exif:GPSMapDatum> <exif:InteroperabilityIndex>R98</exif:InteroperabilityIndex> <exif:InteroperabilityVersion>0100</exif:InteroperabilityVersion> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end='r'?> �C�     �C  ������������������������������ ���YGI"=I>ʽ*befWq(IPUCҮŖY󗡌Ϋ">)-KvO1<%G,8jߑ쮲/'fC46jlRdHlA*L)JɲVu56D|fNEdF T8#Bh@$ 1VJSn'3UlA"n]ryG%\e_Ev5jE7v(ڄxL[D;LUpL\ߨ賤}`IV FFBvX*] :q�Lc:\-S8tRDQ7 iSYu*F'?Lf'/.JMYۤB0&E'_ a356 G˒}O1LA73'rVƩ$+ \)EuLxOeEp,g*jehZQ)8&@W=9my'(8R ZoC-hszL17 F16(gb �9uc蠚9VtTUVY^+nmI|� 2H(R*,%!zUM 8V%;z"E,/DI=2 RX6[r-EQ bZ;1R`h9#SCFF>P15&]�UjpDjXKcDTJ Gg%|SKMc5eKy4 K/69M)Adrd̒:,}R~Yad4=ok)uQYbՇ5*j1~R3I39jh09(:fCNs9:%q[Y%HZWiY颴:U0evT]OM=B4%(N}VJ- Ұ�p}2bGnB2#: lAEZW5G<FIBPLX%�T-覀MBix|$im3>MyqT$@e&SqK1G DQG­rߗdeMUJVPqبB;gB*-'S,FEM.vxM"G@90E畹6>y58!jSf CEh!CV8Qz#9+ @øγٙzRK1!w0FM8:Qp)}0!]ʀ_ba9JծҦ"SnIpbLœW Pi4(BSRUu9ӊ\|aUT n~q$䡛͚U�V0M"VBZg8%pUWwFM9 "e6n'4@Z7),;Nҵsm]N{w�dfsZwqOf+.*.G+aERH>E2DZT-D1,&ÌベDIHĿts'20+Qej "(2tT!v?R}%%>'|:y+,4 w|&ڐNc2`Mu@H"XԞPj^mhBQ>wh. ZƞͺYu%(ML⋼[jta20vrJ2t ЪJQ슲Ucy7O#eOF&MHQ<zyi;oC+U#XjGs;K0g 9ttQjTjYIl2N.vdu94XIYV0+$> v[REZYQH>E"Ƭ\HrYTLP`>ON)WSV")bxt3i |u7_MӘn^�Tk|.6H7\ݖz]ehI9M?-R Z36"Э]T,1-c泑{iv: L4w�]SP>OU"F2e(1'8њ YdO44zL9)ө4 3H3O۔GH !3�⡈DdYT ғ'b( d&L !Q¸TA?_VTq>[=2'<#|GX=+@x Nj-4YhzXNķ1Z* n'(!&: ur4NV6Wa7(3t,uBs=*L걤9JMG*W4FDV�d m%`p IК$>bMQMHJjRpvLswQ6:I<ڳ&ʘV'$-|Հ8_9@ h M9sG4૊!>|.u}!ϭS\.О1Ż4q=NZ-Ǜ=N孶zNt~]_2j+]XƝ02ԴG/+"*){FN<J{ݴa+S7ÛLߴm1J9R΂ Ka55 G$L|h#>i`%|L$_ʄ4"ꪕT\yI6Đ] irndD&B@Sdi~D~5?\^wemAA m'%/rռQI Wz(^=qGMioM hk*]z5r1gɸ7F{l={s|d UX"pSU<tB+=eL%yi=jy5li<TM'fi?0-0alu28( HJy tjjZa;>No/$8GqQasA* L(DMqHh>MM~=>E;$ 4{MvيY7(24l$8ai/Um[ߚ<7ɨQm5s5\@dq+*iaC Ub&WrctoB+5=D2JŶJQo(P2Qb6TB0 4s60\够M LHQn p5&}gcVkMm> }D֍33LelT^f2\U$ &dvOI#]T9U~"- 箬94 ~/ѕ'FEi$=txwa,M *49#3lZ̎3zbAB0CTvTVǶ:}2Niyd_TҲk;[hd3TScr*��DxBȑ%L[*&rJM4O5"fU>ÈJZT|(TIRN+/+ MY Bi甉RVh3,ilgpB@Q" P.#q2ƄW=rÓƨ�p}L@` i X~bDQ`V-jwq-``p%Ĭd8MD1;N%&'q[cEJ N:@ʅ;QOys^~UFiqΛIhՄ%(8\MJk>*4% Xcqr&)06dWG!QeLBꊺ+fM"*viTMgUmp@:$:`]2 IZmɜ~b[q!"~z3zpA@4bdq)5K&4t jdi\a}3jCR\3pabjNyR (fȴ - T05µVnnk&qi0 (Cla9`Ԫ\6h Ø'u1" 145jj- du yvjm84(/ ':8C >M2.4f*骨c_EU)7i\goM.ju&2lI>lDHÀ.ۻuȁ:4F.>V Ըmh huÁBj)K|UM.j]xlX4K<CH] d#TTHan[)j2ƚ `Z'YX!/8w c(ԍP#BGDpDS( I80́NjiqKSfi,9u3DR:jJ!$U2>dE [>os89ԕΫr֋=4h^r';HQd$C. N?%JMΙ25*CąH*h\%6u1h~jT 0_Se3ꠇEIM3T%F%fHꗴzfG3aO0?Pd)A� (Q OTS"JQ͍cyi uƕ*Aęi`Q'|)%x-67cI+JF-Xl`Ku'jM4J8 ʉQBh,> !@Maj_YQg`Q&]%dUxz-gйʒ*'\0᦬p),[fY尛)rLJ0,ΉD3Ym{̗cꕗ2EE!u#+F$Ts4C,o镎:DgjҝV$pD 74|LFh`Q\zǣ=a!ILy]6ZSDYSphw2C '!`q1!`UI7T&bV|-봚u$nvY\{絼8 MQ<y4抓di$Cd5pAdÂ#UEg&zJNti69ŸDQH"{AQG}8m~`ŎidC0&HZj;kDWA D<JI .0a*cRVIJJn`s:V IS�U uuSXl{3 [32'v.jsJU-^U7N}ށYi<yv&i9D�Ő&+1٪fjP >-LRh@;/AgiKIZfj+*+o;6vhSI82M L :�jx}&bN @8ъO=Nn\W "ZA""pl#Gs+V>/yIW7 -*@˓,vP֦ kG!Rnl~[EV]wyv:Tܜ@Z[N .&Z�RqdG*sci^ONyhDLW<Fqe.1 4&ڧajr.)�DdI% 5Q Z1yxFE;گrJ=3;OK4&}4GiSa"?@&GIIūZJꣀB )RQMɁGӽ_NF⹐j+ɪ )d! $8@13J:]û_KYȒ\ͪ(H-ƟKPajAS%dU&rG9ڛjuw;^<xYvC�f } bh_'ɥa2Z%bՍ+sҒnգ4Ë:q.9yH!sKKe͢ԱJ,X\kŹυ"2E!48斚 ֢!D\c )tZG}UTJZȺe>0`1U$ )4^oLhy-7]4 U4Ttő ɢ>@Ο8I;F9%NS,CSbTh$H]WvAe4I cRad2k{}dbNN5&NIp+͚R 4IK',I F=x}akD4tB q()-H|FoKq G�2lui'k!u4*,su\EU! %SCN0Z4.7Z MWMW篝GNg>?,TvmY##ffէ_qdN䧍+Su 34Jw4f=-/VwԼ%^"pQv)k*qBM$ԯ$o;K窙k]:&Ӄ4\jA1R%LٹuF(3bRZÔrIty^Y� __%D`⒋]N.^U8)T͢F:Zaxj*\OMTM$ĥSUtifh+S3N;>?/vp[JZQ�NUDhy;VUJT|VZsJ`ڮbguU.}g>M'i7RZʞ-fۙtZlDAKgc$'R 7sK12I8]dâ!ggyq)]h2/4Z@A==k#]> |.5!H:]T5fv/Ѐ|õƋR:" R}[TT%Yd`JD�KUr@QZܕ̀!QQ\zr9vYwz?HhEJ1Rk&%\Lfl6ؚ";7\{iW[NgT~ffț r�B䁅]|54SSq?!!N 2rY]i� ?93gRjTAf6GEN7䇨1A8K q %I (+$XfR]u 8$hxNte~eQSXV]lNtu M45ZtXmS6E MgS XuIM3DQJ(ƘTaeL`4C,ъmZuFT=@Eϣ7JmIz՞krKu#%*R] ivrM uU АN3+f4W=CERkIQ;w$jh:rZr-uCWSMT| !-A #"9 Z4ХG:& 2l Ivsm (*QUMTM/4$f.5Q�)-[&ϥ3;y68uztkluU՝TUAPU$I0I �R)LXt}W"egjՎkqkZԴ5^}- 5+1 dGD^zm\-5M-X9%�+8)HR599#:"r+ĨߧaQIktAB&4֗JN3=k}GDfѩJiˬ䦩g@K^nz.*ieHPjmnj]5Z~O'kGƻ;fublRʄꆘ@HQAR2H `м@خ)ӝ/_= _1qpqPo[68e$5'qF@lLN,iGD%R; 2as1|Ͳ T3#}i @PFuJesuybUQMF8(q)1d$'dX.m\ghL:Gm2o#_�u9utt \%'U?RŦHbϹ ^>[gkץM-%:3:_ �Ii@ҁ+D�ha։A"W5ZVT| ƭaQͩUEιk6Ԝ$SP+4Y42&I]ʠr:er3FgQ΍LR!J`DӟN!D~=Ҡ" LΉIK' bD0IkERJ\1:2j#/Q7JҲ)uK&gH~Ъ~No;F;_N>\[+)oN 6t5 @Ē,eTR?]5W4J@u+9:|3M3km0/\v,\)4\qlMIhgNSQp) USUqNFΌ$E6 L@Ec"0u<e@s �:>( ]C)qUL NZvZZIAx6ݼApٴȬ]Z(uVzzem�I'ɬ7V/ w-VYбgTd(5IRNR1I ( ~lR%7T@ }Y("nroi-6^R#&'r&K$8 &i_'r+ʷLIqfSR:gB ""+:|jBDʧՋ$(C$> i@DnmA>D r!5Jt䖚.&?RA˩c=fM9e"4onX^>K>[:2,4PSP]5-!RXc"@]4U' Tu*̮*(?O.{C^[xV[-lnldy3% !.2i%b:l+ꮇ_T)4fWm~yq:SUE ;&�m|H%^BB> GI&AD RȬKZX&7SH]sG៥"hZ糵5"jiMTrd~Wpz>FtZN+<d(*>H� M$9_)ASFmwϮ Yji3|4pj'-IԶM6ZŞo&p; 9^#7ᲖP,5ȍtDJʾna L+TQc YuMmP@!9S0 bCD>D@8"$pRGʢ>7? 6@_0R _u1Hka9#\g~~iM men:Y3P)ybN-ͦWM ?H^cӹ]7&FfAP(AUkq &BM4Q}2HғHȚb v^tODPtŧ=/8R㖜Y\6e;XTA| @S On%ELfD\ayTXHUZ|v9H�\e$B8<7 ` "t2&�:B@a@夘RkܛIt92q.V?+c^Z I> 1qogehpW'wݮkţAAZ+/�j !R ɤЪBRC&՚R]x3sy*䱜},#"rZSckZÚElbLN/aWBflZ!$=~Ծe2WAau1!$ ${qO'`@QBh 4v_|Nዒ>J㠼Y^[-q_rL#\\-o; #M1y|6?Nדcӽ]:}۩ \ UP Ȱ h&$@BxnyPΘteߖ^a{9]q1F bHwX\NYdׅcIB(\Ne Y{M13hv]sKLҸ~[^+Y6py0("E�@V;t"0_Vq I &_%E5 �$M^h(A%=% #iޗ;X* 1RαapCTlJ^? /'ǧHړihAS"`E|tI$0FV|K|@EKYLI*sV_7<aڟQ.a?kC.Y`gkaw J(LH}SEpӘ9*M܍.Kz^Ä́yםIj~?D@$%H�F@�2:޲ʘ3]G1|(( }kpH@d.!ykKmGl}щڇ-QZiebBH)%oIr󹸶r`�'?Oqzswjpxh4eF t�Ԝ%$VJP@bUIIyh֖d-b-3,_%WOws mHưr"W0ҋ L @aNNMMzDkUX*gp[m%Z͎L|>~<wmtDkܘ ]Rpqv|.фbhd茁Y {-ҤF5O/Rh RDէHab\^uqy`t_uEꩀkܐr VE%%%jCڅn,+%t]vt iE,g-Q[8<W}/DkEVk6Q�YM<jgRfetTJ]k.K̺)9I]JnMKINi݅tΎ @>DL|ԅ0@rt>_!I" 'w߽8zef|=iV4gtyiS$4:\wZeqŮj=pynN湽-.]~;ͭ t$a`Í4hKB\a*uMjJԾitQIE"L >xܷXk61k2ZMY4ZWaq#uDjbWUvFhw~اw鰚org9,!"SrewHMܛ9CLPA[SkIĜ6|�Lr^2y8+oD1dҏ;^md18ݫ1; y(,r7~.Cի3 YUb IPBb͓E5WfU zh dhQIE)4]G橮o95iv]8teuWB/;/F!6*lbhMf/iUϯY;˨.dЩLny-[Tyu;`kїcG 8猒$G#La۴923|Q�1&qP`$jr8l d#\zTjdA)q{_ZYǧ~?V/GwCmVNJưƎrm\<j-soUvN^WUgnK mҩh9|N2ȗWo|uzE*1mnOFBEvĝ|ئt,8>~m<*C<N&FT'S r&X"YmF:<$4L A-+Ξlil1_2c!HA @>` $<VZ5gSI uiu�LL{br?6 w\}MF}#liusUjCQUoTg 28i/3iWrm8ҥ]AhĈOM ws_sERHr&]:Oaex6&7jYh>~y++2mRH%Nѫm1[gMMjt:mA!ՇOFaA `Q>8I3RU�.� O#f;7Z+jLtfF y);gczqT릐 'VP\\^:)jrREf!Ϯ,1Q5gy.ҜUb5U*N#mH즖lkí5Jg^kN6 9EO?U{?;Ύ\fӞw5S&<έyŨrqQvip/Mfv2ϩHSrD~q:2L !"De$A :Of8֨iTkZ.#  gWFMfWyo/^:J66 :@TW.ԩsyts鑝)4+vUgbXJ!$川/>%fKӛozo;y58io7GNm3bf7}d%m3C®:3햋I2e'VEaQE~kQ!ŪoXP.W44T㛥vV[@j&Z>H|- !nOu0�΋LCP_ 1ZƷCsj]<~zƕG4^kN4誕Ks=|}UzwQIhB wK|rMXU[jnѩkV6&``ӊ^h ]ͧ+u˩*39if׍y3h e_QwW<N4X<4jl3DVY!nZb7=!uDkw[;VF>jiu5 DX~GS秚-G4L}j D@&ϙ6!HPN GBc :_eKTSIƵH*�]U7:['ZvsdYZTc_3}/.Nim¸,㓉Rk@cY�Upq_Cbٙ;Ikr<³j}b-Ƥyi'>NM6UJ6:M/_o|OӴBRdu<;|~OP瞑޿Htgi3Y/'̤ iERHE\a<ipvgˆH <mgSM.E ӕVFn(3>DD3& HDhABɔD0~r,ʧ)iBuA7r#iSE|/M[29gѐˣqI=KI-tr� O…aUy%yYډEwJlm)*S&"Q H}h6k3Ni]&htȅdFXcDS3Q_bW4i]v#D垳ޟnNݥ̣% ^= \l*=;c qC̛vM餙&GQ#MTlbຘVAg""L(ה1 .zpKeIdM)5ZOG&_'.4@l/NsrUUMvXܺ>v]Z͟4]�*T)+bRjJQW7*$k-5Aګ&mm̙BLJ &Q\b,|^~|no;΀Ft|�}}ObEHfNg]?1]w4Ԯh>\|fZLl ڵU)5z,v=9_#�VjTH$U,E"J~Vp$/*I\"ƀHR@LS=cus ڦ Tڲۏ?诹}E_K(8d(#Kqos;Ͻ?Edl\SˢB]~\Y Eh73#+5Y&K 1| iYu#{ OȊ`F<f՗e[?]7cyyTNJ8{"I0pu_wNUR%5 xQ{1<yf3x9KZi6&�\찹RըmKZK1r:/*QhE2Fr/ʍtRnMpD@0+"1u(rD%SuՇ5ehY%Y+OL9lj={]xuQu-O=Ƶ4s fgמ]ݭCdEiu@MFNb+>\gb^q7$l*S0sஆ \#Tx m>펃L\Q^}mZD%TxM=(GE\]t) $/F?O}`ttOt{e 贍FWzvXf6s=VR˕.l\*QBSy0\ib)ŒaUM ]P0&SJ1tz48t}3|â5+սoN#韯oRNe92=ѓ_=>C#>V�DjL kXЪZUyHoi%xT-ql%bz!E]lUli[<r6Sax%wV�|m;*Ҝ_F\x?C|og^{Ma ~1gcs,=D\TܕF/p|WY35V1]S&L<1>"LA5U_ Cd l8(oSί&YqK<&*} Qkso{ReD~پy ;eAsHw'?=toʞgDMuV\qSkiomFƲiseHf餬MZ9^sq}I0׏E|ƹ]T8~{Y>Rj+|�uO3UDUWȲ!"?I.^s2^.w7(Vn*ޣ{ўXg)H ү7y2Rl4LPV؊07R$OeTq0@0dnI F)""2'^ci+;fjwqkpB^]|U&7Cۋqz=�CͮsZgUj xmQfV3<gE4uD&I WMU[=cbz5U.nAxzfeuu Se*.N/ZiM<zbmj!=9r_yOXU֖HH?yף?{æt>/SciI=uVY3Y4i&k-mۻMd\JQJ#r :4WN 2RA`$/3@CiCy5.]: x3s!-Sqޟ=H#s74~af{V: "'&U ꭨVV^+Zx)*-EFs-1]ΰiVG=*3NO25y*>F�.iL|&-fj5Q3^9UZ ÷O욜sn?U]FZcNdso9g4Vh|u~ߝ|#.q5;Z`칌i*Lמ)ɸ!>GR_:SU9o\F@�|_iz|VTT{ܩk*A0IIK<aUU}/Ten[Pg*v:gkʸ\7XzQ�\|]०+X8g#S3R X=͓#usҪ*kTQKPbwkǚdƂ=sԴoυus4*4z;,EVV?_Cӟ mz MN*yUcz3:+ETzWG/;C J#`h|h6!ֶHS~Bѩ9V�ὲOM1W-*7ѪМ&4B#L:Iq) ΏTtpiglt[zs^kMflz"TPs̕թ9iVTaQ̌qnF7[=QBtY̮٬7ziqk =u˜?M=<I� ^z?O#,<:Sbj==3.ϋtsw W;gMF~htF jS-(Хv;?+L5wj vYbO%=^6`dSЃ'QGݗEi<5*`3jEM娳3I~[5UV^2=tNH,kǺ^,JB$đR(ZraMFxTsEyhyY\VGEv:WK]+>Veyikη~o:ۓIX4mm3jV78-"`[I]TZyP.NgGۇi|oNil+>_Czϑii뎎&S&lg=~hnTpn"L-;4-6Vh~uFƊ Jd )Z_#c4(3298CnYx^&kA:*Ϋ I֯=W4Ҩ ToCW'>W·"NbRhҫsdQuy6W|i.h^;JFtRTD+7xYXy-1;rjK S: mm3|0w_3E>DU�U,ZQU2ȥ-6\Ze㮨UL6uoQ][z?5癕TӶChҵYU+_1$7Y#k/Z뗉"d/2:)I0a=#}.-_'֬[2ACB*4ye'!=jgMVz5f4hpTч{E±Nٹچ7}_N7"j"VD*@t%J OVxưf1M8KkJsxΨggY3YvÏ=+ۏ6\}At%-O5tQ} "BU5SBX㞫Md Г::f\u;dъ|l(]yߟ9/+$ehK^^wM8߀g}5-o=5sqarjZ4du$bnvv Z0t:vΝ%Ϛn]>;.jZRgp6lͫfD0ʪ=&.񭞳Ζil6=] G4"k&2S4k=eôHՎ _e4J•H)s9ԨPQXy9x+v/aQ[e4эV{G媖\~z4.ejlye˪sQo?P=Lo4=_t.=^Dh)Qb+<L4Uo:< /SWz^7zE̓DyO4]#$ PE{c vL3sW!-\<F'stO-4P).<z/SFz2>^uR*n<S)|wɈCsVNbc"}ƖқNn5tҦJa3;.]dQVw=(%䷙/&=>Aax^>o:-wuT[l;gg/2;/'S6N Mh܍%7$IrihKJjV-] euy�`aJEWOrQr7!UZ=/cQGJfc;aYI$ft} !6CmS#_N< Foũ\]tUFu$,.Z-u㪥7> pOF5ym&vstxyrMI*\^1季ۅI*yq7AD^f )᡼}~y\j<G'h s="I~gA.}[M'qeQ4-8&_"4ZF6!SZ㱩Zf1D%10Y؅2c M FEeuU_GH\z_xi镙/ z_ӃR q0;�CӽML 1E#STYt&NwReb#Y-3oq2H.LO漽}Fwpa UnO~-^=,MJV["X ))iET|n/7<A <g 6ZZWN^^?䯂ɀ5Oƃ-ֹe0-yNۛZk鳁Q4&ⓖ MUϒ韭)"Oqty򬟞feB+h&-;l7~<-{+�LLN;)y N7Ec@�m}~rzoV} 4YTkׯg]vzgҗ=+XMܓ<-Ut`o<gG\o#vFv๺c돵Pgztdd㴛j::Z-P�VATaͥ(W6N<\ryeYuK:yGl};Yiu8H\okTn]OFḻMm9iKRZ$ms0G- p"")Au[IVUF)v9;~0 pɉ%:-q;ȤLT_+CYa&Թ_=N]6|||5hzc&24Һ4J^o,s\z Tl\{޹ٸYY{^&M4ʼX=TRy}"˶4ީU,iOPW9pUW:q|n^.*ĺDmYHն&o:rJh oэF+i9F='}{.zTȫ<㽚}jҒV1KXim#5([i7l^ P'i9R uRQNf9 ^^{?ѐ|K)~5ҫ"8~AuՍ3I>[\Pl>:{e&:e&ΊHvo*X`gx<w旾!9q5KGq8t'׳ S7{NGa'_18v1֥VtJ%\՗d2e]|_:<M"^mМ89Lcά;e2Xk]Гbn<oLwx{:=dsYmtf(I- juymgRtsyz{g0x79 C),+45̺ʧ4$H&"IC\jtqxzd2^l^P!D`њrzg'vqyk,{=;^jjn)Qauwpvs^ ӋF[YpʶǨtsףŚ7m|cǸ[5Kyg65Zf4sͻyzl<6:61PDlFъ@-W>|n<y?__4&WLP?0#zN/GfQtu.jj3(EzTmZp iw>OnG6,9x2nNfQIZX.5Ƨ55/ {*| uW"bnaq6Ը/%Uս.6 3_0s\�O{[?g%KW\=އ֖w_4d1UknSj/}yx3/D".F훠1l%Wm-F6[EE~<ɬ3{-èחlHfn_Ryl/75 1QʅVj;ؼ^UmEe&5k:\Ʒs(j:po1�5QZ헣uo*QG@@.( 3¯K㯞~V Hu�0^`$2ҔRDvY-�?Ntq"'pSs\*.=ΙzUFֻDN_qLg%2>^dUzu2.m)XyسRsK3b[sy~ӗ Q5w:&r1ђY=;Wx0Y,{=tT/4&\7yL<H㯏2k&&wT{zJoF>їi7nG5Y/EE*Ky}|M{RtLQ*$+ q:ⲹ\3 Bϻ$KKWYhBVQٯBV3Ut3):E_Au78"d&s+G?>mw-mw^Ji[~z%-g<$PA="ߧ/W([P뙮Qsև 1'69QK6uK><?tWHKU *'y?ןF;Wι#(٢g,jv69zqLf5KՋA[Ɵ\*4-z<QQe UC.:`zDbsVCUP!\$<GZ^Rım!*hpr6Q}dΫ-xu 5$OQp巬vqm6{_0eh[-392U4e^'I;B-TuSK8J=)TTdn?Bk>Qi'Ɗe"_V:秙K󙭟>gaq-k-4&D|Ƴ% yyo5ZiY~_Myw ="ƠȊ�zpW`/!lDc( PtN 4'$ccAGQ IjU +IJ'C_WX#EL|'~yLu ;w<ƉrMr%TƃsH?>ҳS=(%Xʽ#6{ cN]d0ڣ\s?O~ Rk.׬MTyWy v|lͻܲ ]V8kȇ*un<fcjqqe[l7/s-ƀ "HhEƀV-ZZ(¢},)9"S>"Rt$Ȉߗ҄lY<yM! :ONWކ*&:HI @E'Lԇ]Niq:k7ܽQ rMq{ bt#-0D}E<wmKGk?p6SktꀙKF5ja!hetz̴V8uq(8W>3> UoMʎ!)p PAW_ __g3vz^J9U Rg<;c4V'|N("M(DG!^=D$ij(5*-#ǯ8/1Vwʸ7*_|NkRrGCM5WY^^v =kl?Lz^PLr�EBNjmy Mc2j4D x+-eHKNlI*aBӅU4õ}TݚG;J<N 3 yXk o[Z^~nv]ASq5ъ'* 6~|69,ZُWh h чOPt8ʖ  rlJ Cl:A`1 ,ֲ,{Ez_%㥄U̷0G>qWy.,BSZw¬ft3'D4A9h\oK| /ֈgQtesIžy}rv pJJֆvY怘6u;qTd@|FkT)k* R��t,QzMO*RqUSZ-Z(3ҩY\H]4�G@aI5uЧHtk/:+$RyW)ګdb:S\Tҧ%CIn`t geF4r ^K\j7a9AsŹ9h5cvTڢ`: hVn4NZȾB ι%gr6]�}N=Q*BjqNs&ڑZ]$ 4P:ɥ0WI9+I3zǵ (MPx V ykvNHVDL6M.ϯaTe_�Bʈ�Ut-%J+і)Ak6]0557 iLuF&+i7\Эk< M:4֓MxօCuUS |OOZCE"g>A!:|WIG&$wμޜ}->(pi+Kˮ2M>[sCp`Ă:xrZԪI5Ӛ&JɨATEMQTZZWxhy<~ZiHHIOҳ<N9}Tqїur[TmJ*Ydm `B s$DXpm"ɄtaM%:lyczZZyT4qԑZ82(Te5M DLiC:Drd8)Askʕ؜ TsaӟJeg l\CXAS]i*TAᾈ QHFT@Z)1estԭZh2-iyҶ(T;ba/MX:(5B7M`HXEPE)5ĕgZ�Cjg"% ꢌ*R7G eTYIvIGd4W& QPrk q30IeajzW$g�h ц)eFJꩧCZdG?sO-BmtBIO:B=}5 @m�eRJ;bN4b&4vUURZs`*ƦI hC3& X*T1*I!&ӘBK#P"5 1A XjluJ#a;YzU:Z6.yڳRQ@l{FFˊɬ f@dQj!!gT.uDI>?ػ9}A k uJUj&L#^� IM9U>q "'JҊ)i̛ƧAs;zTUhe:DӚAKE642mRi;IpVmz#ԦQTSA."qN * i"K 1(> YH(|B #�q׳3Fq'n-ҳu `(E6jD+QN/9l6A#Ƀl mqq?dLܛ Oo?srzf1�PdPP�Ժh5CE?U2(#$ h9fj &ڒJ 4Ӯ a>( -1ˌ t+@ M bL d4FtFh2iA@8(C "p� MM:b LqT3X siҒWͯ/rJSkPI>PcYG&7G;8 *{Y6m-DJL 1 #] rL "а.⑚e M &?Ѡ4e1'�4 fֲBE7FbSM!1aQ $8!9!X"2'�9-p�AU>K:ah6ˆѥH*/4$AKE3fɢ3|0 ci ( 5dfZgG}-WnSHkhc 14ȁ�5PBu 1[`6Z&X H0@`DETi* �MYO 6h6 Lk 𫖪`Gj'6!dU P@]G$p"@} K:l"9hD�0U,i6vT GRr,)Mhht"Fςm2*I+A躊)ydwAW{gZ&VDW ;D`�&` 2E 5HX ``+A�(J&O5V"D@  `�lA 8IPPtC$5�U "hȀNЊbNA$EQEhO%b:�I lD|Qт^K+[* 8d`eu$Z!]dÖ/T֧;:D-ktDqc�tHC.X:0H d@0P mFUo@}h ]4FA) $Ȏ�+.4MI`2 ``}_ qdXQ}"ٖ$3< Ш>|8UMaGjLɷMN( TC$g&,qLsbi0&L)_98.`xE&}-, SiY9�SX K4h#m� q*�m�.tS� 'G k6 /M5ӈB5S 4@iZ*NmDMM lI pA \^&]T@10c#" AӅ)0'T~W|&F?DpfdE ]hjƧD΢.F(ʍq\|@@ "$6H*&+T`A&I10DI,ΡfHm KD ) # FP$,L)K`(Sm:&P0(�&yj$gD!60KiS7,d2F0a48CX�` 2M|! #4I?<$5d1V4ӌRg2<y7Ǿ3| m. ѐ `%8BN _t8NKLbJASLV, L>2и :E[| zuNIq*|F1VID*HE8֌ ywB؍a^4B; 3pCQF:T'A3:Ⲱ:de�.���!"#12A3$B4%��#Y M&G3d̹ R֟;/OY s+9P\qsҲ"*]g3qQv$ٯ6XXQbS'?NɬѰf]oP'Fhh n0+|+eqL3I\qW' Yk�gdY2ƏFeB,,Rah g:4WRDIA`�?^Ysy:I0H2+|a^}1=́v<hoV q0ꌭ\ *3*ɖ`fUS}!_>8G #W$b5͗s7 ~ xqan\Lm;!]hL028Ϯ2W}Bo=EC?hV+w )'H$8%RtNuSrnyqfY%HZT=fMc&:bx:bמCcD`țPhkH6{+;w8ῶFgOEc|e6ri8nQ؜tٿA_.)/0wq_x>rHӘqdIg|~/Z^bJrqP4oUx$8gdjXF?=}d�AUcUd Ad%Ǎ :MV?; rEՌ J FH$ ]<Ƥ zQ8 N)N rT-PpFqucEN?j>�qB:/ڬ,DqBE@$`b"01g\q_u|ujq*^qG:K =KnŖ̝0D}j.կM*԰^lG9x@׮?,=Ԅ$2A6UIg< "<oF.?yP/imP1N.m�o6cF_|Xww ?WVA'1B#UY%\ c #0'�+k0t['�R 3kNm{Jejhc'ք4LKGXײ4,c4Iđ>EGq*'gR^QM񳮪IJ'[-VZ/P̯k L4q+S 7<`&.F$H5,,0(WE8M1$dɼ#!=]x )UGEǐ(�HkO(-y*nz+*ؓTM$+ I�}u[y1jVYd bAv+RC/AUe^KxO ʭNIe+ 598Qm$NPvχ #9<>X/07,ؤeʑ̼]hs(V!0r*XZ6vH:k )'\B=xG"IamU? w#k*55ز\�d*W̽KFĎ\N`y+D;I i1Y+G*KU񯱳޸md|u =!}y9Q D oWk|)S~.?_�5YSճF�Xu2[CM)ㄹxhtυǙ-QF-rX +`~%^Q B8V@;M*yV/R6}ͮ<h} 3 T⍬E|'!tbr7$j ^ԗ9޹\?k7v/jKd/-)cS}mGݰx<&<]s\Ŝmg)Mfǹ.n71`+-YY=G?őu\W#01ma�wV qvU:ԕfB2)vXl }QCx^NeiK'3-gT<QA ?"ᔫJAW)YrDe5FTL+ j\Tc}J`M[$4Ƥ}eq@Ubk ĬA><Xp56,6%$͊ɮZ'Hں]Blߵr~\"?L| !I3}p?t\)שeQHѱt '#$+f㟤zqP!"Z//}.D MrX7}oI 'q&(&vFq|&qC\"qqGOIY#ؕBɕe`3;u+'lCF olAԫl0o{;vCw�g�>cƅ_ o'lF #0u#?R4-5Y?!BPXI#^km5oj٪mLˑ05Q CVfSRsd/C-pso=[`?8Ccpzq|032kǗycL2|`sTYY}3h7jef&ɩOdS~E;06^7($Ί{I\W)?drbBS jJ3T"+/q�#Jv 'z;W'@|E&RxY<w!ePX#mn39(èlL||N)li5\hSef N>JfO\_2OG"͝h錿V[ qşUBc9VJ>YWlI-D̯ d #,nF` cB) K8@һ1*wj\8l5r% 0[ cڞSa bxۯ![i .W$'+YBx*,~KjO2&2]b6:⁀>1Wf �f빿Io<]kUe*4G�N8E"휇rۭj+1hj`~LIhKVY zO�lQv'Qeƈ`@Kko;emdz9$Ep?9JK8zkC7�O!=zPqo׬|ǒ&H;nD=,Cb{lj#E$.ڀr�C5Xr]WMgU=m*g̸J,hmQGyGwST7cHH%0D|$MD13 l5՘V%EP<Ҙ#|eHQPtYXuc&,Nob(!vq#]ojbbЖ=3}&~FXdi&@wv8#:*8;ܣT %Vx009 %2)gXZsbvk Zo<ut(gy"$U=cOOXfq#\3ڀ <r k{:p3ٰXgcu*IW_� Cz!&D!_fh3�lX:ex!)>B,Ԃl_#X^,|CdĘ"سQ{ܨ}=R6ɏgy }|B^8bLј>4#c-żUmE[shAξd=lך8ZCt.ڢЂ9Q87l2dM؎+Y>1h_tf]sud˃?7`8o%/%uLz8'K,;ac *,sG ~ᦍ|o!XWYdȹ0Sؼ"28$J0刃e jF~/*C""RS+2jS#.>1xo.-H$TV־/V'j'LUbdgH[ujg8r_E�],+%ddQj,JFCt Ϊ(Z}Se䱘XzTj}vu#1HZcKK f] �hʸz!N2|�Bcc|~~;GI*SgP'~̯R\u##y5<y۲h_@M]Y.qRG&wZcUճ YLuЍ;-ifISӖ-Ơ)-[ޱd!>qhe"[XrRq<Ր60/T%_}xRU{ L[u>դx*bx&#K_ZH@ZJ]@j,hA +$΍9êI'w9=sꝕx23($<d-hqR7&h IjŅőtYY,�q�r;dk9H$q&Vɷr-Q[^>-5|z]<,́ő,g[_?ʠ1q`dh sɱGߧN{Dl)#}.IWY<c-TLݢ"S*u AvUbZň&gd 0&U [b#>HA0q\\FёCAzq|* y}3pr>5i9./xyxٺV;Uz28"sq[@ ,ґ6I+[^\ѡ8Y WV 1;tWl`abgSǍ݀ibYҪnJѼf)!ž.)Kt*G'aD|ˋb&d1{+!f~Z(UD2H{fDLY4|+%g\S$yr0IB/` =5.|Ѫ |Ў_DjGTZ1!!� k#.RB2/}u&&٬0 `M"F²`OP^uw� <N-lue=vzi/|5Uf_P1;0v51YPf3rF|I5_ng<W&+27l.|nq hc>i$d)Uq;5"Z3xt}bb(L i#`G`6RǶۍWۮ�ctfGAp||.|}m̟`->_V#_ꥫKcD�)bƍ�ʫF#u_PmPVWw1x񓀞ح2�LH V xvdǀ*0I~G`{o;M6H7żƉ :8B?/;!w=D<EJ\iW {LmH&Q2R:hx؉zת a|CWXU�c:կGXc!R%z,_>ցhWǂ j0˖[l~!M^Y~swbyob�nq_&/F_&&;s<KIc>`.?)~ILEo[G#2qx悷"Vh5,l׬0qC`}3NJ; 6>1Fa'PE@wvG S#H11eVi /yd&5أ枹S%FadYՈ^ yzt"@H~dt|dGK XggL$8,78ޯ5:ófGadNcr\O|eefH4ű"iP LZȆ-#)~dg ؔY1 D[1(˲b/Y-%GDx;1ޝ7݀_uhl<}Io㬖>� _?+Jq/b:�X[;COeXuX&hjAs( 3;^s4o nˑ˼FrO"r:6b[o纶m}I+ dWCz"Ey<c,R7ˀx#\ 7�uE/ll~XEX 0׼X[UH}:`bGݚ-ΛƏx·,{zNa4V|O= $i1O|~B՜a(:օHX=iHLTBT#dN4"2Tyl:,Wh۪ć"N1ߌZ&Ң rcmZvU=q#ĕ:P<1ƙBُ՛v"_o&ܛC$ߒ`"CA=竄ˉHIsɹŊV8zC'n>)kYk,6Hszk#r n0BFE`02~�:0d*qzL 1,aG=K"A#>1?P rdmӒqɮYSb)Y,@^E?lHee8%+{k:}5"RPP˼hNKGhF$YPu Alq&೬'hvsr3ex5ܕKne孳=5Tg|07e4z@4Au=`;'GU#JaɝUFM mU8Flk,�1ӰWS">?`gY#9[N�¸E3urd.LiKvtnDAoE^!?F2v3n}? M9GVy2y6KP :$U\ :bH l."C>n�P_F{2+bz *�~حucIF-ђEaRhWT;e3^#vid Юv Q||:ٱu \D,'#e½DXltk;("W�,zSc+Gv1B}cj;tUnie1кcqјa`^*G 589(GP-]7' ّiOy1bN!^Fv|tb3],}gn,N!ƗGz9I؈Vyfd-YwṋF.R %f6̶v)3,_=oH|�ܤu):xA ٮ| lCp1� p[ȿ"+du$eƬo�8vO'KW] U" [m؅dx-ir5!NdflR$ԲBc2YS?8Jw->c?3zlLv=$K\ɱU>UY+ŋ0~38�Ua,}Xc8Ru ">LGb1Ҍ=^ď»κDaA_ *- tYuQz$QuЮTuBkؤ{cH 3ulm3?$򁘜C<[uHLo H+d$V8^!bYh.YOt=5Y BSj:yԉ$~OɔjWْ+ JLh%\k0lɋ:NlUWvU-GOivlOk O~(pA- Ii�~_E٢x߱gA.OGm'uhdž3䏓ŀ3h4FkU2h51t>ALkӦ\zf q`AQ:tGʼnuc)ĉ]bc?uEcNɸT#c}%W-,7XtΪҰ!Po u FDXeI2*WoDr,K4-{D+1sk-1Y"s%^:_ogB֧=</(Ȫu2+$*AeOjM .-?bqWt)GeV&uf|xe?#)އQ*o|~bl3a,bgm`?끵 GoXf5bu@uL{B#oF6EIgrJqtmn>_3f3٬r h&(XÎ,!6~*6-p4NFX9Ou cуԱWۍ=xW؈:b'F%~FoGf uh}(>J(oy1,c){1Uؘ̾LJ: !tHӓXeԡ#24؏<%. gwxt"-ubEb@9^iq 2U5 vGfJ늺jT*uZQ hcjJ!VL"産[qX˴ j 7'"xn7r1GG)R zZLaq +9#HVbCWE $exN3++]0�X:NC9B95BdHڴI4~2/ϓm4J# K[F4?:ma?n $y�YcFY>?#:&Hؿ81I6hdtVnjdї2W,`#3UV֨K۬?hvc(ΩM *#7}߹CgrgNq 3LPؕIjX͛2GaY ;Dҷ�4m%nPo[*B%)ozII!S#B5-TQ3ֳ ܑ}P)�r '+cI,1 YL)Aź 0FW!J;[a)KHi{>N8ļ}?z5fYR."Z?"vIz=F#;bi4 e֑}珴³s]XBɱ*MlXiiYD [A|u^K>� `o}c �]ۮ&}p:Ko~ JFXxٱ=jX) tQdY(|gر0cmdm XH ⏟+q2[Oٛ!NrviQZdJ32}l,Ӕy$C7LHϑF'\hJ!.C0;Vs幅[ j42U :>ԝ� ^ӯ1'bYh^!#gÖɱ!_$嚒jh>+d6"X$fZH.,3)hZb;s@:5KÑ %7!{0ŕG(z.HoTI܍: βF׎iڳDa`h蠄nMgTa K"gc3AG2{\rU/W0~_#R?َ7P;w~~W4`80$cE!ɑB`N1̡v8ׯ|�X6rk^[ZyXÒ7E@ޤӿyU-#G$Jec[>vceicy"59\o5YIW Ҙj,uxRc){>-W%+Ԋ%G2۩MxgÜ�֧V[i=qzzJ*~?+ڞkK6,jfWbN<h_<1cȬ0Z&-1Jg|r2>HR'&E=k%i#Q0FBB#b>B".;Cvc{^˲0J }m�bkX쭝o'Rzr}QZ݈/P+|V8@͞$zd`2zDs,+b8 ,* 7P/pFF[=Mk<N ; NOs2wfi^O{>Ģ,nmMh6]geՊˣ]۲?8װ) ud+NEPUʴҬ =}G[gX<1/I֖iŵr�UaP: gՑ7egK)+_lЛcΌbD)dI +$Y''GXJN,Y13M7Xbƽc`"PFX:uɑZ.>qY8Еr2ˊFD{�@&O}8#>XzdֺC!hc|t IN9FT1?l:mo0y>:^[]4 (l1m:tb䈀F#8 ,Meĭ41?#VPG2DS޴Pu$I"H}S3G�\p%VG\nE_1OVIekWZVJu+Ym:W~*!xU0FreF,,!DV̌wQ2%b'^Ht`Rqz�mrgϷZ_;y؁aYi&5E\ocF3BG;Br:ձ]T3d#8\ʪۈ [.R#t%+FB1f0�8^9>S)؟ | 4CuڪueQX@gRr؃ťFtջ=Ze=T7<N3`ưɝI:tc ֊KЪI8~&i#<6'D #tLdWvb]i`,a+SQGyBݝR`|oC<Fi1QEH�8˅12q! R,jiы? GWOSdFUe*l: eLa#mm՞߮3̱gV zQٳhkt+`#F�b9F؋Gq&AE 8|�{f[Du d˩S8:~.|i`>(J\8:�"1NXgEs6!1o?18 ]:aб7}G@aQIb-*!Ӧ3]P6;YV⏫i%vpȐU[3h2~d6SRJe%fnJ̪ (n:;-,U8`0dq1Hqߪ1AV<x>{t.KhA]'k%pZh["1',"fȧ8 N>d&0p$1\U;:db+wVҮmN9I+^ʇ<JKdSJ\ 3q K/`Į&ǵHZE,Y^gcT:b'l*?N}l@.HvdPA6Y#{3tpbzB E}F TBT^iM:W1.ˇ/q&Dq牕e&WpEғĵbYߐ Fz5jCG|b|Ul'ok?)�uؐ|aNkyN:�[ ~c#Y'hI>4CNjSb,!Un, >]fB6h XaP9;C|ð #;fFe�Dxb)Wq]gpbX'>3�r7,ő}'gb(+x;lbè- )}Xk5 O*PlFhy `[x H]P~hDC}PXs#1ؚI{=ݬ3՞RDv aV.X^ŏkF?iS.8_c*\ji+A u`\EYrA'| Na/o4N6zHʢO#2c#y~|tJ6vSLulȦ !wmղ_fM^xL'TK�dvAȭ+Vėx HS( }}cj!ݑLW8] Q>F|b3glOG6I`I&3=@6WDm)f8Nz=S7g^W�UɋXzD:5՚E<CK qR;w^MIo{1R'ش,@bÒ8}~MWi}I b@[Xn $$Ւ,mЬbjexPL�UklU88'>ulsw5 ͮT$u]rCD]VXK3YtEXث+0N+F2 |"`د)+rBVK~¶vcy" gߺ[g ڶ7lHbٜ?ͭ]x_"ԙ0d~Q3_� +v|eíl6pM&sf;$=zܒ1X>:#G>{ٚL˒ a֊PS,*DUO{%G\d([j+:QVm~1baU{GiVŇ8^]Diᕝu)fWjz*8f+ '@!^x8;%~X�0]޳_ cgHr_! I g#A0IXBT9 ' j2֘?X/ö ?,uW.sW}~zI%U\l1걿DPE(+s~Zrp߯zueTT}%=0h1QgP3Dm?} ַ6}{ύ``Fu[;m{+ud8}EUə"L۞�xe[:~mk$)}{�*Y{[!$JȤݎG`P YQ7B!N0CQQ:P(_o8RN>Iaa GɎHp#1'[7"X7"aJI4jFP$D AZ7L@ɸ(Q4 +"7Y8p>/F>=15z3Dd6c[+-H-?ԹgY=QĐ?T�F?Tm';chb}3x#ف WK|fIc"D+$ck" رݕ|'S' w8[:`UGϪg@v^�Y;XDӨ˭9o)}<hd ) [ WѪtƷ�OyZ&${|-&N0v:?b �MLc5f?NRoɾp��5eYw#߇U ăKL%b0 B!Ď<f.fG>zIT^7ɜ>N;X9 ߚ?g@4c,N[+b[?sw𺐸q9UG]" YI,l3@ !ShgB6m|mlV́mqS|Eg@,jOhh3>pV[1B#bx+ؑIq �V!3]3IF~ gb߼bNˢ$B9迴y'۱S?G{p?8t?]"_1N1Hlr1c| Y+|ĄԚl28|�)dN^¹ U _✎n#Ȁw ؅#ʼhru$2Wts.Bř\kq?kJM' 3=H6Ud2=|l⣾ ȅG(#4�^ uu+u:|+Rq[�vcHv/č= AqXb4O*@`Mv2+ "�5(]dz6Hnga:w|{B?-2w-4mR/a>Za3rS<dAbe1Y".aj"Թ%\)WuH#ǪUR{F�>Ahp?0 wF1a:GrqNPmdp0`.\=ZvѱY� ?Nq)G10ئ4Σ҂ɞC#Juf e@̈́(UGҗ8'J$ F,X"(}09%㗅ch&ڡ'LMc"0񝌄 D+J&SXIm b,~$`~LV �^ۻc9>~u6JBc~BUK!z�?zGj@hz7IkX9iO\sK>d 4q/I^n*Xr?[o*-YȄ QN$ٽm8qI5|͉@�us^B<BW -\d%#8U]!^U~ b'-t8PLer>&X`9WYy 6Z;*MՖ@ؿ9p= >D.+Z^r+'Rح;,>Px7gN2`o5Me :8`v^#V0-$M:�|A}Xh`$ Ї q �gh WF-҂VTi8a%+aYUgc;^DrI=Rdz^mv8e)Hʙ`;l9vÅIHu1@È{gc"ɵczu842SOYoˮRuUl\�j䈎)[dm OJ^/Tך;wG~k/| 'fJ<e>bQ0XKVbL=Ij ,,'DpkL/ך7X_#cl\e|7Fbc ~_DuUoDSHJ`M"^aqШU ɺ0]�W2bu gq�wu.:ȠXO\-܃;18@$1zp,}#Od/lSt -'קFcu:p1Kk7C񏍓 .hL8Sx Hk$. {e*b&S"0hڌ%0n9:{aȿ1<F"(3j#Z=]ʎDkqU<?j/^eeŐ0N@U~;dʜ.+®Y*?q^sH(YM;V!R5-:\L8Ƕ6>ař֕!Fa@su{mtm09bFKde|]ヿu|<A.@qӀ6d@~Z;lYoZDBL^H2M=S :R.D;Zii4y͈֟Ƽj' %mI�*ɼw:#ഇƧ-#8˝H1~0$LAf[L/~6\P.YV/f%(^6c06Kیr|m.|~YFAREdG>䌬K>Z^(?O?ꞅxdO%wѪO&U_;IJtf4ONCOt fEGNd08}| )pw<K2r[@` F ~Y@3�ip0aH/>G7v@u.*ýveX^~v>ZA jJby+zEV)e$:Y߱MOBg9]%h!N2J&1Uw#i(+ƗI!ٗ >X;FNzLf?ua۱vR" Xg.tB؎cKrhDV"IUAZbkP5_ieQ+rZ9dSI7$jz;G"nэy3x̤ed5׬cÆ?KG)νd5 kdl5y(%tuvr?*HnHTi^@l#TiF__ei}F;8e}1 A)~+mWfHz2ih^QW$k>C5$f_):&%@RY W24r4jvډ=M%b^b)Ir[5,Ր~ּ'q�\Xu| cTI9g0ȥD% r}v<*Ohì^ͤR.Ks�a屓㗡I7xu6#B ݲ:AŪQ:uD�ڸq7zy:#46Z^n9S?dG(ceÃd|"9U/B`x/Gs^^B$X bPDhYK }dv!P'n ҟ\Xk?|щ婷Qbڅ:_/Qt[MW֋J+q"egwc|�@a�bӍ0}np}3eNaHk 񪞺7SEF܆+gn\@ֱvU{s !޾H#Ě1W!C6.Jy=Lw-G!X#`12yv^P181(n�X ɕFY\=E?#W=aWU #d9^K|rH}?᜘WLz ^q0Cud,##IN2,j4f$A@ IQ<4%FEXM^hCWG2SՈb8[66#WuZ(J$mrQ#;5:,"iz2KOWSC8d*ųmYkj$: *`=pm*vT";;Er0{G7,-_K6A1C&׬2~ ~<{Zv,- XUBx2t:/aЉq\`}dsHu1}18Ɏu&9C&@1|yl[\.ew9 9V+Hԕ1]U^`,d=β6 *ؿHgQE NŸJ(gacǡ| +wh!Dwӑ`23Gls|B=1>tILrS8iȩa& LYwEYG33C )iK6붱u?>8A956q:%J`FN(1=w}qBC"bF>�s׬8lF*|g'nP/~)Y, lp[a`ɦGGpIIPK^%j B24zgg:F9T!ƙ.iUq̇x9'c j(]bFqnCE{,oV ;ks4VabMOjǻ?<nI!Oa r �Z+VPʐ(˳"r1S$BkQ?LY�E+6R:il?s~BՏV+Lr6Cv6U;HH9BJL1.uִF9ю<b3v>➸.ܟe|*gmo<Tv?Ņ Jp 2 K&h:2!F�ZWqh~|NNU@"x"+NnꤪIR%gpel^IHF=pZ6R)@]^{NxzB8^>H$mc?ُ1 ryp(-ֵ<0E ix�ص/ )$k5חd^6xlxRbgP q"SSs]wCثh"s1FWרE#Q&O*sZ{0ǏʋQɰjҺrd{V++.G8r^K ##wy39ǜaA?BflYMh�#7 Y[S5@ѽFRIRzd ci_U7f#` ϓ>=̺.X]jVܐ#yw=jؐHD2tX^xjISC熪rv'|kRG* D%�yDYo <3>NQ.KsJw(xNJcraWvgO$ZxOLԊ@9s&0Ay->2~ 7eM 0W(Hs4 8h.VkGAE멻Efȶ/V²Z\4`MJwL؋ a Ø;KƮ:7ѱvȅt#gNA*:$+}qFSk=['38ŷR N6F~Lrg7Dr1=;� k9n~p3`Vh lT9Q򩞔i&r݈+a ~T_K<3vlM/6qrʚ\ʂ^ե."t ţSO9~M N|QZq 5ѩQį$IsѳL�\3gK�67IwMVjǤ8l2;mθX>TX�mDh" PS[aW%[n)GgIHndbԬHmb^d*$LC+ `mmTIW�nCNObC%Q!_OޘnK(ɗّH!;W)Kx$NjBLR0κ^�Aya"aog\_  Sl�Rsh 5Dk+ZQzΟO⇝ָ>?%j[9Huuz߿Z Q![и: X#=DȐGI-j/M5�9${vc킮 A `䏑9l=-ܶCƕG)&"@B/ƒ^B2WbKo>k=c"SŠ AW(~\.9 `V=^2ZVвZ{|y62dO>zIGFISȬ6<ks'q3sĵ%iCkKY7o' /p:ɶ�p}x;BX%2.e!�Oa:Vrl3Ɉz#p9#6ض2㲜?l;A׶2v%[R:0:]"\;5Y9ryj' X3'7+U&IcHM?2\!ɦѡ`?�綧I(stԕK$?_�|$:y6 Ka޾qt AGN%T ɠleVc''zR#SD@܃bFm65͖ 18-gOE`?1o^>Iċn+ \]7冲_EV8jU\G(O]<u 9.qO&Tpve;,~Laɖ ]ۍ(V_&]wTLiȯ~q2yvSY8dFg=Y(lXh3n<C>Wb۔NFOLwGw2MV)⫂@[YFq2 ?b2q`HH 3* x墪ʮ6" .YQ)լx5:If;c 1^qΓ�V9/$u-S �yz/ȣw\q;:aݓn"sj ﰇ9_YCc0+rH|_ 6J�v_Q�/~--'j^ƈҿlv)q 3dŜen+),:z5 Fdh>_d6{ebh%`8&`s�M?Qchpa3y\R'G)צlF'ʆ,G#9F>(VARrD^c2MfeWdH;1A  K-ڪ3H6^ZMdhz%_Y\{c>B3>tjt?qF;@/5/#8,\ R\Lvhvs0ċƱd7?γDp<}JeA> Ao0O?_pUKXEͬ'cX<h΄Nb 7ƹ5ڀGZh[XdLًHNq#VK6#`ex$<7rn#VuzM{ 6dShw?t5{ m0:zWgd=[ ѲHW""3Ё\ U?V}?u^]z_bgxHEĥlKQqD).ui] ȧ'|87qio;28p~Gy w�ˌ2\l$cN^W5@$~W^l\ň,e5p\z._+,V/ء/񟎾L$y);uXO`d*'HwVmd2aE;H^;VxQQ8'R*>%NY̯dlzdDΚ›Om_8aIC� ԅ'L/K[C=C&y!_O j_Ưb9U\q=$`:/W^=`q1�t;V(S띕 zn7̈M7\`H4;|m4lBT 2-u'o`D;d6&K>7t9J-~L೬av �U2I Ԣ΋%ngל%|CO7s 9hZ-gl? �(19#6> 5zM*=n"(*qYk4j,|%zBN 4aug�XuQIl/7 T?a.Kk%=5ϺMm0{+ 躃ō %9b R!4VŹy_=EЬo)bn.ߴC.CW?c6W '+$IhG%Z@T;d2u^!J,_ GCєov?RpJ{!S+(`Gez=�=d`3ūMՖo "=Ijjª$oNV!\xl ^^$,goЌ~qXa$s线eު'G~F#סG>/pqolg'$o:2J݇HEG:婏ZE&^B- ۯ@CG&;:b� ;�"=1I7x=)]%NVpN~[ym Jm+`E?/1L+Zf*h5h*M꒤g4~L,j;cF99qkdyߌr*3 Y㲜\&)EN$i mf({aP`K/S޼|P;vTG22 HQˇ(\mgD?unUxfwȮV#aE:LV8O^.YZ]%–!u$ /={A j19]$I˫�\>Ugy'1Pdcƚ\26윔@e´;YyF1F$pa0Fu �!yzܘo#d6?7| V},Vì׌L a 9;jElk{2Gh�iT| } zq;'EE=ig*>rP<-' ,}ߍ#HGag3I !dnKBʒƪ}ǰ$O?n?mRR4h0� Bc�3vg*؈ NbA1dX0o ]$z/VXkD"(b\{*52ƙ0hLY7VwY\n$K.I`9oˡȧW9Kf8Y6 =6[5y8_+�NWK Oǰw:lGv*Olc`ݲK.l;/[xFÉh^?|”GԔb98.~"xr=,ݴzMY`k)Dr#9.M_L'h8jnrxf*>"rMRIx 8飐%h"'9Z'v*Ʊ<|^aYq6&֦ޡNذ +@LB#hyVlkʻ ڣp3yKԺ<{'v;S+/أ[rz[Am[^޵DKB5`gRք@vLILxjv}]m^s Pz`yTwn$$˯! &g  QYW2�1⟂~=Ic|&+{`:X?ԎVrO4ןy-ul:lI#zBss耿>1rW+Dr3vxἦ"-/U%S|zԙlND*ĽGڬa~1qFq>+fJ ۟[o-5'_!wR~--g%V'"7\؆U^b.;"33]MV"$-E)_dk~5\6b)mSD̆+Bb6rMZ E<yGF^z,=~YĠ2rYnZ>J�,1# +Z8<62ٶE8\o-yKM w,XY {e#~eKn潵܂hcL!'epb)qcu?+tÒ>e ɬ$q\#ky<+9-ًV*[CS|B# ,lqܧX�9#IΙ|s3x\]FCXir0+9[<^I9ST"\|iĭAVͮ+H%&e#ƷrTcΪ&'z&܂X� ?_$|^>f"*lmqU`|#v@;Զ=̰[c-nRh ֡b=E#VW �Iږ9r \͌;, %vOg)QnUH61-԰1'=˱felYYςD܍Dp4\f:z'gpZZ7خW'jLOJ1>]硋%g9Wy *\1 FF9ZO^R=8s+oX99o<s.79튟=>I$�XS/Z3(۱׌EIV.cqS& V8#MTlBf\aΏ1ܨ�"yUO\hr=_[pY37i}㛊8j\?8^ԠO w- ׌I:(ij9|g5fnJ2t9H_t5I3L#rC|YĒ4k6%; y-ے^MFcʴ}q ztI Zhʮqvqù 8D]جJ42G =_*RS}Ĩo!}sEIJ.ƣsc"U@i`>Z!I>IǮH2>_O-|~9?0WNR&y<,]Za$Y!z/*,zRA?j@|<_#=n/腟ǡ+kÞ_IX.pad(ÄB>̒@!G#[\ūF6[W4Xal8^ŕ0[(b'R9-'rk\j eC@V=S5 2ٚzH[Oo.oÕPKS9S�)Uo>cI<W4!I;.[Md>ױ<"ŦAms">ZfRבγj,ލr#ʮzlzG\b :Zս'fQ1bK)Q˳%NȾ卻vZM b|a_Dc}l? �꭪ z`c"Lp�: -#Fs$:|[  yM4|S)I;ﴛ &:oT^CҩpfMἑuNJ/M7)K^Gſ.RzrG;%Z'vo eR_Ō/Ŭx܉O$, a[Y<2Iqq~=~9|hQ 5,huK5 elLUmg xrgR+aYQ yEg q& Cuk 9>fxVI8nDtc<w7܀;T^mAM,q-(B;VThG ./G8o}.sI?Sk㜗9漃jn85gD5TDf+G<]nDIJKaZhҌ զEʭ#6/`�A_x^7 ojUy|o\0=϶m[t{bŬ!#J jS 'ySOo"|SH)'I(fXxK XcHPUrW:q1n02zdW^>G!kP eA"Rk~+b yX0$u}d�9 `+Yzr&T[*g\n!)檧?E7\6<ye|I΋dh&`gLXa9:SqPҵ<4p}<F^3|Kq50_Ԏj_w (S[k׹x-QQ))3KV9zukJpaD\64M8("P]OnNWb<Xy=.,!WbwG'N/<_xI)gb{n;xv$N,$;p؆KH 3l1|efJ KE.9) :ccP2\}y+qs"QN3ΡgD.O>4r`趞:ѴAqlexET\ ?,𵸚d+J{߽AѓhVܡ4"CmS"kHUT]rEZ3BHlSձI5} ݩi#$PPUSqȜv?'#sq=cOÂ**3J#yg5BφYKTjI؊1zh1k 1q$P`S9bG N2>1gGnTXEog�J` X:jRrug�q f7]oY�[j%{_<0}ҳI /l,[eoH? !~ 14aE>Eǿ( YS,I3Mc|wDGE71 b2SN*1Kr>bK,\)F58|/=Ry%|v<H<+69?y[,)LeZd F7D;c�U'w3H~f?Ħ25YH7}d[:|B䑭X֥ /^w<{p_TFcWA\ /xǎexڕ*xbU%s 67?<H L86rE' $.5䫄oeWf5QR8Ugq/-lyK6"RWν3dʞ;jH^3<ۂ d$I~զEjdkYwe'AjjNv<vl3z1 ߨ.P83}©<F ]xZ9Rq\߱qyY| 4e9Uw+SXXF]a!+G$XB0s|bۆ!%)qZNGŘ56jqhyWxy EnBŵI$,:ܕ'OĬa&a~dOW# W|2G[,̝|:gM@1vX9(Wjܑ*?8k"dt+,ZRMDf[㑆Ju08 pqw'$ Nh|;GոsCr#)z�ACqΊ�!60�%c]H}N]�Exң�B[׎я~Nēy}rXsb1r9k'k0eVwrֳf , y?b5I8Y^6VVZ�U `BF^gυȾpl]*_k Ģ ?˃nyI&95cוӃ<2ZsJ/liOϣy n< #m9,㸺W/;Z,gTV Yi$"Jvcӏ?+Y8Jsx$~-S5�7G^/_?M/qO[tapr"B bB(l9ܜ.ve52bȣdkƸǑ_/)8j^J7 @ !y)lxą8oL�?Pfx-{C\䢟@Pm&pڐw埏j~|r*P?�38<䤒EJOs\ڟ<}Jg'NOep09nzX 1ư?$&_ &zc,E${ed~͛)V[JXI�jL*r i\2]HeUV\f3kp~C&E,d ?j%>VQR@s3HG4fN%o.u\s\,r5d<r/oZ^.L&g+9 o~GS<zDO&/q^r}�иL :0an+>5Z9~F/{Ry)G[zK8Ս~.)\{V͊>V�ƹs?O ~C]ŔJWJSdTYZ,a4d<߆~ʼ YOS  J#ɓ�Zw(Fz~_G圕Kqx&qMJ� `jqӘ�Ryjԣ^:?Wɸ-q\FF}[V+^&'Kx6=E(ÞY_Tr.s9w'eTY+BZr^??"ݡj#jW&gʒXeF̍٨I#mB*&Է>3흻y⹙iڂ@ߑV71%':+hVUV,z?W]cj:sw?pVrXgT^+9V _n7ri{%c{9qTܼx8.-\.RE,xVNOƹ|O5ԎvO,y9Z#stzj_wrRqf$o�s?_w=nCW?EG gxEO'�2� ¸A4~p9i$ ]+sڣ&F UiV 0Vdf鯊[6@. N">z^lҟK�W ׽=gJ6|.5Bce2gl8n;j5{y7�6cYWGw5_Z‫~\!NSCXHk,Nyȑv9lrZh=Q.DIr~촟Zr665ƺELs_-�$3}(%ã|iKBt9KnԗXEb7>22\v?U\$ay ~ˋte k&`_$A^s<|GqKy)%堫I|dw-s("c pdjEyV'p[KLu<]hx (O"hDmPy~C/DdY)ڃkY*6HhzjвCbV$ix>5%{I-sqrH/{$J+FPőd 2^Qo9"4|nNXMP[6$Z|P-x&o#E%8NWb V[75b42og'-_ǫvI�٣uL3^52o"6aiwr)ұy嘕:ߴK]DZi~*DȣyUrKBZLKTt>oo;o<㹂f/AC\pUvWq"2\;A W+?�e,w/3� \\W$Wɻ ٛY4rV>->W'=xJ*LKK"?'Ay[ |;mlS 4\1gp+XY A�?NM`8vn$u5N4x}_<5~2S5/9H&i8#? bq^R7J=ivcm𵮚<'$Qrnr_'N-Z'{ ^ҋĸߘ,œV<w=~EYyMqry\}8AyȒ9NFױ^\ⰤtdC{ړ''G>/r|%?m=0ιLt\>{<FCuЎ( 8M#Ofa#<˕ovG`4VYSc\peBȯ+ERcc^mX/^a`o>IX8{I +?W!*ґy#O\_o\sxo<rP6,Y`N6X#yg y^#bqGcR>]!j>qPL'Ap<!O'TQ9,[48|TO4{Y뜞m@>bb/3c \' G' s~u140u* A-TkOơ8fib9 7,ʕ^k_{7{<?{r�,xMYS I,-׃[oqQKq )KdJs@er:Ggke#W�$-˒Yo\yo ҟ7-&4U# Y+3nm]_- G+^j9nW)n. sM/H9hyIsբoKqH7,iz:2$ UGOZj|W?6}Cl.A5y j=;2(w*?qs>QRu!?6טq/jq^<O<Yx&RBomE&X#f~'/V*e^)[:i4wQ:e^6#S~?//,V=aM64P⹚$7 o%ъul`1fŜK0&cՠ$bq)qYE9xx|,^exFx k6SnyF|S\RvXdƭNq^Te|#CVd\g%'%}hZ|'74EB^5v0̊J$js)=O Z;ҳK^>'E9YX>tݰk !QkXy?  hY 6&7S14[p3&g_a9= Y785saԙNԴޭՕlJYL靆@D�NQJeƊx~u;s&]9y>,ZiwzmE;�G,fG=~W ycWځ7ی8)iyNjyVm*rIй<䔮{l~7Rhc|*<㓝I|K[׬2 E v>-W&wf}(˼jp~cszñ^*9{rVŔH?# c蚇)nRY#W+u bfjHu =E9:Sc+�|$� VlQ nG,<I2!VvE'7v,sQ:HV+ɭ<Q䤲 ɓO{^A#|y ;m˼>- I^ X.PUX;g)"Idod̋/x^ !p]}Y$()֏Fm�\\l�UVn+H~NV1lPpmO<tƴ1g1aW�"hTg[wȪ7z>[rVg¸7=`wEa^H%Uz~t/Oճ\3x?RsgcyY>=.qvLj=P6.Y${3<9;M<ߑU?7J.OO+MQz|zRqor1ԶqT߱`?#K@=sז27c~G~fI=xCy|`FâfRNNSᒴFC6�F܊9yNNpL+]<KܜeNfX!{-`%33l|PC4ި>z-xb�G1!FWǩrr~+r$v qxf=�lBlzrX̊xX5mRU} 7 `)drJY}z`W& ե}y #=aDcc}t G\/. GTlv:#fZ>"GY*Zլd*=igh -x (n{JF$[<J $R刻cVT}o;[eXyOn.>TXo"yN{켙Hyhymd<`f)PMNЊ շG]�EF~EA+߸\j_+~ $*s^Ja-nRE `r^UsI U"<sRTɎ;Yy|Bb'd;"3k�/:yK$3r0@|όݓ,iVAp0`<ʼcU=-`G 26vwwtU_ if#093| 49vTv6x'pFA-/.o,żqUYI2f>F<hKQ%֫^rv7H2EЕ۲zinƑ7!* ZYkCQ^E\&u2V+&׿'!!Od/V.U[+Dp "XAߨV%Y2NY-ѷx6+E9fS.srv<qU-TDlCTT"\1FL8N'jg8n񷳉J{Vh0ֆxZ,׹ea?a9vlڑ`乾6ԼאɋlrV(a<Cv܌f8d<0+sUua 9ҌŔ́k\fdnc]sek3-;, 9ft4HUbO`85Ԃ#s׬�CyI IzusĹp|I&l*p<RTI6yIG_;ev$>3!r'+I4R#s4<+\4;rk |Id8�<-x:[IvI/C)WX{0rzk+M"iI,r }xvxJW<0[9xw*r@{q'>wx^C%+ON2cQwDss~?xlݮh+i%?_&դ:՗nX C%+r@[Uvx䞹KXM}%SdUbb ZK) xt:~ xU_áR2y]V]2U**In7▤ߨ}4syW7(i,5k˦2'Oȕ8( jG 1c�:v>᮸Ԣ`]SanRHtYQz꭛Lmt>Z>XG(l�f~irerŞNx_y*<=g�|\)8.l3$pv6�{P~j*ׅt#BPdHhd2¶*d?d}sÑFSITma{<܊>^<T䦮hʜc2+[ȦrUyK$HBTL{>[Aa�x_v#uhdE)ܓfUWWXb{2\iG4ߕZK@ѳ^$Vxi= _ݑrlȣYB6]pI4+Q<?*^R\${$ђfUi%^fL6g͝.ێ̬# i*U9 BXb1uk2�ֳׁu =d cHgM36\3�C*$6}7:dG<-L;6=j գrPJOyF?iR W|~B |DLSL}bVOmjq>Hf̫ S^Ha%eFuYwHGa*$rZcMM$M[Y(jE"vEF~tMܬ?"8BWnqj)$6�_$WHSJ^#Z*ic'/Vj/85tP{VԐ݀۟"Wwڤ1nLة=w4ȣZYX|1*x>?ֵn Škˆf"^+7|OV9O9PHSoma ;9&z\FaZCG,hDMBP;#;'lѵNzsM^ª3v X6uհ}I=+.@8(쬰O:� I' XIYc10\vo_KɭSȬf (T\�1�&'F!@8#.#HOř#IQd4|#t>GTbH1-LH{i5vgpݙRF^.Uf1n!o"8c;)Ȉyj'/�5aprSO,_?L9+2fKb,۝ߒoKO,Sn%yI$d.6lr2}j\>nPu%CЍ-tH:Ga,qja/=0th;aM\jVVpt.tw:30|^9jLMN5zԤj"1,QHUd i:�izu#:.XIy{2_0�\d+]ʬջODoNtүc�y+s\)l%p} y*EtITV0ȓ;z̋g\B�\N!d:f}aWb]d]!%Z5lA+Z&xb~F7|:6}5MU߾B[:nJ?L@bUɘ YY&K5gf҈f0g С_R,f#}4x^H٪։:+-~ 4Sݰ sXDrHΙ,TuUpDyN2m`mW>5s~pœPC{M�UvSUZ*ų@Y}e%e>_D6b}G|W+=[yd1;fς '7!|cv׭l%|_\:1}xӉ=ҕ̫v3oI=O++Av)~/98eD&$޵Wth-HaHјF;UFN;Gf \ct�E^UZǨE u+ 0bg$Ii^{~ֽ'k=,˕iPde+O3ؖͤϻLl,9BUPkeWU&$v>DaY'ZHo7[έ$r&h ZFTYLd˨剟582DwO~0l ض\ǟR?t2Y%m2b@h!ǪT2S_J!Gk$Be2hv*6S�hc:]c4V_T/S򸧶h..{4UdVsC>56$,T�-[W^ N6ȵTyF%%jnN^Y�a�MǛ I�݌7K$̽cIwPx3I8E=,$TJ jF4i/Y"giT"iI)nEtjKzƵ`Yj(4>xHpgz8K֯^&- @ZKF c[QVgŻif?O3JU+c<(tL#H*qDb_ٛT;�"k% V]UH?8^@YZm+ ad~O Iqle'RYI\I+5T#` <z䙕2EDWYaelk]>0v'd(pu6#C_ιjE*Z\U5g̛:/'W 89N6\-geA\?.IP7=uCCh\i/Ⱦ}@~|gyHUY"v2I%vlUڈu枿ر"Ǜ="IVzVZV7&\Y?4,vy,X/bZ=]N;Vcү44a&7"azNMފj$Ċ[O`?)$BhWf<*|^KU�mڎ +R#&l DOdiufM��3>˹!wa f%P4hMx@њ B, b2( `0u9,={+9nj1h` X=0<Gb~wܶFst0 bމ~WKm63(TH/px'Qퟤex۰�9 &,\a<EuJUe;oX~%TsAu%Y] 댋.+S$y2VU)ı2, 2#ۗO,,s5E5eG5o?I0O#޳sfy8݃,V,L,RHeY<zTJè-\5EK蕋ѷ! FGhZԈROD3A4!Og$5usp)�kũ+)L#5 ٲ"XX4kFbdh[KBT/מHX"'W+$VE _yemF[νEЋaVYFk =6!h b9Hч^%()4Ŋ(.3s lԐʡB' e~ fz +][U($W;^_[qDZpijZ!L2I '%ӿRd9+"䏥rW>3_}m'&Y[hb$ҲAiެhGF̒iؖ9i;iYè"һ,l0*W?-|x&VHN$zIHu�,Fѻ {2 #')͔jR"rS%2jaE5^#"L7*^M>6F7gZZjL 4*Sd+3&K$k2UzzqQ*@* %g  lT<AfGgxg12?hG.FHDŽd| Tw특3}k[P ۷�mهر#6IGz~# V|oџSSH ?*0dC: ud hz`3."6R<~ɥh;DBt'pB ^fk{fZXIo`x瑭 RM0lLOf"Gn2;%/M5B~I ex@3,ZŠexՄgWAi9X;,Ir {uc�"9ed,/-9ֆ9šoݧ=MV+$dry%ٌZHVDd`D045 YzLhD7WZ֐6 B{c'�<\@%Iʠf0"G!+FH;FtU3|Wۄ۲LՉD@ v)ubseCԖ~1"-N(p0'U޵>J<#.֖LibY#츧YiId'D+{[샷G$6$k5Ċ?}V$"k6܌K#X(M U"f>;nK%a[Xx>EjG:O*RK2DJ{)QdZtm,D-H K"8d)M9e%vD8VCf;#+ y.7lF"ھ|2֓9)$M5#HFARIVJ#>[1М4v;xol^ʳVId6ce}Dމ%ݱףv>ؒw7h˨=,,VYe:؃>4XX: ǫcG½lxWU|~2IR!şfOېD =#2[JėFA"_؏#,[v4puF?fgśWa 3K;bE #LXBD$fUfЫC4ity sXy([1]9B?ۋ~<v mY!1A3XJ<9&Od(Ŗ 3Ԓ#9dw^K/AXJE'-n_8i-Z1M,ߋ^?)/ܒo co+6yI$ToN)"_nܓB?xvck'$EVV2k }\iuq}ֵ@ UUgzmNUVX�tnD^Z !D&0r}[Koмg6$T~$y`7e9b;hg�.������ !01@AQ"2#3BPaq�? kZ45Pǘ//Hk/P}xB$Px_Lhk G['E^XB~XL hhHˬ2EhƊ&byLJ^hhkv.<#<QEoeb,3ez!uD(QDha11c(dV,^(Z w45J(E^?xoYbv;,YczE"EeQBXcT%H ^PNjZ^hh{I bXzQEmEmeޏ PL6YlHR; Bv;,[(DCVQX_eZ$!,~dEFGZ1byEj:֏o9e>#v;b/ZEZ-PvXx!lc((q%B/ e<P,_OE=k^,,,:4*BĥC96^hk! Xyℇhja j/ 7o ?eR(WeY) 'R$N)/((HP' %_{QCC1b�*ފ+eYeQPa6<XP$My,1ެhB͈X(hy+[ejϒZ?eE^Y e#CX^D! O8xHhx^Ŋ+ 48w^e(=Q(eg2Lllme dc/$IPDŏCǍ$I z,LXhh~ hebxcLZUXɍ(6<#HHF2!ĭ:4VNJ(wE yP(+D&Yrhu:Cnj^e PEBYtwB; 1Hc͖&&^x+Zhh1=(𘘞EJ+69PE38bH?C҄+Ra̶ܲv-,™r;P41_>¼!kehN-h) 셵iY(!!cLF1DHY^NIXU,l/E1/ؽm QbeCCCCXLL,cQ^+4PX C:NVZ< ce! i E,<uJlMxև!aehk) EtYS,ϱENEVZ~xlRll! tbbb:J(8QEVXkj+j:-hqbeG&tVRX) v, ((k?< wNTDŽ! K/ґCֱE(! # t?>+Lpi:J(1Z,Q~+X/aqJbz<~^b9 Ye&+h+Z^[9d=P,l?~Nl#ƒ:xHF$"| {xY~XE8#X12ޑc67dXŇ9$1D,YeH@\dxH$VX"GS$~4J$K(P8z(eYzQEoeޫ5!1jzd^_ϡ!Eg4ux ,K448\6rp(%8#+eeB((hhkLLL+еBxY!m9XިBŖ_HHN>;d8Q$V,BZxEu:E98qI@q+ ұb1&&!e448Q'BeZ^?z,nOT,zVJBqehHh(Hq(q:N~DF(%i[,q,8,Yz45ފ+d-/E7VE+g h(;"!4u:89<TIPGke,=,%&!aZ(hq͖&YbvYxhhkjhxEӕ겶EeyB"@|t%b"CP,Z/Z%G,NHEQE$8?Zk 44u(ؙbbxe (BlUVE"YQE$$$P8J+(L дrEQCUGֵ֭ 112TQq:f(uxrd-^/FDZ$QBBu(P( J'C8PEaqJ(B{DXf(q+)&]*,^OY<~ߥj^W؈$$QBEQZ4QEQE vOF,z48[cELLOG꣨4V<'e/EM9XBXXǢ"pDB(HK_ִQEbEVd(CX=^CBbbeᕳPEib{udɱ%yB"DH {?ZN6rB c4W,J8Be)袎SEeiɲO +BLJ#y BBBBBE"4$QE yg,|DCCz44<e'VIa12QEQG_S3T!Z#W"!!!!! 5 c/VsB!F1^ /B$3L//+ؿ9BՏdp+d"EBBBZ!i鬬1x4Hyc<E=hdO1d_,^&=!e=:""(HHV+1G*&1c͖X(yH=,hk)HOVx,^G{Ւdߑ+z"$-ze19 cLNExcCYO [Z!^Wc'LxZ! v1DG8""Xcl419!f441'(YxPSBXyVkV1Ȉxc8?$D-X+𨡡$L1,cǔ&'(<Yghhhk)2iZ;2G#Ȉ-Ǫ!pDXy{<^V(!h&CcǭXhhhk <seb�99숈BٱTq/'$D!jH$8XCCZ&&^,B(,~Q^!2?!a lly#8,QEj^ (Pc'|g,)J(hhheGP<Yb<NbeĔtG_ _'r}T! K/ c8qDPEQXhk GSYNJ'pcDJ#EȽhLHkHd%b ڿDǢEc G 6@ȑEV(HHhhc/B↉"q!CxB,l#Cbbhhhq+^$5q8{2,/GDb$D8(,QH2LlBP$rȼ$1"rHPBdX+J(hh$5?$^4VWǻ薨KkэXD$N"E il69! a𐐐$P#$Mc~pccl/$Nr|(◒+LCO-CCY YLMbR3z!b!@VVYydb+B!Hhhu(^eV"G'Mdcz#\ (|>&8J#"2È45$2/1?¿C961(gG| Ye!3&"(! #Ef,Y#9&Ia菈rB5'=|‡XEf45,Zַ<&Ez�GשC O>RgS?h8^^QEkh)#ՑZ!bG ,xc9r2Llb6^1VQ8)#|O' $XX~z!ab䡡Q.4!^HV)Y-hhq:QZg/Y6Hx_Bc|1↉ϕhi a"'45'Xf< (P<<&Ey 08`EV|kGSu+[eNVc|DŽ!_'ϗ?hFBƏ<48",O/l蜬1r~e/2b.6","@q!#(HHQCі&,^XXs?v2p"xd5h_2:LcC,}QXcEDŽ!ng$^Pㄹ%H<Jأ|h",bg8BhYe,Ybb1匑ǔ?W#!1eh! LC"Nj/vP!z/ 5!l{"<q$P^!Ӟ,",㟓"q?$pE[:<&,22C$Is}$1Cs8N /c$||lB oE!?O,xBeߥ;#N "ȈGbXDdg|w񇭖YbN R򣽋v<G#9Hc<,1 "DDYc&|��U"ȍYǔ4V)wޭ~ :w8D߃~X}^O^ ?4PLg!BXx!a;)9eeb"-<c&r39I c?C_Bed>lDY8c͖&'by(q(B,5!~9El_L�L9|� e"(E>//hge! cgc:} FHea#~IC,1}EGBd_B"D%ŖXŊJ! -en{NjϽ,L8e?*UCi@Ր lZS&6X(Q:ECEXc2Lgɝ$1! x�,,Xdx`rr˖vȑ"D,2<YbbVVh"ǔ-948#ED",(DŽA>?'dBG HxDH'AAhhk1L~풏Hhc(Xc�BŖ697qD?&"v"tF^!^ǔD9hk[5,/C7EuE xD~ώ 'g1'l <c&E1g+M9681,cb3ߑƄYe>Xy>o?>WlȎY"͌uƊ cbY9/(HkB^ /D@tFg%3}eLjF1lS;ě,/" Bxcccccd68$rGceJCL'h_K�g?_GG; D$G,,L^F1(>౏gԪ![Yb^"Edrx#-^q* SC>Y#D1|}!Hȋ,c!dɳxcC),?gf"D$^$,":<z"D/WgzYx^qCCPEb8W""2>,=[gj;G2?RġdBY  l&O#džHYe <$$qLlllxBe"YaacCCH~G",,PԌtNQBE lB; ̻rFBe"D8&O(Bdd)!lH"S%+$1cd 1hHD#@F-1 !f(|h-؆I 1+.HXY+#Du(ceBO Dy\r~Z#",llllr,HQHh"Q:e GarH"Llo llcCGSP:yD:%b8c,NmxXXhqF2g#K44$! )NaBX,GH!!$q3|>E%R,r,DP1;Crr|9rƇEb(B"E,Ydl4I hyO$$%41 k $2l",8'<yر ,6v,ll#v.T__DDw#wr3ycCCCE<^,o yLEŝ_/Qc9>Nj,B >Dw#"/ c X2;r2Lb#*>7̗8>TybYcce@Y!v̗ !$rėΓ'!,LS%"LHLO44411&,Ybe͟xcQdb[e7cXԔkXdr XB,䟃dd&XݍLL.Z*$qX8>DɄL"3#12!sÙ.R\%̢|Kȗ4ݖZ, aLlH/,IЦXdDfFBz"(H*Yy֔QC(z2l9&v!lBB,r&J YccdR;.Hg$\bڈ>ew#z?HˉG�!3">D>X(&#L*9\ O>Ie$HeЙ&,13Xb,1x(HK c1ch2Q$z2FI'2Rp/*F,bgcvĉbgNjLА(DbGȔN/GnD$yc?^a�-?y'�do3r�O) ,$Hcjr"BCԊPBb$VO'x-iXZ=%Oʇ^z^,! Dd`ņD98xhiM1Hi aX+eYecd&<$DC2e oEy+(qY#ץFk(d8Z^h}xdHQBB Ȳ#$Цw,cCGA5DQ(B"1℄d%䢊+QEu(u:QE$xEXXE) EhhGK&rօ ,6YeXTG y122Q(Є;f qIa "%PlCEQENZ:V(H(\} y̒+fH$8!(lL[,R/-f,<&) GqHO8/Yhx4Cu+,lƎSQEQXQE+ g ؤFVYbx+/29>ƅ GDXx,<PP^(!12Ȳ,O $5t^^ɑbxLO z1j(h+CCCǁE,11re#džI~G 667ByԢ(͉!EfDD&ņVQKbeCx=kE~ /eqcM y?BK,L hhc'Kk+X6^QEh)a(q(pv.6C(QE4QXee^߼iemeB֊%ŢQEeM,3輲CC9C,Ok,2,l~,ǁ"(kDYwEP4uW,ǝY{^l^,l!!zhq:q:P}ȋ<FIXg'Yw,obb Vz1ĥB~+Q^=/6^,E 6QGSXLX{&Ebxy1CC9QBBd)_/aa+dEGbXc&!!nǺoVc*'(B1HR OHrO81$EY(S,hgVE2"ُ UYY( 4/ K͖}ⅷ,2DoɎ#/b&! W<Ye^QJ=VQXQEQEf)QEbʼn^+k,e{S,L$Hx[8D, ,^,12eNJ((/CY5VX^y^Մ"c$s 7kYe^Yhv;Ŗ^,EsEzh޶E j,! W,}o곰ec>G"/[/҅tYgcYeY{XeYe^o>=7^�EэPX[~bzLR.|,o ҽYe_,-YeV𨢲EQEe}hx뭬^>@y^{VoK/[]b�'؅z_ֵ[Є/(CiY[66XẊb�xyC!Ve&6r iZ/m{/Z(C%뢿z  /dK|_khzU{/୬~'!{,QC ,֊ҽfţ޷yYU�)������ !01@A2"#PQ�?/6XJc?$Yc12ɒC?,OcCA,Ǘ^o+EWBeyl,llxllZ6Yga|wVN!?UE45CCCCXnNJ{yb+д/F=1:PXSCcYٖXqJebxh Ф^ZٌyZ!i^,OUŊ(:NQ4Qt:Q!#|seYe""~ ͉(=ވXBh444122"Ŗc~ŚZ^_zƊQGStG8v;^oO(H,Hc腋,R塡'D$'CZE-~-ceڊ((t:QEY~(Q$;, bwcz,O)cCCCXNLO- f(//+z-^SeYxUkZE ,DcdxԬK,"Ȳ9Z,L#"2av'(ޭ~YyڊVll՝E"%ؙa췲!*#+/vP$5%匱b�A z//eZXRED/%e&E8XC[zqNCє1'D&'H ,P8_^keXMV^hQ"8cYr"$1{1,!!<QCCE XО%,O-C['Va=X W<!!!DQ4+#!HlK/dEb{4P%ȌbbyJ+[.KGhZǪ //,xQ͖",yHhk(LRcccc{Z$Y CCCD5ʈBbeC[^Jbbxk[ՏD/ue&Ay zXv,lllcyu:&Ev4PJ#X[&^XXRkXchE |,LCyHPl\"㣩GSNa #?�1DQEafz͖}OF/e,Bb~4lllc,RD8$tfTQEb(Պ+ D/KXz,59hVlLL1,.�E2,rEFBs"eH0H=/E^e^QEen7b&) ↆ^/愄gcز,H1HH1쵬Zؽ4QEV+E^]bbbyhjʢ(eYeY{<^N(vd#HXcVYcccv,O6Y&&'QBB%EzVYڅ(Q(,Ye/?!acQEm~r%!ȼ!YeGaHR#yQEly,2E Q^תE % C[/[�Dq+1E66Ys/Yv,o &YR=袊1K,LO4Q[H~F1Z>ǏVHcbccd9ʼnxv,,HR#ekzիư͖&&"(+[,cΕpGc<QEce6Jdceeev,w;Y،",֏+bbxhxzSQEV= q-Xǚ[6666Ol,Yls;qHR,R#!<ؙe좊手 c) Bx(+ZqFK+GeoyX669Q>Abz?ceYR&)l2VlPO hcXzQEVlLM4441GȿZ1R'2+-k!HaXeeƊ13Սbؽ(hhheXycC^hcHHLl'1j,llx1HHDbbbbeVwV)ѡeEjƆ֎8 F1[/EH1rŖ^YcȲ!!<I e11f,oF=Ybbe憆YCCފ+5Տ xމ ,d9'+z_r"ņYǚ+LLYeE憆1oBDQBy,D67ow/f=BHƊt&/9k711<44Q^:Ye)+ I8!eee^$MYc,OO9=BDXƲ٢4QTV Poe{,�EnǪx+OXLJ$WǢBG"Ŋ2,CCEj+J(kD'>6,_WV5-X1!hBM ,c^Xe"Ab,B ~ kb45N[1{!Bט/XK1"2 xDY!b6Z/d!aV{ghB"ǗBќĵ~("EeYE445'}t!^H/ьcz! jIW+ {!a"@%,LXkK/W,'IX�I~!^H c Zdߒ{=_V(DCxDD! P(Yy11<44J;z�]B$-Y!z fO2c<WJ+[$D!b/DB $beE1e11<44P%(O[V pْBLcVQE aȲ BĐ!BhhIDhx3ؼQEh'4J#X^ǔ�iӅxdBd=jEPBd'DedX|!111h(%CŖvHO4QX2/-4N8Bƭ^^aN%�j:[!Xdɏ44444<,ؤCrYg$""XLL((D#11K44J$a?GfE뢰/2 V1 x!!!adZ+c/Șz4445239%xB#0:,df) 塢Q%pq,R"XLL b~?,h1^(!! FH"Ca66YՍ QEPnD#0J"ĈFbyIB)1LL"Eя �5?Zx!!*+,d$=Hb"!!!,4QGR(kAQCD.1@PqXX$N$41fؤ'"$M!{?FZ]V1҄$3dzu袲$1}"! aeQE(LƎ8 4HE$rΈ~C.h%^$NC$N#U�EYcBD(džH,L^,?N4E ^HHrG?%ȳ\lI†^hؙxBby$Yz|?cBy Ic{QEa c!"2/ c!v/D#y8B"Xd!b}`;y# -(h,4! F͖! H</ec_R(eG?\O7X66I! ōX!q"PX�cVS8H̄8&rqB"$8%!{/G[9~$,3G4K1,1LlLYgo'sݏD,!cW"!i�?Pb2qr'4rBŠ"Ȕ80$!Z=pDB=m!$I,Llrev/,x$<@ZYeXޫ ~$H HB�ȿ"^ FL@BbkDdX,485$41VqC!XXc$$>gd9/T2H$H{VYe!aH(Z$~"! !W1~4#^'%"p'&&&' ~!n["* BZ199BtyEa2,c$D"C/D'IE XsCZe G|1 ,?#? :�8 "I5CCYBc^8!ladl?'C:""XHMF?K$$PE4<Ѕ"(b#rd(?̼d'D$EIq 2/,ea O 8!HZQCD?� (J&ye4u(h$u:#XzвG,B"~DJG�9~C8hq'dC.2<<kv<?ץg6*B">OLiD(yQcDD2DԎDkE8J<,441"(DHlX1axĪ')1(DXDr@&)"ac#>DYBE d#"LFeC$k cEEQYX, BBZ$$EDByc33Z"Bh ,5 hm1HǢʼnDž[G! 0$N#U,LLd/C9dCCJ22cDŽ$VhH!1,1/sE$$~7˒D $L$4P^qDkߡ~#^eYeDZYdXIY8 c!Yc,xW,1 E! ∈k̉ (Hyhd "DXdRhxX|,<8ȏkeŖ61B"hLJcJ:$PXB&DB/P&!(NC<j!K$PЈ?+$4QZYkk/g,G,,Ŗ/ c!aVJ$IEQGSHYD ,,LBE $b(RG/;K&� f�?Ua"qHHHJbLEPok,LXtbGCʼn1zQLHk,48c֖Y{P(XhF "!"($R:?'H2đ5!!!""e9dMECCʼne'iCZYgbLJ"4r!k/'=$(!!a"DҰ1,dB(hhBcg$c,LO&&&^x^G )Hc1JGs,,9!!"G$<^FT%v,R(! (H"beXhcc,EPP%7䕍 /xdbbzC8ճx$1&JTJCv,DQP!^"Q!-DŽ,DNR1( ,L,P$V&dzCEabbbz</_Vȯ͍F1l)XbҊ)$I"g/ (Y+w+DQEQQHP(#ԡ 1dX,[P$43cQCCYcCY옘yx^#Є<[1CCP!,bD$86O(E,= "Eu(((8Ep+K"ȱ2yzYcDI &&" h~ xO҈gEx>JKl\,|TJ$Í:4@(VO(QGSQB?#Ԣ:uIFY!XB44PYB19%D݌cʼn=hSb#[8!VtHj12(y*"Q%Q: 'Q,Eh#;,4Oq|gSCCEQENS.1q�::fD(IX1gv:BE#Xe4$$! $Yv2xBbz~#bDbpHccB!$PGu("�(5G(?ddgbuC?G:3 D5eD4IV"Ȳ8O fC yB$ȜCCEaXctB~<EpLR;DŽ蜋,ee+GQq>bpGdȒ9 2Mi \CqgAhX /ǀbOGCD+F<"$~&<PЙ t /6&vǢe"S%JCxBCCCEeeӅTHLC9yL=(b":ddIG1IqaLLLLb$1CXDH ,",ƎeYxv;.BS%! I fHbCULLO C�$N/g#CPhQYDEĄX"d45ʎR2/3CXBe1LR,l,4QEi,"HEcx@v,ev;hs;Ga9!򅆉Dh{I 5LLqȎ2s(xH/Ia!"QG3Q X3&IġlxLФ;hHYe#w;9#ز2%٢k2vXP}8_HzVǢDQCGR(:aP$8 A#6FbؼViVW1$,PI hhbm lS;(LwYv,v,,oj+vIe / LOd"N"("()beJ$"K*Df&!iBBlph,JJư%&,beYeYeŖYe^/J("3.<!4MF+Ԅ _󕵍) 42Ŗv;Ye^hhh%Ĭ" X6XIQ8eX! 'ReYe-^3E(I gl֒VOPW!Gk(o1(H[,,jƇp?HBd$Ggce,,E $QXxES(tq.#NCczbeX4<Vh~*p" y+CCE$$/kZYbb~ /,hq:qeB;EzZP: ŗ袎R:QE:"P: %bD2ZإB;Œ+ WLL"~<|෣q(PՕ^ XBlZb+K,,4QENStNV$QԦQYyZ}} I ~2QCCX_͖Yeu+#y$G! 7/D.,)v4u:J(c򗺴18z^<yYbbeEie3HI!/Bc(6Ygcv;gsز/XDceYQCCPc߭8�8"&XߥҶcBҊ+[,ݗa kx,ev;™v;Cych/_?Jݬ�BYcEQBBBX^+[,/>eYyYv;c<3?CZO�+KE QQ:NVk}(((=l~YeYbbd^c1HXcŭmJ(h"q?D?}iYQCGSԢ(-u((f,,ؘe^ c-[V4P^O�a(K z^֊(XM)墊([޿U^,LLv;^^jcc,VCDH}?cE{z+5~OYezX�Z߁hWQ^K<^,M~(Hv&&]e�Գxu^Gy^2/ZYy~=>k^j,(V^|/V?M|=XY-㗒f(,gc/662�F�B��!1A"Q2aq#BR $3bCr4Sc��?]Vui ZRύi;o-lp̆ƍ ʃBK; \Gh ѓ= ˌsw񽚅pP?`eK*7 e[UcCFAPL"c L{)ƫ{/qD;RY]# �U…ΙHsII+�geg*ѪMh*;)?KW '@u9#-j'^bzi<Grwg@ZtS#@ll颗 'E8DjyJ [k-6.]=.\C$9ZƋ8]-AɔhF$ەC�vvs6nV3Ct#+(Y[N>mlfqW8I@çUmFak`!KB#Qji,,(qSrv5Vjc_I]#Zsp6 _ N٨=;70ls~n @Xm*e4o oW.V0EnZ^<r1 _ !rxn'f+|N�Ӕ ;n� \tTevL!㲺31hӺ-c~m%fkNP˺e8ZPpecIYda_qs"u+A 0N|S 'et{ Jd:DV{xCT9HHEhi8~WU(-+'\U܁{:`tAUuA rwl nwN Jk] ?u|ւK3hѸ(4$xh%YGy.]^{ v�U�r<6VcIEqn+<!E-Ψ:sduL>MPɷ%9n@&J$D^hJ!F$598EJEܣs#M>UXoraZ)6ڴ!noQhG.;Y!YqDQm�nTԽ)$78(&q#0'Úm&j{A@u1WJ1ߵ \=xr.juV'WhmJ?uk[ps*ZcN ,l+P ؘTxgJA/#{-W5p|MHpT,# y_4'Pe_l_M=Pjv% n2W:cr^GwBeӿ1kC(1(oiQ{ cܢ@ \+R{N0uDA:>ԕcur/@>NW*mUĒϹ5k0aaEG㲊} wV8%uub3iDĒ1uӜ'\B鈞ˠt2uP֌7* k&*dq0UW)U`i`ݑ6й\@T7.P4Md �vA�ī[*I_9�.R6`_4ƂuC~e cVw�+JC2?ͅs80K +kui衔oqAsZr?0k̍䏺D=k9^Ou*$dBH%ꍲu %M Eebm P'QEtcT@lNzSw&WM@uWlOYU'ʃ dt8ju]]S_W A.Mpuk5hpB=V8F"=W�ꅿ�W+rBe=E j L@�#T,=R -tFJ54Yq=�nUӢ )42u'MJ&\Jn]OEgG.7[N Da1)5-g``-hܣJXÒu]FL.4ts@.@8v%3u(K�Z1 F:Dc(ڮN( P3; �c;+ʹrm2:J@¹񝐀%f&Sncp-vW[Ԯ,ث3;+SQׅp8]LZר!^btRᖨY*�\T[>%?\8 tZ|°I ֝uXU()# bBeqجA;"_;yS! vS)e[:(2 Bo\0A&0\VU T>բ浢U؄SƫV=+Oƒ qVz&%ԣxg6mroW[�I}GDn퀃))S@n{ 'LM~U6Ks{聎z3q M6+ʿsVPm8¶0 !rRzV;.VvAIOhP' Ϊ)QQ‡4 2ʒ5_n(ܥ΁afk}Hv;(UѕazToMVE0tQ/KXWFn`lAڮ?eQR {8 (:v Ѣ-uQK>Sƽ:/.{.DJ}3X픐P" +7Q* Sn=ԩ@kY(-s<'ը)J#Le\[vWwΣsp:fS0RŢΫ�(ץ!Z0]M2E<;N�]˩k=]W.Ԯ PyP}�EΙ( 襻(4K ۺ/ nN9)U5qeQ+2Pf'%Nij!#<iW1 dI%rƍ;ZvԹ[pE(^рUآT\ҵPl)(?ADXNj;�cUF1l5V6p5XDF#EatKƁFȘ:)~ HR7x\ku*QOE}ZѢLܫ| eq0?'}|AFDGzV3]zW ykA>ɯlB�g'T?FO $Q.N쥌PiwW^> L[ OcT+RUf7_yZ) {+As i�DxXǹZ~=ƴ֥ ˥T\$na @��*ܳP_A\s%CvD{F}i_#d Q5 s!N�.k6\ aLJ2'HʚO~؅ P:yp(Lʴ\]M�5R2*^b684e(D!8hVUS,}VݲGJmV]%AН%Z Z:T/#$(.p"K[tsr4N$t)#t?L(+`7@rsd}֣FCQ՝Setu:K]xLSTQQú)](gh]EΨ.-lZ(��=Pe0e6vX;j3 \nT-kA,'!ZiՓd+q]+HQW;/S1\Ul)Կ_ڥ .e#pLhjΤaD`v ;j@5XPIG /!iojGhVS pj5j6;+tGJm3@U[.e]q:&-Z\֣Q.S{@kQZ#%Cᒹt-rd.$NeZFepleӍqi+ObUt-VZ YJc A Jᧁ$ڨ?hT�$z+b*r?Ke ,EzBu\>gQV,0کUtϪQMuj"St7StTʈW;5/.71CdC^b܉R]r 8A`үx+u? g; +NߩЯD� \ҵZ"$n<HJP~d-?)X;B|2AW#EZq*iVA]DJ7>I+?HZWV&Qr je gS/պץ|k0\)%I-FtcV$HEI_XwaruQR t%ZNz:� !ZV N=QA]Z@ guEk X7P;h={Fe n#1u\۹F̂3[QH&Guڢ5 *w.!s{->7*n2;+LocV(pGq?.qR ajFj svPRt$lrTJ�l@t(o}d`_1خCMʻJ/u2('Ez{z)tT–ՃG�jZi3 7e 9Fpdekt((T'AYE0Ңt8k@n9ho>cBSEP3 Q~,mjS)TjSr.?l Fd)0{ @{c0s G 98(RL5Ud4 NB2&W/@5N-~ӏDrƪʀZb5\<+:U3SOMLm  _e n2y펋 udºi6. &W+H J11m3]#e] L(V9=ThǕyU{ IdY@NN̢8@{zc5g N]Xz\#kzg:QjWAF׺Ϫ.h*^*kt9To[ WwRu�^>xpAq>lkEp_⼧8µV�*s蹴 IYc++A\W6 =6BΪ!bW1œ4(*UeaaKOPmFt Zve3?yNۦtKؠ5B n{;o0Χʹ;q=Dr|jV#(s| իjҤeh$U!%ytF  黎Ƚ=r{a"Βx򒅽^*V02W-�zF轃MUz_3#L(hdr&L�[hscusN=ʀz%w�esRvm5EȘP*t\i'kV]=:(?PӲs´2ﲹe%ڌ(UO U:@`rۢ~ISvFPT( i!0]M[kGeվЭb/jUj e!POn_ !vPP N %\7> xI=ga޾[+a{ VY Gp{ ucʘVR+@Pdd[$,!Z.}8"q+ [bzNм 'a N{NQt(;\?j3{(x]*>k~N .#+ӶUj"0wakV0cIBN)wctלOuJNgE{:Sʗ2=2O-Am.[}erwm4TrW"uZ0"F='U(cᐁnwTZ`.}8h-pLGpUvpe9Ӻ. "?n�nJ,+S'!sLaZ'BizG^Je][x_BNS?_1t#˺dʰC}|(R]1=^Y&z,i*#C*p5`ÅG_U{j8XdPy^v^ ?*J~OV=Z| 777 vPSqSZ#^9òTlA0Zi](Y((GuQ: .̢0.8(4N0ydhfFk_:M'TssUSSNHVSY�?* κk_ L+9t1q9µTnl8R+C{"֍ʿJ}e.Z8T;%FϾN;_*cBu Xv&c <VF7u;ᴢWSy0ia_HZNMB!TƩ' oPvThQ,n~˧;-oe~W.=FO4\ú6;-$.-:]n$m 3P> IiX{5x˩ygTmJ�A h@0}L ӵsh920,c%\8λ(E50)-'-iV\ه5 DW*P0[(8`4j" jQxk]<;˙(4ōsmN634+\p! \IW6K4 Rբ|aF삀kPV{l} sv)iF5OunYl9܍jBFtS꺇KtW}ƤSQWSzBCL@\B�;Z:\m+G8]#e lPaڑFL�DG+|r "’3_\sjW8^dh�6eG/o `3/Mnm*(@5(x:l"2{(YiYPN A-�ܩ#E`r4Q*2|/iY V2GLA/7"�ܮ+YGAd.㲰�S*ңUDl ߺec˲L,Jtӏ<+tԹ<2d̠VO$ !Q>.#]CUli QR =jx3p ka �JHEyQNn![ˣur6dP?Xn5M 2�"n4f P5 l=T؅de}&\7S:v5@85*c(4A6J}=ʹ.Є \x'`rnUkiS'qyQ*jFS0:MfV‹[ />J*>ed{ʶwK-t�+9Eu݄huTQ .� #' eZܒ$vRj0ddb (,B07V3uĒ'@ΡF҃YUѮ:I?ZhQ({)Etk=Vo@Vbc,Tp!gEhn), *m@"ֺ֔@zOB˲�Ҁv_n;+l* J%Һp7 i/j0 {ʗPйskFt 5Ϣ9[*.�=E�)jP4(}hPO;6Me3RODvA/zoSl+I;"=#BMC֮&oC#Tk0IE:9h*TXK/.9Mك _Qn~[ y^‚7P=|2Atnʚ&>f)Tv�(!^QޖlScҋv�23|u) 7@fgIhnM-d)S\w") l*71%*>7rD\u\ĝpB(Ie@q٪J2 }HNJPq']!\t?E-=Q48" 5 ͶTi6N\P'^fv*0lUF71}[G8̻`t)W ҭ ]H%�0wP ծbe^ؔr0vYzmvW[D+ss@tsVU�-tF06 BYT[VNIMW�tW}p"P/tpHvuԗ"�›4DN0ivU.\]*\0+j�ث* L7+KnwW>V0KMc]rh2%/L L &.s~o';oD'yǬ*uJ!SB2m/uU8YrlΊ#e!Y+hUvWL ~fq&(@FPwJ%n.�sO4*SoYL c(Gt+Nrt >.K~PD% Y"fDd;[]9teS$#P2NO@0m(,kw̋'3UWi*lx8ȁ7DRX89$3& :#9V?rB j?d4ai2/+f1,?F.5}% Pd¾05⠷+Jc줝W5y6WND?UZᐮ;.Qn7M%8( `ZܐV�>OUsu=†%25Vvdž{-}Ԝ osO h?F "\6W8y J.3\�hr!A%_ڪFrNAh . .]s_1x76�y_9Ts>�)P;꯫sn+*fѴ5"N9$>V[!-CB=EG 5>[0B-4^ 3DPtwDAY:ݑ$0}$}ZC] oy0qDqP5:ʴlrQcOMCSӕiVgr Ḁt\U.Dz9čձ77D~Zu$`DI@RtƫOZ$F}ќpdK*()ni] $"j:j&Տ.@:aZ>gUYϪd@ :ʒᏥL[im۲Z JDt*TzT纁‡7u2v#c*lP֏ΨtSUݴV+vOҝ'wWztL`oSBy˾XHZt짲�n+:5 7?.m\7f>Nmvնm5^3mg:IWF_çhU Gh2T C˦4eiR=HB'9Pg}&*?b.+'M0qC/DKuZmu{ct;w�N˲|@jMGmE huwPM<d+Ue˧Ac=.ːߺƌE9>P<e4M0J rJ:I#DNJ{AwQUQ΅OuVHMhuAOuc׸uG=#u  s_9|:茘<E!'q"jdM]-2!P@)tCnJ|B=po^]_u`߲,`au�P)2�0e ~7=Ԟ԰3>?n�Tt[�W0o=e*tPS^aYu=)$]Q sOԃ' j~; ǘc`~UnʛZ%_}lj๔s%\2#PuЍ{6׬&=pf:SNˋ';f;,= VҦqܦYT~7TF75iꪺdSU*@ 1ݷek3+rNF$P�.5>-�5>go�V&MB({"y{}J4RnYjԜ2rqznZ.Ȓ% 4gNtAnIVKQQyDK9D!^A%dB$NP1t9h% z ot~p}?+)D6-:{+WSvF +n#V˞p�.t]~4:u Zj]#rQuT^p;wD0EǺs)Nm<Gsv/+RaZtW7VV6a>v}X?X%\4Woy+Uv;.^U&rJ�W!4 ڃAZ߲˷Lm&'*kvhV^Tjǒ솫D $I{HD]/!G&,a(6k!/˗!Pe<e1s(7~!$F˒"XOrzbU.[m<sR='BPkBϒ*j ;,Mi'=*jBjzn瀥}J q7_Qقtphٰ4MiA>S�+�@Dz- `MttS|Mp-@{ l0gdlpte ]i�χm&�gInO:.9brDmIB8D*j;Pqj?Y8o#(Ԩ42.|wlcB`!  F}U Oqyb2e UtP.UNLCcuZ5*+U.h@0Eo P+juuA.;.rNQ`I%I%I!woaJ@]'u hRe+^̫#&;B ,i謧_9MڨR 6/2CPZ'diW(Ѕ4vPd}8NSj;NG\dZs��evGҍ�旳S*<ǜ\nWs3p:- kL u]ND)S }hӦΠz(=Ǫ0օAK M3s3t\腛-䵫a n̮(Uy?hAydb!xqr;?|J(p)p!Ir nٲ!¼x?Sqk <OvKA[~&_#')l=ce�V0SNsFΉѰKNڝrwV13 ("7( N;܀EEJdEwqVly+ e}A?e{ J.s4E!` )x.Q/vݗ#˅} X]?u߄"h{\uSSl#g.81ᣲUZ-b}Ye</1(@=Nr%:s_Pmu�pdaFN}IC Ҥ'goʍȽ#�mT k%M'PJS=@VU&rvMWcErYPc3sJ6tkW)U}GhzXŖ�ҧ7({q;&n'yq׫psTv5̠,Hl=0tDsmѤ&uQ7 ;rZeAs]juGoVp;sYɠUvIW#l.dcDLÏmi0*jѮW2q KrQp3wHi �-Av]mD8@{}Ja#;X/E vZ (\V h!PFB ,,2\;d;U时rىM*Pi!d1-"qDgu~'Q6(Se}v(p:RViö5R]3SIq 6k*@MS `.MPV#U5_b:+Ĺ\T<\eg}JDz1V)q9 #`"˰ `YK=F<8D䖭2$+]d+)i� V].{nQoa+' T5k'E$E{SfU Pq2+DC!CSw_5Np!X*ƨ mT8sa6ͭ0@LhyܙJp0VzSS^j>F@R|I̦ӇZO]R49{{òUߖ.V vI>;mƭ'bD̷!ΉF]=gVot\yaM:s UMNV؅q`; n%e .>ezxcU8|L `t>m:5ڶUCiphkBmC>NiH\|"\0RbȶT~๟l{{i6uszx)hJ~T ?F""C1 F^aKNh~a܅6@'m1+OmW.aSU5*f;FV7P=3[c<ȎThé[Kq(*R3.G"U2Oe}z)Z4QMt9^tW.AQ\}f (쫵tQM+#:,lJWQPJŸE81R]>`JMYrH*]~܌uheyd,xu&t=*.tg ceDs $3u]J'tL݌!KfJSGu e5k;.v]+=n`U0ȷ8a^ml`!EV{&tqU"Zsweko7kQh"AgO{> d`i +M28!C~MĜu9V`5ʾ~d(+@Slee'X] aXQ"wPtV/Ci�dEo�JeObuku3NYs? c/: t_ÿm5=FBPWW3 ڍtKM^Mw\eZ.*`IVR"N@oPr4\FUúkDphGĝu]Z".*Z%FE @V@RuAOi'Ϣ c�],Rr*~ʖACVNTT@TL(%t TkN2P(e|BQ?g-V~UEH FVpc #TrdgvRq d *[h{~vTj2{KW5&Ka7)u4M1UGjFEKp'}Npˎ4qyJsaZkhL,l436sE�J&YWT`iڿVh7ͺo\ 52[\el1 ":AWWM5ABZ)dJ4js?r6dv\"؇l%uT " TUiI ddڮ;H;ߢYPwncRZ` 4.Q:G#pM‹pe^N68u8nܮ[rW- . 9˪.+De/"vk^++?zrڏnvTdkrD`NOu{.Wtn(S TSdrUՌ+7 EycNuiNDaEa[* R.W1 했 ;UXYg\ႣkV'&W@6 70AbMoi5)놤k.otFOo=(FC!qy11*VBƊkR2Ju5 >g#O P6Mc2u'TC c�tk}f:m>eq~zv@t8yv5pM ![67sChiaT(AQ[h�kxh x<4Ps<ۭz9ؕ]cK?!g0ֺVOTIb2G�,Nk-VuVUpϪ�3)#e!*@_;/.�sF~۠ U"'D) Q5qYI E=[";;a_Wqͪᑌ.cd{q-8\˲$�e52}N,I@ tRXVz)yW<,h +t rsݯҲmohVR0>粵LeK[8YԩRL,uԥӕ+EoeVUmE%B]Y²U졮"[�^HBVZ!50 -O 'd@q&cucLrufOQN :0nj9eN`R "r\gU:u=k1A.#DZ-ӐL۔8fԗnW,a!=}̎ 0'`vӓXTOON,Zpn;c0h0�Smf�2<4P+JtZxcE %hW1AaeZ¼ ѧhkaqW?@;m㒅2Bjj=BU 躶+͝- X5WH5d*x'T#U;5:#ey #D*8WbצjD2SW9qzir5\ɕr7Fde:A2|ګu2)~1tt ]^K=qr3 j+\" %^MC)%cD`.:c eTJʕj {J˔.P倰+;1f [R)w|ƐxOKSpPT⹑su `L=U헌ZNV^:ϕd[9V QL\*gp48'TA&i\},gBuJE$҃obUUwrv'%+KutR`#.g?g<#+̮;;^*@QdM iƨri:PN6Ҭhsc[oy\4, +NWe u\2v@2$P{/wZrX lȃI+u~QW8e\6]W:F{(ΥI%sTeatgG9_2! Lc8P߾@R'CD-dy9ʊ`A(QuI.*_XR# /,D}F˪V ѻo ҭ *^\,!ge6^Tn}Vpֵ7d&2)R ^ *U2veпDEs5s)#!@~0O|Ğ*Tk-or-E=B$mөѧsl}F~n=0)Ԙ7pWFsӟE]2Wx?+vM%hqOq TE2ե1 дB FE\4eEC/t!1:FvFG)k,VԮfpyc#R!]Wod$eX.56\Ӳ\O o&^.ջU/ 7z#UVqSK]YXšc+'+N(oZ@ E.0; P$U2pCDj±Z|4Zeᦾ*&|:N7WwX]MRj%тӲMpe X4\6:MTNXӀq=AF2reκ W#2݃\:T.P'TʃI 2KDKY ]p-n(@piΨƉbr ƖW^=0*W7)Ƒst`#^˴\5@3ʟdRcE)hڠd\ӛ\ƽ Q.Ѳӓ,-dǕHh#IF�ꏾr.)>[w\ONϸV5eU&t6Z>5T)\KæcZ6|M!Zo̙a )lt+7%sVVz.QDaYMzµed.=_3U) E+E|1Jxt+c a^^Oe*u+A< aNpkOU}V3xOSK*ythgq<KHZQV:ceiht<'pq m2bS<64nT2>c�j-\w\͒�'V hvEr^w) ~�`O:[;`@cg>iԤ,xgZ6|4Z)*Z\L,B> KWh=^X^h [E:ƒpU0{ˆt;^d\hCT7VMfZpOL IN? YY>PP8a5j/?N칀MnAW,juC[iGuԜ.D9\ZtC< >~oPq�Ϥ:wvIc(c8CV;]*]GIǢ 7WFHPBU0X´R-jW.'Sf9DCT/7( UpL X6h]-Y#\KPkN>˧jQ[3m:LW%477m+2*%㶸Eͯ!SW:>˖Lt6O-ao!];GzڮkM1#R>8dMGa#9_&qLA#%ΒLl†B*TeL�s <q O_Z{VTW ދ@@c#EˁT g<ƋwQe6J`n?La>=fzu+Z~nb/�`^~Үy>M<ȍ@Vԩ SrI–CJwF�FCs7Q\Uh8܎2q5 RIFt^]Hd|U0Lꋧ2tݧewGu, ( Հl1k�d ۥi'Er+U!P\+!IRT( `3fT5ʀ%Z|T*vǗX? Z"BoU#dT`}]mes*> tz,'76``sGuˠhף%FW\275TbNɂUMv$uP9UY_y4R akY>gφV<!DljP ʈEAY >$R)9{ LA!? L\ra]ʂ SܕRTr&d)7_ڏ!elcʺiy%z$�2~{AdU\s=0鐘FBrc.P2U W5~T5BPd`XbT*I gi_1 w 4N%lq!r^HMCNeYWCphVIEΉ+@4RuKԍ%rncEʋ{,+e\Ձ O-|.q*mYZ%AYXjV9SV? P#dAN _f,W8ke'A8CX. ccS罣0{6P02=29>rU-Nd6tMt'ukjЃA`J.ݗGl1`<3Y?ޏ -W* ` )N른QHeG+P i1 KeyB6[d\۵uz%%kUnMƝ;tDsK\v캪{vnBo\cuȦarB L%gQ 2>T=wبJ7--Û nXT|jkw@t EkBUuL7bPqʸn^/2)ht) ++]Rrt(z.ʶTW谲a^._-vW8#ko'Ț#M !LKA~mv=陇3рgj¶9ЧE1yr#A lZ1F_sKrb=OkVs꣉_sa;ԣXH@8�?o QᕪP\Ӆ'_ IS IV@|s1uI;#,R P+{+\>mw%\�0ISt\M=4eJl'MHk Pvyn7<M.uVZy½Ϟ_LPGJ'\׌0D]SY_,S@+Z<Vv]cZnܮv]"!t_YحW,~Tj'vB5 � K #D@R g8e L(: gU3nNPS5] W9�`t:$uԔ_o[@ArE7,lJ~ O4nFBGeDYcU{� {$h;vl'T ^p@,iU&ϫ,:{[�rhYQ*JZAPJ!Zz!uM+klLx$//iY *.U!RB:_emG]R@)p-CAq>w PL@)W6\ l̨H1McV>buW,7FNsAĜh06ΈSesNm'd8uD*ec;,BwFvP#I;J*AF %`aCC_ƊT(VkN PYYQ*A|%Tle%A:#%_nSg?s!SJmGѶ0\&Q$M�QѦ%5ÑtSPuB]nPԌHEN W-ʶXV:+Ns6%5Ņs�7뉗M-v7ƽ&:A'pdSͲt* ݀\9X>GsO啝<$g/ʙܭ4ZAo]C t[OnHl#e3tLI9%! -(S6? W:~Y^K>[gO| QNV*ĩ~WV~PcI[e>SUS3BOA_2/ +ZWGuE\5NLy ^mb\D{-VQ>QUEq `Hʛp+#CP… 9~Uy5U8D`J,ꎒtVcH:Nե\AƊ5�~C/s ǬHc솎327V>m9E:H"ny܂'Fuuڡ$7UY Chn6]-&*k_B4Z+VO + GJ·<}VZ-+HСN3b QDEj#/㿪[ثZYW%]u{,oKڬÇ7bi9=x\-&F!VCaZaT(7X?}:qºz{[VdW;$T4\ vW_2#=1xeQpUʆ�(bץ(gevdrK3< ڌ5Bʆ#EB ‘YA+Eiጭ0[% U65Ȟ&;RZdHN.uיٰQ::o}J:cѪuP\DB}NT5aqKsLeCi[f|3@D?Np5`qU#3v8�v~;u;TyN?0pֱ2UNQBes0< xGΤ,hYjA|r<mD\͗OOAšu }QQSUC>dZ+*>GRTl;//7 c %:@ Hs]Dh~ 8z5_N-r >2B)xWc`vAR{aW<h4'fAH:.] 4M@P{)%KZC{, FTt uԵA63qF!:`ge(; e]+&>@Vڠ(B+*�V(;(yeh!]:V� y:vf\uQDrS:L(~~ϝTWuޟRu.!rY\*T>q1t=]x B_ n~ĺҟȦHE4dXyXpZ8hofS2s@QLy0mOŸ l%d(W5jfrVJXLGٔZJbi㦪T QF{!PvN3(\s&=QcXe nNZ!.�|>4H, GSՒq- T:Bjm27lSV-:.X8@Sn@.'uΨrЍ +u˦`۪m0`ogB`Įpi(euCX;.eq c I0tAWXoXV % d)KZ!ItPX@!\T j~!>Aʔs4*kXq@"UJv{!t�"-˖M�r'D�h]Sm S|"S-,Twri<<HǪKS\[誹ü.[Lo9mOiR!Vh4CtEӢ5CU g.S>'U$-|p+͂9M؜1(+*J˖V0Ӕ `)r]P}Š,1zŠ! ? N;|D]اgH(�]eUwl2<; sM1VU ǔGBAQYN9e!\NZJΥ_Ys*bYVRuBf{Ax*ʆ-\pYA\?U.eh2rT¹֨$WYe%eIVj?*$xZ�\ D \ɆBJ^ͩ2�ML;l%:Z�BNLȍHL1QV[=YۻZ@"ljF^3&5ΞW 3DF"NdSaRΪ<ӅOAwmQ5:":oZ)v<b%gUi|2-\`a@%\? O Y^(XԚ#ʮvaAZ ZSXj\LHM7tq4=(4W5K\y�J, eAxW:VO%r^H*asRDc^�i: :zqk8E(1tDndA@U=kN $-]>,eXѲYʝN@ ouc;1`(\QY PWXu|eTm^U.CDJ%A@8ʅGy&q\n)@k[ RdsΑNlSQru>>{q5MvP:6H<8T͡3hc8ʮ Gy_@Ϗj᪏#X[.$Zx]? A€V Vt�GKU.HwAb`˩E�ľe4u'!L5SG}=mU>9HÚ*O:<̨l0E3ob_{ʑeaR lɍe.M1d:Ƚ(Ũr"aMF �.@wW衺kFUз hwDixNfeHZ)÷QktC9]bvRKey~T+�k@ s~yp3蹃; GôioEJ"|\˿ӰPkV XQ0TҰ7HQU=o3!Ij@Vu/_Qچlk.|Ƶ|u纱3)w+O #H)5eeC?= IXa^6>Uot;V<&+MᢹE=wE?(AKDpk s#Pk]y`yT:spz5nYJ VR9?tn+:\tM}M*g!_ Rwe*P6W @Gr4u5OtW\};,(u$W);)x@XVU @ jIʔӝ;# 6ܻp~#\1R~-]aIBݪ Ǻ:0"hp=<͊4gPQs&q^~N6;}rΥ2|V<uw>gE7d9ڕ O <!jY)rY!KG AP�mB0PUYW�QR;ܢ?=K-bH/�0ՑL܀ DŽ"bYYS*.QyTxZQDVTh<1oD Yfʴ`o+ N}7;v=L.[C2sU_"6܀k|ӍT)RlzWۙ/`ݡWj[꺕z�PqRNXPsN(TLrKA.ρp$/*^KPeA kTeoJʱcujMV>a.:\lb6uڃlē'}^$yPSQ#}eGY�__-)st߮L̩Y~Ћ/u8�,/'U91@Q"|*/ֲtTDOir#`@<CJA*i�)#$B|%KBV0|"V\ ЩR۠]? ,Fcu+L)UmRT(\a�Uɪ4P VKvj̞ku]K-XxfF>X]Y Vt}@ 5:ʭf0u\/9 꺻8+5]b<;n\֎d wWw rS]G0Hr5j$r �PT0gR܍mb*8ȺtʰrnM<' u AӯZ7q20`RjUūz(hSS0ʠ (گ4od^Z$/ \GU.: ?䫚?,ʐWV!\u +)֍>•F.|%ajX],wtJӺh{ Yw-y LU.VgVOè8Vl#r{k>#L0D2J쮘l\?%gBf=uICU+_Lemm_T*0ZQt Yzp!CGu$갉P+yWiZu ,(ԧ¤]=jZP\4c[Be[t_[B5MiSesh>w𺕥Bu"]eGnrQ0NS +UE񘕢0T(+r llee\Z)q@V)?)!aKSSc|% S2- usN?+!*)Q)3SO.lQX nYAOF!yPc%7=HU~g0w >iZ T=QRQ( {<\wA [ E1ꈵ`GuʈW)XS*ُФ�?ed žVhcNtR80|/fUN}P]MD8{7YwjsRVqmPN L X .SE2Eu \WK| TVVJt�젫|&|#öP U{J Pxror0V }O+ܩs B}.Wlw%h2BOY*2<i5Yqagi4] Z(2l&됺.XUwRkrЎU?RڙB $.K,\BT)pW,yzr�(YV~ꩱNABa.ƮfN4OW"1ycUT!GoD E [+sPFJVQWnT�wUܭPW�PҲQXX+U|Z)hO2HN=C'#Nԗ-V<vO 9X @YY VT֘4?dʃv̧1]d.e 47.vLrg)s)V|"07~!hr8cXqE�Zƕ8q'm:lDNQcu \��i�uЮ#U c~i%FB-9R+Y!Y*GZBy}n? r\Lkn  X?rw_0FWkrFt,6>x4ẑRNۭ|!Y 2 #ծAco dž|bW{xZ ˖UU8@a[}SJai>8YWBPQIvS>6:^(; 0sDeAS MB~!8k*7<w4j߲6W BNT�+-Z8 Q] K5ءW?c|ݫM*0R󓴢\oU �Oet++dǷARo�{/uBU~ uZB+T ]x2EM;]:ՑiͰ;{~&9@Y'rQ΅Y҅-tW",4+?zvȅ:jc~9h=Nl�{U W} EףF wc�r.\a֍% 8@칮g\UIe.+Te + 2k@/.<<ؗKAV7Dpp7^#6th -xmF9PAOp6#Uh(B]_dQ EԎ/̌ͨO7ʽ�`Fc]*>beB`_.n<E p+j 9ZS7Xa``DP])ۋ:Sй/wKSMT5@es*3EJ@e"Y:AS`ѸVzqNq垚s$8`eI+Ofn~kP r}긊ܠ>FkK>rgS8!tc ,NU򹏔M.pQK%hUBP!y;+|-P€ZI}j57)1Nא6m06RJGJ %� S(g4ť21n݂6ahп[wU蹔 Ԡ|gE'?E7JxAGH9Ytgdc;"ݔ%jFC!sޗ YP㷄1u(hǪ6aL)Ge*AS ( @F .1L@ &0-aS;p[Q#&~a 3칏�E�uL1ź"}uA(-̰oupi: yO ֊aRt<4-<'ęR]�]B3BjcUuɍD( aZQ(qJT B4TNZxchq]/)?X05*؏q4ߘ0gu!vFQ;+PhnmA7CL 5F#e`3(H\f+/9Ds iīZ7W=QYAoҢVFHǕjE0>fz º #Z-wLxUd`\)cTSoKǯ-ǫ˛؍ Ǫ7܍Lj UK]OE{HDː')mnɵ7/3BRP>V̼Zq:JWxl�OuxcJ[A@PSI ]l)>$.W_schG)%Ld`G*/Hל'-`Pv�ʲ"JZUҼ^ej)~`J\2rT|\<y :{A=(1D)rN<, c)c(Tfꛣ\ X*tVkV.Q Be/+[1nVQf:[*[!s*/%&Rp嗀`r0=|fey>ڂU_ ¸㠂*!dTY Ԣ|"Pxi+B|B,#eD|;jJxt\Jʨ.h�K<dw]\C~>=e[N.ny\х˧~!QROuh5\q'I)?RK6L<Eqw+p^3TX FVbڵqMa v%Qn$+O FJ[r^ፗ.`G?I!9r0*6nOt+7)괂V m|Oe� \pBz,;+,t{.+F<#JrᓢƋ\WQʂXYZBWe7pMjcڴ#BB0Ot=}a(֐;PvKkGul`UF̹gBT8*ЂQjD+md '!ARU2Bk˞QeuU7+ C)xiE@D> NV&¶ �S `si,�7�o ŻnBb<9Bkv(6:GK-5B9tw\u.vԨzFp_ iתjqUqRuE)u)缮Y #0tfBpt6VF.�""әÙ2rĻ;_Usv[MP;+}Ah|VNUJ >: ܫ�(*0w\}C0u^|(DAJA+hhV Y~˗f3i>wvmk +}Mzf*k."mJ85$jW6h~ZQ;lnAN|4Cekreɕ,rmFär%G@*Z8!uq'Š4?RDڇ]ו|Pc?ʔaUW6P …SAܾ#4WJw24y'Uvh>qWG"lȧö[L]h@Y E.&!ǥb8GLR;SaGڥǪ,}qNh~.cOi|9:_=U\a8! B5P#@J+]WG`9WpPEI:a]Jg*<l;D:ὔSϲ%\QsUPp?kS+!D#>T.0 UXq^xR %hU>I@ABwV7PBۖ ,�UkþVe D �U˙qtzvE^!9t E*E=!5H*%|{\> _~Sժ-CL*K]Pv*#φUtw\ڵ\�Eܫ긒!хRB2vU�pXRh*$R4�o)@ךUX�%JXuCGX:±ӤUx~iTW]@ v Gtn^F WayBGu+LPBT1_5 ǮO܃ToCn5gWLo:썝>'p NP 6de�i: %LW)X l*^eB(c_Eʫ\pʺrW6vMe&x᫨Q9R eaa>^)Q0\|ݔM6;A[Ssxf߂�(q+!8>%ڪ5-X |}:^2t_=Y}"`YAE4F*H_�n51uU,q.ew U{< y{_Z?k0ۊ6cC M'X-XS +2Y(ue k^{RWb6AIG%ZY젨vYPY AZ) VT+V6sSh᫷؍B }2hA~m9?mJ,%v2I`z9 *먱�y7yn/;akn_O,U2Κ=GT;IrMs՞{\Wq�Fڄl/i@ݍJkԛI[%;`O&>v\y]sGt%5sZ.T~s-<jP|ʳtQLuV` UԞ%\P5Yt`}B�hV(Gy8ND U;)Pj nV9YA<\9X;TT)mpl0@uZκ;<F* yU7N` z.;ݗ&vOiel&xN_}S±k�xgSUdw -FH gJ0c P$X^U0_p_7hT.X+*BB N>BNk1S�7Pch5`__{*ք"�4JNX,H�vSYqL١^:ivTx֙ռ1Z[5NqS;cr*lpZ@U8.oKْMCmj.qxKorkk؄}' 8M�ىԫۊ/u}X5*/s4HX+c` )(-»mBBԫhV'd *2v¶֌i+53kϺ.4줅qarPs=]T4h^_4˗¸T(!XƬIS]E\@**9ݼ?^le߲p}6P{3eQgx~ JAXRJ`jb~%A5]RT+ܼ1J'e\Y ˕ckquZ5$-/HX\cRnѮ-<*B xܠhNTڢ0J֋G 8X\)=JS)Խ7Z/p캑AUϩºuzYVxJ1S>#}jG=pF3ocφ{hp{nٮWW� +��Þ#{M6lx-U ^dO.? ecl쭬"pKy-U*nB�?m&_7)U*brvX\! 88N<p&4D{[i.!c)lʋGZ3+<5.9!;84yӪwѥdw0Xfdo+:V!V_/;DN;NB(vXn2RrL` wo 2:`Og":Kqi1XPQpkFc�rR—t?. %laF 4]E;sk8ꮈUϕ5 =ȰQ8QmF\~3D@X?LgܫZ!e@/#<ᯇzzM!+_}gU?U{xV'(uSi9w9Dj_²Tfhp }6rR3G?h`\OĿP@QNG t%Y}ZχO|-,u'W$Ϳ\.ڼفs)aiBOԚǾ�hvk)æU+͑9pWuk:.J|>ۄl;bfd:]ФeKkW'W1*~S+S Cwx2x>Z uP1VچWT:!U]�hv֚|5PBwYԨj,˨ fn6΢Pk*y,ڏdڬamG+[Z=%!7�4 U4ء,gF`յ'Tڔ+7ЪceVM ,NmZ^=/?Kj)T)u!*^U&Vn[/,L&4\-0:S�Ujs;*ժek6#g!H*Vu<P!\# WLRӅ1ʕ B .Y:P0\\~I9ڠTY=[9X� X7Ypn0sӅ{6}1|c[»rhI)&롲=2'9=ֲ,}w � YU#u2ptPGaA|-WJP�nrTf\)  We6ChԲ{G$6)' {xc"!~B�I �q WGHj` pHA",&rU?CM-{4 R--r7Sx2lTnU)|XJP`R�D+�u)S *Wqsܸz<Oi`icktU5\!܂vVvڕP*RJ'U U-T\\!] �@]N'ZQQ6fX _i:U83]sŵ㶄-TῆQMo`_HBq}<n.`�%�|F [U<2ƷH;)X P)7|&>.)|6ޯھn_ ]ڀT8ʀ+g]Ϋ�l.D%6V߷W)@A!I)oPXeewCY>YN& I<[#uTHV̒,.Xrhm;\.{N eڜs'*oiԢߧMy5 GUՍͭ𓧢%E%|%ڔ`\χq@Q8ʰc ܫ>1p]TsArNMTցm?DwNCIc Lp;/sB : ƚn.D,yiN~W ()Sn;VV4kk#|s3u)ѭ-sNZTܢA*Cp*JfIs˧W 8 R2h+ `lE1xNR/쪳�F0хxq4MLmc.<'-ۙ|HR<œ-QC2;N_8}Hyd>pb_OtSUїiA\� UC =zFC*WGC\?;0FȖRvֺ"3 i#3׼~ʺ@ܔ\ >!YI2p6 <4r潑O+O7#E'?chW�DdAR)I @! 득�\:%7J,N8<k;]^U~U Tds)?k+4\.VQa=.S|0EvV&H´,|=ڙڣ4f7T[<͞(R&H?QsxhaܯW׼KuC%pQ&ӂp?x;zq�;2QO:2%+hP٭^~N5ԹT5csY+e!Ks#Dkq ]%iGöO݀!WwĊn4frL*YW=.X8q.> !s` 2Q.xrTT/Pj<eMsdh*Z4PZ%CToTw@V[E@WJH ai2um^}xOp%g*l!GS?u="|#us%u YAʆvĪhq\s 'W⠐o}j^JJ*F[q*=R ѱU^>s.S�rSf#\:J5]9M8_iA_ڷZT-g۪cWx]aB.vS+\`SZ1פiݛIPW7;/Nթ< Ld)u2"1qaT5�, wW7aR?+B9]1tK6PXW wڏ;"B;>T+;�W;R24\e N+Qw9O+ tRJT3)`6B^2oHc)/s~Nf4_ڜHTq@bwJS+b2tOxn\npRU^ʧ'/ۧe7wT0JH5Jʼ7UK^1,S-stNa'v1 ,]` T{bGV~U=xOm+h>& 7幖M̸FeZe� 2bW�⏌q9 v3Z~S�9˔b 쉥m)‹W?SgZ)qtH~M´ڤ`.8K{0SF#7P?di|5D z/2xh6o|JKL-EnPu -~01Լ鶤SJUgOL&v�)@8wj68 ̂uЪ?L.ct;Qڍ�|6 ǀ-}�>[ÿ.P!l4* 5Cp?VqYݣJ Tմ�q5Ԑ]G~1 tY}ĻEU^\P .kX%1=*tu0wNW[E:К)B}7Zo.̺iUr7L2X!.rY?e�mjXS}{q 2Sk?�&1** BʘZvUeAv,mMe1 W븪5QK5My:rB:\@Tj4j4PYʬQ(9�|Ri`Nj*<ʏFgCsXU .s *f 7xQR]ѩ9OӶZYWR\hiX7d+HԼ#{>/p @Kod_:wW5WtXW՞<Z#a)jTZ}B+.!Z< �rX _ԃ."8"eךx]<7GPd(q)xʹ1%hq-4ɣ\ 7Tq%0NzbQ:5zj ZWj|!B5Ny2\Z<G ӌ:7r YVµ;Eӥ0p;{n4|6YOf^לzs UhT8St~6J$Tt�@!fb)F!Y\3Ci´63s++uV{j*H)lSPE ¸ E tc^U!rzD_ Pm+;#%b@Ԥ +cTLGiTS9Z�]lsh5uFEJB;:w6ƴᐈ,eOyUQeO7ZJ#d\\"�8 kݔFI:=;MLyF[Ȣp>~Nf   )B!L (#´:!MM0Ewkis7ki$5SG{*4㲎#tB{WP4m>pT[9$5rx79ֶIdžJVT~`ĶXᐛd5<U~V +JƭSnQ ;&YuHkXea8/|ZЯPIoQVUߏUh:)&=jO놚.IS`1Oֱ}GCCďtcS%JnTʁ5Dl|gDӔD,}aZY\4+V{UW-2ҍV+J]%^B;\Mzxf^GCu:;z#GuO]jBr8G|%"ޅaJ>QiѣB<?t Pjfr=:t]h$lTnGU5CB;k pgY_TSr*]۬\ g6[_eSgwr[VkYXG-U6V{zܬ]!?<#2BF)CvYT_'9vEEҜ˩2SzCe+VGq}0zqaB]oW9ZƂC%;8e9 cRڗHt_>X,z&q4^OV9C, [\ 6hܧ>FeQ?.Nmasj=D+x.1k~M\E+;7u<m*|nQ\ӺcGaoh4T\&bQO>1_s*9 ѣ]O)͞ 0k3H uB>\vEWu%d̥B,!AF#UpE RJ�e1]H*&נ�\.ERP?*W'eJNae[{>XT)l!%F I~&ZJp.'*ި5^ޔ:jFpt,Ty[6Twvah{t|ڀGdjL ԞA:\JBѢ?Ks/ŲVZzg?5xk�C)>e'FJn?wNCM>J :kMmGjAxΊe\ (* 0Hja(wNa6:RcR�SFi|}U& hUȐn `ap 横N HR)d>ql}[9j?eu m\tU:fۭ;�ƊS eҨ! ĕdz+Ѻ,/tJ|F >\wVԫ=.kr܀xW>ixoQ9Sp屿̠P- ݔFڢMBs)eOk.lu|ꅑQk۶@ 8G@v’4R꠭TJ VòSgu +;,)3S Ur|0ts8ivʏ(Usa5㥌"J*g6\4z]ue5lt 6˓[Ͳ s{k`7ec(VWg@xrkUs|}{N4^p|[lr ߬]A!p_]<]:Vo+<YJ;w%1ڹ|E5îRG@zuu'{Z#ZP�NPۅ賸P{E\_ E6oM( e#Em-Q6uX5n ۺs)9F}ͫS6B ixP  VJ^eZIYT.mv9+pS~%ʟW>[g #XBE̤'S ZJcA *a1j0;ιvHĝP`.[WBӝ՟à\f_0 ]RT>PEJuvRT"׶ANx�4V֦t(cY>G?H4BZV7̾o{OZʹ)srWLaVB�ρkf+EAʗQc-v Eϡ¼Gv_o c2Ts %wY)!8Qm2քE�Jo8|nQ,9Dd!?ժov67@6G:dZf{�|9spif*9=T**T)8}4aL"m ѕ % 5riчIBxgD  :%:|&SԴ'OoSx|I=Tp,ic`hB5NΑML'%}\+_Bm>�\p\\늺"#9R0Bq2F%=f*ϋe~yވ˒Cb|Ġ%:\SN@4;F%@s]ouaK<?3]@:* ܕ5!jvWRvk*D#Ei>BK@u,/U.]*ƌ'cENdʥ4T,Ceވ4vP⥪7":û'TZΫ¹Zc}.U>D܁`*_l&ލMq*ǰWd �YEHrL(n9(RQJj JṃѶS X$TxΐeZ�<:U eB[tƠqOtѸ/Y>C[VW) @ˑpKz\K\9 4m0; +Rl`8)NW?Ka6DpcW&yv:VBWM!'J{۫B+6U7]O2GO7<u.MXy u+q7]Ն9� kZ3O<wâ{tVbWnzXѳY.AŃ ȺWEUMNowN­U.zY<4KF hdppFtj/51* N)er8$t4R;UAe.V0褜V\T\Q \a@VRFG,S ^LK3ˆU<:TWa.hFQu!0D<BvWSw|?n5N}_ iDpS,]X @_0Dj+S Fa9 ڢXBzw:BFm $ TR*fjI5LQZc֏d\ۺ}*5f<l`_ښ8vD(!ZR^TRqβ&<u6](5! + DلlV\.#mBU{ph5Vp/i*y?M^vl4gxj\|Wզz?DUouTz[vi8;(ԫ5(P{.o\]c^׸IcZ+Zi}SjpPmn(t u: l\cm)dӂvRus `}]*΢e_RNkLNSt5Mm׿kꢋn. >f7dCE(reap�RTqW[OWC~*)J]TIع^&]o*Jy:3JtYW|9 \ʎ}S73LLh  ҷPs|3m}BêÏ\ø{W.ۥu6ѲKO.//~[VlU< Oy,'Ru:,ec 7sWJa\3ma˳ Jc|">bݕkW"rW:PAS iYg0BUl=ڎ-.mw\V j0q;$x-4'(VQldTl0auVjUo_!N&Xzµ)?T~M>~Q-?&Jg.ۺ؏qLrLZB̝& T(S-W;|m*p=i0%%6P<tځuMcZC._ȵr jmJo!Q#RB:H:̠7 s-e~0q43ѷtM*';²4ATI֖*PB:GnWߥRwj6SLn6u'dxzy5±E1lJ)P#B6W1 `  RՏ )BE=m@')VܺRXQ(eA~P`,OZ4G=VԢ!NsL~ ]O[93W#Kz_!=| ־8v3~ EԿ ΁4UkwuR\0!|�%~&:k_j^ 7*2̌!$Pْ|qUR�RNIѮ&(RӲ,y8.U=9%si#'P\uӺ,ᙍЦ{,2SVle¾Zڒ=+Z.$tMki{(�njO1츇R?6#qΊ�rekd [ -iyՀi8zm6u½=Sm�Rxw ۢX !\*vQn !rA>\fZ/i=W88T![xT`ک?.nR Ԝ4l^{dadl$+vPI1 P됏v EӅa`ʵn]X8zl{(!KW[VE\xJ,%XS'MZhD.S�ETPH++-V`bФ@cqUj N`Mw<uwG7~jU5˜U�N!WeנQ DwMO*;[8jϵoQe6:ZmktёBb:S0xaE !+'2 1_|JC{ }Խa\D߅;p�jV ]TJk5MKoԚGSJæZwSiCg ˈW#@QV bBp^5ΈˏP\o+R M4lcҹdVY¶CMlJ/ʟ4./<QW5DSfr-o; sFyz/<47SO i|Ϸ(~\ƺaVOc9#1MAkGwqfRl\+^Z)RW-V1WC kA#0;u]F}WXWʇj)E!dT VBZzEdXB\N)umV אָ2G�ҝZӸG,rZxGQ$+#dž~]rJ~Ng)Cƫ81>[mSuj^4X\rgUc #7i_OCc)!\!=-Rgs{'(vsȦӆ>ދL2p93 ޡ(Pi:(jګba4Ӈ &к[uY¹�FU =֝{+\TS']Pk�+Y;+);vw8n24LdYDh.tT4|=ZDmjn;x*oҹE#B[Nz(ӧifxǪ4rï�wNe gB6r;&o-6$�A: v]5gXPMcDJ}10=[[).�U8V 0>Ki#K*1j-pWn3B:zϱVP{Wu6JQUZҤl~<0WSgt,0˨՗}\BXG*gM|goO 4V3]ChD}ʍѵu>9U!\𕅕X>%[A]�+:_A)TxvI4W9r˛Vx5yh>YZ9tjBrSjO:%D XCʵzڥ1jWwQ\awV CaJGR s :�6WKglq()s;cmu ՘|5r-ޑ4 *B�b:f N~L \TА}pU]\Q�tmhj jwB; !:zp2$=Th!�u7&'eZB� >q+-.ÍV*|.?خ �E6nvWԠ=ͩ>KL8LMXF=eG// R #Li {|1 M7+jU>9Bl FZUhTۮ‹Elo vu-5Z/=%Oe{)绩*$: VUn (܍7!MrL-g.anvW] M 7B>?v[Py h7YnP6Eg}+>̙CzCu(I jQ OHn .LiAA_}0cch9LᨲP e`ʥ^.خkL˲RS7LMHqlc0f հ~|ݫŃU$:sq00R/HF0g3.pi2a7]Z.I4 #ЦB֦\e_M>S$y3ZH9V-BZ{[öLiSM-.[g-a�jJirm{NW-V>4BW4}ZguP׏U\> �upn\5VC1e-POELU VusVuX0~jVeK aݔ?t4*2•vRMHX tXPʬP>ꥹ h^0P}DŽ p\8k3o2дY@YzE.3uwZaCt|;s9]c ҉"B.@){sn?gu\ǘ+49 J觞跉̌�/QLh7uv ؜.KPo MTOT*0hMz>7?L'tC qUƍMM9?rslgX0b[FxnOا>4.M3%;DѦI.r^ȳ}fM=<; nƛ:Iԯi9W LontfwivG7KHNE[Q˺hRjkh|UNmBrSodu:ԅݜQgeù_YNH>ݪ9|03QfOENZdNJ5\U{NY8?ŒeS�S{)J#Mծ nyHcUjeMP \ҧQT .¾2oBh 04>M:e[ aK ZBu#d ZYXW9[P.Q>8'OS!=dS%�VmlR`.# e"fJPvGU$Oop^ҢUaL@ ! vU=2#{ Ԕ8:(_,tӺI\If%9r{ݤ'i矧nvJ LnTu]~G4 TtOqFˮjsӲ<Ez2pNĉ@>T8#(%@u&[�s/h*R$nU)_M@7vOuͬ57ƥs"{*vQFLjSQ(U?jy?v_w 4X˂~w _2UPET 2;'K ѥb9ϗn%"#BjcT=.:ޫ|`Q1Ve S ,7PK{+ ,uitxlNL)c<+Vo\­#+W4O)a]XV*Z*WIWk$\ҍQVR RJKt`)PP{teRџUвp|9�eNG6b{tdr̫A+ +0I$nu̅@1UA#t8fdKFNĺHC:MUs 7a5pT]|db>[S^v LG/P pIoT'SِvN/٩weɩRi_-8l)Ynw >+Rqk{i +¹ҭtnWo`[M5^z9̴h˨n4p|78áDB\7d\ CR99pZ"0D!R5jsqP ki"׎W1=5[\ˠs*n'S^Dۍ;.8 SP2J=;+ߤ+$ƊǪy{~E{0>ZXZt.~ #g-* da^deA|)cʵ#D]V"Y1$W(RQRdV6ûni| }VcZw68A>×:]sQN@B2"Oe1Z@M!�Ѡ@8 ʓYQC p+oU.sO{�UFwOdK6j!SC7oRL= _&:@_?Sej:iczQ`NRK"9OK0:mT6j:�VF53d_OEYOt.߲u:m/�RjTecoa"ҿQY݂m>unK-C7p]K1�iShˈV4Ip2Cs^sEgI8{|$bT2WjZmr7?^& `\ # (e(4ǛUwQ(Ӻsxgyiڗ lB-vBhu�df P YFNEa.c5YVa\Y^­xS^ ʍX\+jPc (9Rݕ5R)�uZjIep>>e_0N%D!+LbIWToDi`(a]Z5 O �c+&+ ˽PTpcD"'^pYRp.9%sjղY罺h5GW[FQUv;'#dNJkMM֞V8'T-]nSZǃ=1$NnS"].(ҩ7dL3[֞WȧsS_]@V@35Sz΁ScAt<X 0U^%"v( 3۹Fw &\kj%GC%W6hM^eQa < [R1Gss;"nb<K@En}8B^-fM4x1UlfP>–6gG *U) ^a^5\suP6CʹEkzڢ.`V?BZ-=ԵX9JZje@W.%BX_N|&!eo+((ZxeGiU:MM$aj<0;{}AdG@Sh/r@9A]?Vf,qV_7}�jS]! Mo9\Vp'b~Tu2TwOu{<1�`d3mB&'ՑdfxqL:9/Z4SOtFǭ &v)>ۺIBlv5);5*`yS:nU75GW2DvF@=P8(<SuBw-.s}½qޓj5U!5;S=xDRtpL}Q 14Qk�쮢td:rjc^˘={@N:%rʒ4N uFҍ\+wjB5Z]MB;Bb raЭv(9jut* i%J~BOca{KB*) ܫU* 0wQVV0V7}Q! ZZNE<{Sr!XхV#eͨﲽDf}'e �jm?dk8'Eyj.ɀO5G9`]OAkWy)}@'Ec]&U[.qkp0p'\iITSdBUɤٕvJ. ӎZԝ>{ a9�KSt4"ߺIe u^%~iW%:DiWp֧`(Q6PTffS*lvU*p%Ce)A+T.;*u/n]L[MjctvAԪuK1 i QHBp:�N:ϛ)⯨}{[oi(  (s?+T`D4o+{Z񅜡 ^!^XaFZ1!ZN #TSnBիG\:]7\AdsPpY2c?u*<?�&������!1AQaq��?!Tjbf9lF�Qs4,0Q-uuku�Z=T 7y?e# _n !`ll'Kx3FCb|cqh]�c,%7kY�؀dpE~jz?ݎ)څ+kU* o)8n0z&3Xi1Kbw�WoԴ8�e+7 RzGLeǘ>.3i{F\GEY<!vF/سLeEUin&`ҿ(@xn]_eҴy`_0\Fbm;cTnF;%*,_7@sVX \r9R' &t*kshǖV6ԧ\ s7LfJK@var*5ʹvÙ�٠̺ `kW'n1[w K]Ǩi&PAF-\as�JgsUQlAx&,.mlPNӨ(Ur>ƪfo||ԣb"j/n~Lؗ3U�8B*l}pʽ+vO#xX{0PY[XWS 7^ɠQ h:YH &scMxffη.J2 uG*ee/'GFw.g /h?Qyn&OKz?0ZqwVA,T pK Ӹb\?K�A}Q\"m_^*끜^"e-Բ_E"xxA�ZRCuL&ɕ֔(i (I)ó-(h h^2LQ\.dSrJ1Ii g%U!zV7Pz1%zh F6*vy\ŭ >e3506Zr)Ayū O+<2z l|WM[uӀel7 z%3uTRKqsC^e 2J ^8 1}A ̶3<.˂,uj�naƘEQ@ fی�p˘*ځUW0MO"#VZb 6H77T!\4% ^ȃAL/S19JhຕgE-͑C)sEBXZv3xʮ+tꩇ8 cT᢯?a;wћ\~3�kRi�? Q�-a.ܤc V5<j9Pп* 6pIJ8%%p0:M<\5`Ĺ~faъT>ܦʭ�J ԩevLrO}gvEمk?O[fP(/rg1]�Q"FN%[_)jb63 2AĢYd\Um zSY F!׸a7qyH@bvp z5~(^%x ؞!v1 U*ʕa# 15.QQbeɮ]C3f Txeew2pˍv̭8^;3l.9}zaѼq1waPU9\A2aB7_2* YUAoH;,>QX62%p!I< Sk0-jYY@[u:-Ҩ&F[BΙH'/$` שB2 *! !yLPvWQë͓ гw/y|JVQBFam#&TjZ_2F9<#ॹ} QP.Xv=E A3 +:a}7RVAd)q̨ڷ3ZxU_)uD[H>\s⚡.RNr\a,QMu@_aM6RX1NًAr ~ufmhڽm ЫLϼA/qx(sa]$mA~Ļ+VXn2vab"P,ć z^Vޢ9U|zM �>ofH֊A@,6;_q kV5hظ�,rąXkFqu1~?0Vz†ARF{nW>`ΐn!&uv= p̣&n1cVcU)i+KǹC0Gc<ɿ9YZ ̧wk+GRWW)U:ǴZ4^P2\Җ X+iBI=] $02ikEZ38B,t]YQZT5QdN$6ە`Nh)IsW+21 Tj+8 `ń> {9:]Dj3 zt$o~yK3[1$v^7$F%Zble5y! YyJT4kc6M /0kOsM 3=p)W+c3 Hyv %qh�2\Xi�I9DRoJB\y.;%�4]}\c�"<C|2 dZۙ\XqY tPsB詜&XrI@@&` ;TcJ:&\mx<ܲN|&�u<o`.iM/×sMtjJ'qS&eUse).vHQ31d Qn1vGcJQ@+H i6t b2bRwA(a2�F}[15dMx\'si` )7}φ~Ȏ4 'X29-S8"ceywܹ`zT\cHvfONb r30qATvqpP[oYT- pU�xPȜOD*)-k31qf>xN^1IS!,[|3rP5c` QRρt+cSmpx-eSKc%p\(y&3H7㕌yQRs.2ߕKִ/2SOR`X �|ژ` }1%4pXP*Q` eLLØ:Q�-2&r:58#M3U%_?wpǹh*/q�u̶!UpP<3jUK bQ\6  5Ln ?B l%࢚1/Yw- Ĺ6p,vFa cX\/kY\yKp!%-|d8'H.]C{`CWhU1NgP2c IH5|f< IGGr5*\diCR^�E|9J~fSPt3񀙈1 fzuЃ>Gz�\AQnq5(" be_CtN ͭu֣[u<3o_~,V#uCv@F*�f<| J�-7Edc38!ߨ/uV(@hgȯ0Q{IY]0^@̩G_ @iݓha1 uKO E1I)0x-j,5f2:eLڎ*6u\3HAm9 ܾEmZnÆ w]#5A0rzP|F Q.7N]Qũ[T&TLt+#0kif!EBk C@f*8vjZ&8~&}J4VԺ&hb ,xOL2#L'.�FN_yn1ZL^VVjJJw< d`S*i..<(]q75DX`1*=\U@VcJ3zllױ;`C?u1;_S<O-*QDf1 = ω̸s|@ְ;lD3B`X3B ɂC -nQ~eV^1yu/Avana0]b1`ztHة^dDL6J͌YYчmz&w]`e%Wd$o+-9͓_"k==NWB4(`#~傱CTWz쮓c8CB9ژ -T8f*֚` k))7ފof ܎ ]JYfSh: ek .ePoLp&<*ZJWj嶵W F=agԵƈb,NWq cS()b2tţI<R)Ӑ1\4~!\TE+HVVB-,\ mq8JYi08L-^Aeè�0tbKo~"#ulCʫ`.�)F13+d8[Bbk,E)!QaS=E3<Fb 1FJyDNW�db `% b�Ljsrhn&>g(q,+DlF|GwC CyX[ K x,yىxL& l ֣ \Ѫr dTZ?NZ5X|CɸJw=ӟ LX_6ntIv^>2.^m�1 xcx05(o`lTU>B [ԮG�T7^e # LH `{O<fy?,;vN&%|#HvA.Ϙ $K cӆ` s쏪XH6&X&zZKF�$ E#d[B)н¹&T4 .)XqFS|AUeN_KB oNA38ܽM4e8X%R!nPg85`]qq 1@61!X]L!tT\J Mށ(NeKgZx%6NTPbS2-lkLrH R1nԸ|EZ Bp�( J6 smžGUϑc8$agζ+7CUY;V53S:u$pf8G, bs.&l�RfXX-XYeim8)̬*{*a WQ(4rד73QY3D s2b]vO-g~#8ۈ+Y FWy8%8+-xz7K[\φDr~eobXh<WdrE'iƉUmpGԫ)ĦAd�ص(c[7 wJ5pMb.`9]gOÌзURݕRf2H8*#f Cr̅X<IO&fFP.۹z9:􆅅g*A~%ddG'x2վxb __.CqR5�Ө !⿉PIOYY *{*(K/p\eMemˈl3blAp8@8!:䊚dn�&B\33 (Hbz� 0K5 S/'];`b#I}+naol R(% 3lܺ[t,qH1deE9!hS;R֌8|Niw*]ݞ=}Ȣ1B6 R;JXg5lE`=,]0ʨ3�d`񌥜.㫇@ .NKf)�WPU%^jceŝUq Uڽ؈,5 }ics:<SNb"bjb.ނE"o?.o!ci١'H8].qZ9x4a+3N( \\o8W6w7[|JZw67-fdcR1Kt9RY#ul\ִH3NdSQ,CRЛ5YZQ !LX gdBN~Sf`χP ȅU{+y-%Y\1K+OUUK@rYc0Jf<; g0+ ~' :06[*3еJDSiz`. mCrEO+wb�kY!輲 �>X˃7b|Lư;QQɼ VLq̓UAq *&aיZjTE(@jjw .fk:#D-dj3ίԣWIkg<^cwPQأU.1*o­u)լ|m`ŃA Uc `vJFJ!i Z |n7(S0,gZyQPk 5C0Q P(1t2L Y__-I|Ge�JY&r39ׄ!s ne\ 9S]n" skL,f .OtWey2Զ5{y"$i6eqQ " ipBRrR4ˇ_nABߘ/]k[5D-:Y t⛳ z69R`( [y;g^ &gwX-̞Q^X3Պ(rSsn _>XEi*.3qp?R7@‘ԣM.@&Pm\̳[ x*gfv%µi*/KpôLw;Pł\P!zEb !&ד�q-gs0@јi0]GHJHMZY�rV C٭CWi/fYNb%`1&`‚#M^ Qco�s+H,ot:F BnFKe3Aq.ɕb #jRu51,wsZ)14e]`&�a=kIcِBha@b#wV/%@ 0&9ҡF*TpZFԾ u)Tɛ9`Ӫ ^>f Qdsk�u-MoKG6cܢG6@DP82L8|rd0rY3D 7qq:\!t)yƿ!05P3JQhY }NLLs@q3fnM *޹wvxq#9̧%ܦ_ #tpvsp̦m_e\� ҵ3D*K0† ܬW ywph,ӫRGBk^^/6ޒj* ZcUEİ uP TShbZ I^z6㛘0wܺK.Gf oYJ*pLT/jp?a(Yu#b02�dy1�1Cnja:sSoi.O(V"Ys1_i׀1�o_3a:6U @!p`!r6x <AG$FIJI $WE!`pXc4P] \]TO5)S]ˢ[)$.Leg2[.EZ@q ]2[~aK*n:긗" 5QR# R1/ջ"+^&E c!-X' YӋu-Uf)Z<B̎Ӷ9 ecY1>.cww|}`t["Wl&3ޓ&x]-EY@D^` 8^Z;\'B~bsXZq LzJR ]~wvAP@4�sk!^+J0:�SFhFA55J.#(F�%„eg_XL!&bRypMT˴�3H]zeY]W4bOQ2"*A4QkDck-S[zaWܹt*(%Ax4,(1Eکኋ{TWV\3Tx{/U))&ψtر oTc>.?2ar,#)u  bYeԳ-8O^C1E8m |Uڼ#z6*70CLV`2@u4Ns+U`+Alc|ڜpWF#*]^U0^za`ux]~ѕ>픺̠;R48z8Ԁ.0nzq*cUQׄ.)ɢU00 ^%*P+ 05btK9bKty3YΈJǤ+�sQ̵r٤ BnF+X&aN-�ڻ`W\[ R=د/Kܗu.%[/�.K, "*(B]ma#ZxoQ�QS,ԻKگ`tNC::A6X:R[$.jwu<FżO*-n9Z7`ƻ=F)NҪPVفx\<B*xѯ0Z Ula \ۏٸw.!o[W94[[ )f,EM >e[CSJ:V~c"|i}@CX$W]'_ NA 2,*w&R�{;�?Qxݚ)&{%acMAa 0(<V}2>E8')o*eZGu5�WaJu&P˶f9kbN|sQt)V|c`"c)2g? O0& ַ(b+EYyɨz&<%@+]*ٙ C D]2&5}\S8O a\ ` qfyb�:EYbT~f߸bɤq-;ҠV[=⥷W_PrpwY.!C9uETSxk6yц;8~bB&+B>cz<3ˁ  rl4pBq.rMM*j�Q`gŢv˔]XӇ)5ȰS*zwr`M�S `۾8[:C 瘢in@z_Z*dCxi7S)A|B 1c>3QJV KcPE\ [K΄7zaG6�fbJ+&o|V1.@~lAt?hujor73 E^bmVe5- r)}Q얃0~uS9m '8eo fj x5T^+g7�8Sa /bK`N `4FZ95y4q Z di@w�-B@/ڠq r*5kJP|_R,e0Dgel]f`$[q V,�M#s�E&%R SL`0ۓ5.]~5+}E@yb37�ټʫq'+BƵ`<)E5ea~Z1Dc2 ]πN[*Mj,+㨂,a#)`g-$7Q+YW�Wqr*a4f{8J�t$⢻ ȪX9o>2-h3<Jfo/.Wf zGOGn"/+}_b jsMdn _m+';^%6ą-CtB{-{�fUD |7)r`աwGI%n]cfؐL<% @1w+M 6'K̩PXR UD`A%DR(%ׁ�聱q YR;N N}*sE,Vߘjv?&T ES {Cbxwp['$sy VR_$gw(偌{%KM�}ŋ�ܽxslz*D trGb;rWpy =KVCc׈ûXޓvi|JML c0k3!aHkb&I1)*Q&TNRW&q:p7R^YG\cBaE0rV( x"0Ma@h瓩@ȁY>QMԱ?�69Q`"�ǤZ~X'/s%S:5W!!| ڣ\i044c iPbyu 23md5|\xɔiN>"QL ^0dU\4F`Þ( ~:fIeܨwXsg6x9U̼o?%x,}C\^"˂U�U*/ d ݎ7 Zf5Xr1ˈ`W q\4+ס4vjp+RƇ</ F4_fĔr1E闈c�ązoĤ<>?D+,<A׫E{qZxer߅ȱ2z%)o̿=̆O:[~g$\U 0K3`^=S` 뺁K(E deѰZ/?x$nz1c+CG$Q7VESĢ JjfDܵSԴYj�@©M4S}M ;5S (qݾ#>,pK!rݫS&#�K: c"-HP [ a#SoeGEKɍ!Ma ]n-!J<JcH0;y_jُ L QDv@w(E /). x˕;EV2_2D~!VPusE3/u|G[aUW )]҄`q3EɘڠL,  V(pTv K5/zD6j0(k DEfrD��ca<MK3tWwt| Lw*6Nk>f6Vkffvv,o2a=nټkK.tA2-m^+S"n!P9OVWgR9MQ _rfFlhePcd 7hg§spw;,S碴xҔvTQYk"3 y{HwI*RGU-4=.M˜5<Եl(n=G.)={=Ly@99qE M9"M(eV0cI5J253`Sm1?8+�7s85uzLr'ASfB[ch XT=Z7aUV H,uJ͔WEkaf Xip@iP[#Cc A &Մp @XjF,Vb9֢s_ <K(+`C{7]1,VlP20Y8WSu6G*֦~w7/v _S6= #.!?V&:D9jO2U R$jnpfHM@)Frb <jfzM0/3!.VG$(4lH* UU/g3}Ur#GA: dX/,Y❂#O6hfN#/&c}cU;֥ݍpjfuhx ]qA%rKuTz1jX+0P]1sD^enU\8J]SqA̓GӯB|s-WMĂCi~U΂Y Mñ%`Pwf2z>Yu h @Ӈ-6Qyp`Y:0nl~(㩷,:im�xTJ3䙛tj-J;)XJUAu~%vնDWF&I5b+a`~G,}$Kk6cpn3f/ܠe88Nzt' >⒇^&??-ӡ0V[H%†SN0mgu+ lD/ :(n*R#9Z fN2@;9 pFKok'!0Db0[ڛލ&gዛ-$ͧeFRdۢ�ޞ#E%ZTJ1cy{:`X#q,PI�4J X左q[#ރxXs*�h ̵[ Qn&WPc/ӔmZ/En4-ZG85]LuI\(`b +ꐴVKf<.@nl 0`"LEEcp -�e]oeia@c,3 YPh1fy 3(L5u:J@pj )`<�Sj|Xξ8 )D[`%9\3_Խ{| mEQul;jYF8�\Dyc-&jaIJJ;e ϸLzSW ?*  XV! HMKI8O2fg]DY,/`EEu޶<2�2-57~Ԯ XwrVW$ڴT$*=A)vr[ \p ]4 @|1y j*(Dž#"A}[8a -#g 57F\ eoCNǒ% J-cReg;nߚ{g2׬~+6F#w�3q({(I;n1PWZ# >lS M!٢%i# W4eXM̎䧘E7j6F*vJ}Ld8Siް-ƢܶP«Բ8hCJ^/c~V�iFqd�9Cjgl+&}N!n?m Cg,Q}jlX#qsPMr3Qz(a8o .X8xdncn"C?mūzx% n <x@e_w]FFQFt1 8cq̹vީf9*e镇b�hcvH:;j5 _:{)1CW,fģnZ̀3;\|3\ VYܪ).J>A  `K?V챶lތ*QU}ËJa,PrKM+~0 V.u |lGr�ٟP@4y�rT\63kCNBe �%еlq5Nԇ iۑ+ }J&(%r/_nK%ɀf_,�_-r</vcQ.1SӾ֦^f7ËpL"gĴna׏�20UwreKS LMv MaWJm QES]D$ࢹ7={@!.XS Ҕs?, [FH8a4t1a]EA+tՇsm%#կ 21S,s(:%*Ǘ9c (RR+e >eC 0 r΍%*n!�]mC o=T!́8Bҵ6XxO/`rg ڏnw=1,OlqlY]]ߙ\|L~hF@a 7۰EPlN9KR^lש1dwPyF[ TVnƐ,DWI/P˦q8 g*?u|jȭE(ux<=] ?B6N:PZ %qj8]Zcvubhvu3{eAi(}2JUc=.Z~HV6{ PgLQ_aWZ]5qfSς4k^lU;%m*z7RZR7¯L..�%ZX�nmIqz 4M%n34>X|y"s݇-`7„&E~/u+00;B'O?1p*S FyV pgYZj2LF69\M@[R['Hr͂rw4P4ngf3`F;32rDMB -S[v"pCT8l9E/Bǻ)$Jo�%fȦV ~c-x=#}E[)^g1#]b:mr~2K5VV ZZh\@VU]v_q̱7Fshi KC`QN 07k-|UiiD$x1Bp6R쩚b&g.?p6| TW-? TC26vKS4S(x-8Fg!q/@�2=&s٥jPJ8κ^0b0�>ҦMJдfy{9U^ ]MxW"ױ+C^"W(gN. x7Uj;d̢HA̫ba~5pP,05bb *ϴ#da֦dB䝛%ȣƱr=̼�kQlB�6[.,^_LT)⮠XhwĽcJfk#'?[kY|_g7 BUha&PY}qZ˽YF-p]Jr &d:fژ` N}#(oS.6&A8:,3h6�[1^ߘx2X] 讯wg1(͆ As.JQW"KOZ](?0WKV3p,1eUؗ 0g�M0+=#ƫ6v*rяx,xbjb*l{ v&QaV �#7$,�pKsDHݫC<0zFBʀ9/" SCr,1nӋ t1>Gd G9"ٹ(3lkLJ\ƾ85~dtMJle!mQ6ZV1\wn#ДGի'-x}WE!5{:aw1i:㙝ܠ948%IqEK ϒo/؍pZ%J\<ƻgJk2dF5 ɿlHGf,m<ʉjT[qcb)bwl60fW 5׿bQcI +dE~fxS:h^+j5(Gё*wicB.(V <d>&X e C6㈋L4�!,FߘYVsXf`(Ëh`2{�jYx)KJ':ulȮӳ71+9|ONTyqn`ZJ~s̈`T?O>"r:*ػjqג"PEGppT; ZnVi|$i3n�ўBZF)GUĸÛKU07 Bv.47FNf+sFZ)'}%ηt(-D{>SR*]&è]@`՛rgU8-b6-PP:5V@0T:U(AŰ W}z l4cw 96n5|##L{Bs,]/1Ṟf02O/OVCR5>p=#5{l( EXcBʵ/p{G(1(P-S7 L`q* t т�dc0lU @22~Lo5-fL3MjPlY]ޙ]B('%0wHlxrN0 wV;2\Q)Tx %L0((cF{s NO_/ f`x5q~�%-�Rg=Mgl2}W|,V˗橘OWK�*F8̛_pӣ ʖq.`gx c <57cS,Q1fQ| 9 Eb5r^:gL4Q2i/$81,Z٫80`!‰[ 3llm*"}iN-fX2.1\?# 梛5DKʾDWco5P6j:Z %hȃV+Av7.1a!YaJK /e6�w�)8YL|ֲF `R"�XEx=A;tԸi|"QSRD( |іAU{ aւ5g%ZYpZVے\lƣwOWqWrȎI1Qp!ۙL;irrCsW2~YT\[rR4(W]1*bi K@p֑1S8)P[߈yt 0\ZPdZ rV`ͲVk5 z;BĹ׸65&v>0 ݝ3:m˨f�wAU�b2aهlM,� 59 ~<3o;ހP]^満D;e{bz0P{wUĭ2q@? faK!Uql?HeS) {9]VE/FUXl/cOtL5K~j&vK<ey^Nˬ�t$h mWQ c㘀x.De{%`^Qi6dVqpVZWޏ1PmZp˘XU'o*`4TUX"@e SF�xk hX/%eA@ S\[SD8 EepѼ <f ʹT8[J7l4zKU5N4jPWKW=#!ZZ&w⼗Yc@DQuk5kvBheQpUB'Ys[AphʣGA* .1 *�VX.&aMiuR͖%Ø (xc!yi=̙˔OUs J>D`@-{hɺ++pޢٺa 3aj euf-h-arAc2557WҢ_0 v${p1c/"Qe8�vg7*&(ܮcqkn(LH=H<@Muo3L2GC:\;ݑ dzxaagMDv7ʏF㱤9`t h uR ]b c/EK3GRW0jqkܽW^;r폼6Jobq80ܫ/ecV ڞUz.b6˹v^eKy {?\=™V nM{L|@=P+] [SV.Έk7S1Z6*/ۭ|=]1*?qbݕ ]L|M3>0H n*S6F|AM86GK 1ZR+e7<=”zAY+:Mġ෸( i�7V͵WLrmR0 f^̷Nh ҨJsSNN%AyQHshpIп-JN&mR|vQ.-؇(WurumF@`8K`g n j̾bo3[kLfP_0w4 SOHrq )F/5fOR7q,D-R=E&T"K  cR@ ܲp7e064 ~ I%ͫ+yX�Ni1z1Py3}y%^&rB.O9R*{Ib0_+N~́Mszj"2(>$(b\G!V6dv||JfJͤnT.�aWSl"bə0Ô6GTm7gE,s0KUTb]ǻʜhmc +*µ.+LN[,8-XtM`ωas-Oԃ!^u?%mg9Lknvh~奙� )!PTTv)fM ,2sr9JJԴ$Z*Gmj-HWl%*%ԈE g�fYL�({u\̂ܿD��;V5rw03u'%8zx70X(LnIWp[6K %"SAEH,8|Mo1,UKhh�yԫ*6-03/|#2Y 33ƞ V/{*y̥y�ĩl1LIU[KpD0Xm#|aVxܰe*=0Mo.@Z&O nΪua_fyFU(rK[O'Dc 5)roa ]2+utUA<�;.XqwW ^dƮYww+ɒP(^5Sm9ˌdf5sT1[l%7�1=n crFeE]M8`)/seeBWd@2c,l13ryĮʜ9 FayYYo) ,ĨO,8E;5W\@giX*8pw2(j%ke.+e(ҙi hKHq]:^m.3tY"ȉT+JtF,a b%Ƣ:/؝@p: Nu 1XKveuCw Z* øpx,&-B4yy%5Q8F2wtDb ôbqNb˾dZ4RntAXhBSudY5$Q~̫ƘhEhe~J&j2T*\LKfV[gYzt֥{lQgEj^Vy(Ve Ne<vf O,›D6?4`_y-_ �N,DXq+ƥtܡ0~ΝZ:Ԫz_ uQQ�6†ϔex�Nq(UWJ766a)D CQ*02hO=J3TJT[_P̹o0Ҁ9n|KJڹq)f§ v<GeJJ+USqi�]{U5F ڽh/U =NW[HYFkpEۿ!`Zl7nc e@ְk3Ѧ=Ac:J_ qUbyBWg72~sģ+c2SxJf-M1Z{w Ѳx8<΢^Bd8 18CWD۩gq+8:&.Eo1X8.GL8?VR�O"=UPn- Qb|ぼ^"G�_7 /iuGۀ"\ϷQBl f`Äʶ ̷W!kPSˉ%Ȳu \(5CP^^R꿉Ap*,|XnCo*k٫zY#[� 7.s2?[_U6"-ol_Dzu*~G 0_2ߴ31V> d;˞Ag%@Spo9UVw BhÄ oc .UJ]|3/'Uln'g!T5U�faQ*9TȾ˽M1qpY DvnWL,D$OkkՎyYpT ia%ׂ2/H ԵT@fG QD/Mg~/Y)w�E<exYy<bo#ªlGVP'V$ K#¦la]8N%K䁨P 26(6ɣpʷe@%܃@YLln (U"ZO)p{T@gcv8@1VFL])ʜm0 !j#EEJG56н|0Tw*�)8,<.0Q| dY}k &)WUB43ơ: Uu2?^T/l{pD .Fc#&&q@`]MsDr&GHkqJ>C4[]dx¬1*P"3u8|[$un(M,E +gG*k (aݳ@c4 Ed^\ � -7IJME(Y ?]+e"*ON׏=~%r.JiIbX  גr_jþ!_sZ6X*j{4."{l|Oi� }IJJZ?Sa+%9m62^샙zv0e_!~%d>LIYk{ %Q?{|&Dgi& ]= $u\^V# [̳QׂJ\2<I]J\̴?Ϙn8d"Q@J r)ڶ 92ջv*RY2iZ)qUEj_+UΏЄ7b`*^ \;DOK\Ƒ/sGU7&_0;p? p94e mDAe<z߸s-JLG<Yj/0!.X"xp#,`Ah@2\jVK L5HDXH&B,f%Qq"m/rк p+^fCɖq,-^15<cmͼ6%|L6 JƭW�9`K<\== R.Cf֝鈱luҗfFY�f%U ;ɵZ e)Kr]cF Lv"�xV;^{ws8 �RJ.$ǘA^Եep:+pS/lˈazdsƱ0tjRG[><7q1t �}~�[>FӜe4۸Ӊf/+ש_Fw\AmVʐ u0d qOG&/<G9q7~v \|2!gX Z/qsi[dܞ#;E@czVe\4tM0=2CEZ[n,w[++ c?pXi1r s1PCpArKbM h<8:eT)f!BSuQ𝙒aQ s@ Z0#nK:Vvm7W"v8)ɗ,"xk-7s9%\W�:)TNUusK`F'4%׮q )ehpڹ%]R( Ѽ]ríWh-^`;q`8,˽zԥϺlyc NO0e,tơPL4}Fb%stswl*%1s,e9a~24Bk:;E3VD%|D#XsEo+Y%OVj +zSUt\-^fg5){~F"e2.W ^1|FO l:|_`D5JXC+ WRUC+jZWP^Y0&M.qAQx+T/0^ +[2 C <B2U5m>\Bg WeC^T:%/Wі 3vpJ߯LePm--JQ1S` 3n1!AyhEScm[^BvU2MEA\eiU;nc�< $o'#m�%bsbKb}dn*R1g1=,�K:i rE4nYvKN"7;`s{XXZ .*qs-ܰ_k<=:X)-(6ްC*1>8ì1 QL`1wW[VMpw+Ot)k a(w<ľCs#B36h ҕ z0(] c,@UV`?Q7}!e<̔ M3rOĻsW4';P/@~f7m@?tD4m6.1nT̒W8/%k.OyofhchL13K5*o7Zc^,#\1E;=Wu-森 nr m�.Xt51-.:tUelJki7YG6G/TK+~b3h*4vl%]-jK i(nk웅--^gs"r/(=Op\c )`RjR;%ȐM^HB,Or\jY Ri@$rEkjaµ-;\ƥq+U,;xקsnke&{+Dm(Y4L}YT0rM }W@Pkfs*|TмM@87Vy n!D]}~s(8aC#<,X{ۥ )[T<cWr6 AoE CrV0qp L|CpEkxĨs)�ǩBbl:ujV( GAP#ZTV9w-[C whѼ/> ?6L G9|3H![L.ͦC=8H=j<❤ ~D%yҡƺYd d<4s`YnvL/݋LSdFG4`_s!UFlt6h y?A. 7(̖ �LNOp4wBC$X�*j! f*į!7r�\_ KD鏅-PѠ9%v90Y/+ !MvӘT`sN�ဩ60&5z3\ $\U @W5, dR+T (<%-%dfB*R4rG0=̹3u y*�՛ZfW` YF"P >$j)o.jX&qj8bWZ+ޥ&b)Z9 :?G'8^P`DX ј 8)rqQv')=:‹Fo\9W%3 QF%|JUUi'7*�+AߨQeʙKJ/^{b }9f.t^`a-mf� _8Z<Sq2 4T1rvrRq 2XQG 2m+ՑbAE޼JݢWs8!z9H,%U2[>VRV-ZRFc9^5m(Q`qkɧpmŠn/uotXs8 ˶^4ev0!R t;<Zc̽; q^e$+E-cԨk`XD躏6͍jвiAњڛd!AGQ,Q&(8zxˡK(Ll#1é}MAE;�;qˋ k8Fe ie!! w-]㺻T n2ZH߹S<2dلbn3s0lL .yw-oKq-ed? pcO K`?E؂$nVҁm |G�L5$9 Z@b%|[,F-έe(ε/^%$z;@\(G9ƨI+mI,2 <WO�>aBG=Lܪxт [坓&jJhSJԲsf970<Uۥ[1:p%%膃`̮Yb$odG)4oRgyo/ˬ@]CXq-aؖ\F17b GpMV&)Su\�C)ɮE [rx#!^V{`7wQˈZVt-]1o\J0gECbnaVO`sϰ^CO2U#{lbҠ0..(,`G#F6 fI~\Q ʂjW=L"+|GQP2gWX�0ǘV�,=źV t9V&_6fQW(k+ekTp<@sx ) ҕ&@ҀJU+8(&@UeR @!t (բQܶ]!©p�䚢bPBFvbJ0CT9CUГA^qKCQ%w=SH<Tw;O"Q;l{eP+G#Ss,"py-'.82x\:6[=CLӭV(Wg2/As̻*PP:"vŶw7 (2g>nn\T5:\Ә\lfYp\9qx~b_3 m^qLal!>an+tfxݺd1C(ϯţe nW>AUTgj_!_H B=)b5H iYs9 �U+!5FS.TqGvsD\M-T\н.QD)|KFh[PQOVXXp^:gbaP+oE`0u)DYHZD +d Y 4xa@#]&^nXl PSӏ0a,7"JlIY eXGjвAL�6܈nt &35{¹^^0{.)f0[yc(nZVaQ]p*5L*UN~d`=ɻp3_?(_Nc6uvq)(Bݻ8f@'Sa}a:tXX_"tLN&CR@0se;7N;`eh 1 vp-OHr˗ghA,cVrZ)U>@g<@B_*JK@c}UO6 _svnȉe_3 h0!hs t?/cR׃3:PFƵhLw<.ȩṫ8jq/ܶ i{W LsSW Ȼ#AŽeeB76>T v$r9M�BϏpxٌFG V<B\1#!#> y9E8KB)tC/|ےA*`P~;ni29yjeC�?0PXW¨[pV:{ҹzsn;_?/9QE{W_f2]0% I¡h*6SԨ) f*9([o,[<+rx;02b C VRd%i~MJDuCsY ķ,ěb^.b'ʷJU+PȲ5|FږPESnwjs73f 0їD*F™IR0N|n7N5:beCb3aƋ?d%CQ 5"`%tނ*fW [jr#n,8b:&P Q(6KFK$,W w )p[4 8igc8Oɰiw`1z,�<Mhکa<B w 0Hc㕿-Q渁ߞ9erw]Ye.5S>g qs(7A +CE]X- j0cuv91/k� x\@.rb,>(kqŃ)hMAr<1[ZPALj+%e/LYi1w# m#w B3@@lX# A^7ؾ`�*+aqgȾAIؕ(2ώ9XRYPRJKl+s #!,%[or eaRc]U<36X oTcR>B ^d,N=JhlזQۚX }9xŽke'e<Ml(#Z hdlL'1+78ybsnljuC#1W[KZcbfY_9Vu.xXK J>YV\GaC:o_%v0!FU>ёd (3H虊ᐆwJ]_LrW5!N| a{y"h/bc7]j *lwu5 c>P8|(wre:<0;`>/{<FfAkE 9GPm0x2#&QE@/1�͒ s١2"MTeg 23b;-7Lp@,=jxNbiu<C@lL) N|V*7Vf'YCEd3)92 F?T1z&*$Xi kjּl>]E� (?쬪k1Ƌ"ŀj7Z㋐iyԭ8,G3Q|xG)2ȗ�UAiÓD.r+!qěO|MD+RK@UjNAD+ <W K[-1V%pqrkWC]f2uZn%rtyRJXrM(`t1>.*ъvPr%W)�y03*90E 2)199i5SqY Os|@Fh&hQէ+ 5G@0x.ר۬s[``G9%ݴ`S<@eI4>ɗ/]̳ �Ȋ<5-Ti,@S~* `pqm\bq1W0 !L%eaUFJ}k9�4\ָ͇v7Sc`w)F: ?F: '` 0IÙkmKv5.o=f{ *jhlP@dc~gbz Ib/K˘F YY:.%z nzR5�0rQlǦ0 r U)gx\ zZE@EhXH-GNz\˃Zjb<H9;j j C1ZGiDm]8Y2PlJ|$/(PW3n_ Xz?Q]it zYpUnZ( 1z(nv9{DŽO(҆,l)ї|׆al<C05dSjVe R;a1mc–+MU1cFV0/>&U>"·ܼe6P̳�sKx9n(�wΨg)KQ89RҦVMKĽ&Po 9@w Sޑ+F6LA�]<i1k�{X>¦}6%+.؏{�2sgS>Q3n6̰":M?W-W0f pDbeL<Km [Rֿ @ Ke�e7PjɹoXEG]\Q Wr⭫[<ǙΎ�%JƁw >c+x*3PEP`wEq2A8U>e0zc,XQu8Ơt):/|5 Blʧz*`ht|(':=^`(Xz}CMpiNvYhl-Q3k qD(׈-G\j�S݈ɸ?~a?B1Hkf& ˝geNiCLg0h1..�mmU4K_TZf/5c9\B]\XVise <nUpy3~N\s([+23,09N1f�*H^)@CODP {?R- [bBk'5 [|vYE).B뷹fxhK`keP* _@*)Я$!1U30Q5l=Y0o�u1]x(V=G+  -D=a6ª)h+3=ӝGU(9Ա:]ſ PGIRUh;xAYTyէ5 &o~v/}1)|cy2Y& Vy"5�:58ԮScGC\Z+{gaQ} z˧saYmΎ2Xq|M ƣP7eMu4Du=eHvЪyu& 3dyaS=*3fKօ%=EXF̃~q ƙY^&l*� 1#y'U_qU|w7,eEL3ex�qQ\[[^"ܕ*zsͱ;1F �F"y]+c Rai["jqܸlSPl,RQ;B"-*U/;JܣYHL\W(ћC͙s.C] =&%-ûZ$ЗaGU-壡&hψw*wKܙ0VV@zHU@ Db1œw~&嚷EY z@Ja83H +PA.Y0J UH?s!^_pFޣ Ծ <[كshXq)KT8զaKiEjwİDEϛm`n UY/Yujj鍉&/Wo9o)քsMiJ8h!�q{FKw $p[V?aʍ̩R1*xXBDK7 Uw-ι^]G4O=Kx<A{1Y %,f"Us#mFk'1gB q6mTbdt\rշ jcakmShϹF 89n!{Sd20m̵Qd|1fPddE5)%zGmE2@]y�ҦGrCYd`[v8Z0 _#@'bn6�pJ@b7S[ w7p{Y<TJá{G !Ga~&XKvT<W7m[EE;rqգZV$ miT6(/nj3fUhc, <q5KYQ0K`y36@F[SFl P_hp&1,M.XXp 7�1r.;z fRΓ>f cy|fm“yBcZ:0+f1ao)kA80XKPFqW=k<�._ g7#P-P6>ܾUt%XH_c˰ag²x({cflͦAyv,X<3DbX(]ܮH\tpqgR(w|LpH.m?`:.!B[08AQrF�a}̲q}ʣAtZ𘙡;͒9aW7 �pv%/11Ad̓ۥ<0X|uqonp*aܱZ|.q$g[8|q"5Ĩ?DkҠPؑLϖj ZA\q-hʮ Beٍ)֎-? \.֬uli2,+(lY>3#:T-8Xpn" xa~ L&f24_LvʖZ>ae.;؋^{kf]*Ś٬4Wn0*n\surW ~5z MKX� 0<KYJ敎HR3fY7d6NO7 HYu^InN?z6jRs.L_S@?g٨!isBۘ;ɭC&&Ӑ]N%} ̹3<H /44nޱ;.Ye9~`˷y-1 Z|D9Pk$~OmE#KR̍:Ч]1}Nb1B�EtT%mg|"&;G�9/* _3ekT'HV!󂵢|GZ-s T& T6&Z˄Z̻֙%7nF,d.ct"rz! ƎĨ" m  -$/:ne(~]\q29J[C.vem1d^x{(Ǥ)N�I)5*@Ǹ5J hԓ5b2R^in=Kl6JJ */Kd>4ט^ѦZG޲;9+&s2<..Q C;,^.'j1[KV,]]Ak ExqgZeőS"L@v1;AU w) A&J^&%!&/{BGsO5y"׍D*F uܥja5vhL2F n] f:9N]Ktj�M5_/Ly.󞉝_lcnm% ?< {x""pb ֩(RsQYTp4pVfG=a[V#07?^[/8[:A!ZӘQ˵c(I@�u-M  ⏀ ˂H _`T X (^Q mw9? ܩWЀ|0yx|e+퉔G.h2: 2-,JF!E|&l,ճ O+lsr\_,qN['ecf&얰cm{Zo1-#@qT\yblGf_Tm 0Lwpvq C!'�,!Moaz:[B9F-3F=c̪y;Gyph&c=v8XU3wh 5s9L ރMbXپU>]"B6fwu >ˎ˲RPĨSM..K�#J-9&-0/TAyVh3,Vs'0qu�}u\! [~ÙO ЧVy6"2czsw)w F&aFfsMKGpwIfex%|ʕ^Ug [:ԭ!�S/vA;B=n�Rnibj( fb ̥\,QD]aEVpJR멦_[tVR,LדGImkuoh:>&8WM2İv h$qq8HC7\wXK\^�jm_l#4�]mZJA שh_i{!2˫ebkE×1 XQm \lZbbSIM <3hJrdpZsEs:Xd/}S%rn"ʭ&bE-`wW7.a멓{tˊ¹/|L%2^$.(KW5۔ nd~epVWTE(<c19C̖RL8*ـ^^Y<J+Gpr %a #ljUŭ[R1Q,is?GF1v,P8b&5{ Mqʠ^-o2 AcM ,:: ޫX$|ʵC2>{&2źq ث{`'5wr M(% ͆Yw2 Eu{7p@C[{eF,wL'/#$۳l:6L-ITj.CDM9<̋~#+X73-% 20h!Q1�a+l mkum"Ut:(k|o+n̵.rbQ3>6ˈ48TQV~3-zv{ =jTs) }[Qy:JO1 O3;hgf1PfޢQljpZPQnϹ@C^m}BQy^:O J.ԠwfM'/aIJhQSaEW+UHU& yun(sD�爛 óc\/,*uQQź j:1TvqQj >}*<RhD PGyY:oWGҴ_b%._Įa QN!lai#+FP~| `c:옌2Me`l̸JT5-aQȉyUI>w5-_70Aqdu <M1l7MܷL P~Qm.7p %s " �S-y6qƒUYVNS y-jg8)od$3j\vq57 kXsV'A[V=lbEsyb 6 :|3Vѕ7@lJy@OGp)IWwBW$5GVx~;(2xpγl_ۮU/xpfDZU4-ZX,ǣp0U.s~eX1$SS9SyGK\ѷk0CܬEl MNРh�qU}!}ͥ090 FFX^8c)Ff&p7k,~uqp;>:&Jzih4 &PЖ%!q9бY,�Aq=$>7qq+NGZBzSb o%!�|AlDܿJ8`qp9&9!r*cѦ3`̶ܣ]BDTVo2*TЏ$qDƏR:SK]o LS-, Lޯa/k0k®T< B;ߘlaM6<Mlω]DhK <T ηfErbá ;0C0�l 1 s,1 E+<Ky{"*  ƭh8EY, Kc9 Ճ5ʧ'@s3ݕCseQI31M#QN틝�sJox55ط9XpJ.ߙ)L4 T˵ j7] ~# ӻIkQjMn5 1Lu8̚o: .YtVD+Cu6әg"n8tH.c.dL<B,Oy". G= �t1@/P,>X](јcC **##py-KBM6KF1AyfHAdv)jQˊm]6so8� *rp}5*"/i|sIbw- +13cfʕd� Zh0ܤAt_,aR"%g13w-wSYTF0`4ғ2%w|ϢqMi8`tLq3s+I@tOTܲrPn"Х<Qa PK%%` n #N&u+ siC30\gB_ˈǤoLH<m9%T U-z骭U]N/ `~]E`gt><t1<b"/91JvΡ2ĻgB0e76}+mf%�V4DM 5v%\We\ʓl~JąN+cs0QټéwD?\^]< kW<N%G9v7EC8 8n)hZ(`&Ɍ| 5]S4~B!7 u=L}ÉRw�Z؂&|Bٹ[p*]SQu�PxN4 ch1nok2ʣ-+p$Q.B9Fd~.a2y 33+�w&?mjUb!`̷e n1? n_36I{�PMQn ^Pm:X5<<A,>n-OkXVre֫ _TgKe P�<q.#�3 W(J=�{dA.iR)kh;^%!C qӨ704ƂZ2n;Cl<hgr10Zs Vj #^)mO|%*x,QḛxTY5et !1<Ojszq[Lg<A?K:7Zok+< $LFShq,hq+rIEčmZcnߍ:e5~f2+DQ=M 7l @=>U4YT�+-P"@σUO1B`퀘B?kȉlvKb@3/'`t%EX߸9/7kuϹzJ{3,UɣĨE;S{cp93qn옲ԦpjV IZIFfLjhl丆qW?%qp- UnKQ.G~#̷e1*ܺcvx8f( ,·œé+L]EKjDVpod2*�N|db{@;/KmXR J9V͙x?l~aի8{#8۔׉Udov % dY1$0J;* 펧Է+|e�BlpqRU=r<\[Du= Y5�m187|;+8q¢Y_q-VB4.m�dkVpסf#|JpZ@Z�WY, ieLhaE9e]LZ ,&hQF8_}J¶߈L>BKZQ|Z*=UQ Vk50KoJWT񆥍asϹ MAwr�^&O ,?=L~e0MKWF\>YPY UkTTG1ޘE@1$�O\a tclKVC7Z˶㧘v�,j7&Vg1Q!;p*grː]ܽ ;ᝋ%WO=ʩ v;!0Fq%\owp٭0i,rt g+oY*7%L (!z28>p-Pb\h;&b�`ʔ05-~ķ}2J{Ew=<@̑UCZjiݿ ,"=m?Pr zybzCJNRY&5y*a"ثJ3 A|U˂c@f>a( R#4J5EsUmbWX0eq8\9oEZbYT9 *!o S~ޢ=)&iQ a_.DksoeDظ�b(ƺ{n`8z/DJ(qv)?ĭʿo,h W'ntSG9~Awh7 }g0oS@BB3g !�5Tۮ1rR@,�8DUM"m) U@H(\LV+hXlXY%o ebqtf!\+Mv 0g2+yUybLGTJ'%D\"- yrG N;5APQ\(F)wu&VdtyJ,p*K Ն̠Z:ΜC4:*tt&|cAǾv_ ܵW<N N WxP#1?\@ɶ 㫙q*;g]L& 2no£..xXg`Sp!u+\ |8,OeVuYʦkiTCᯈA|Ib C_ uI?V7-d9~!Od"ܱTbSS _stq9 ]nX8bLP>Df9 `3e9/k{Eܪć=P`Y/kL[j\jwOTH"Y/Zoww*eJii] v+K㩪G�H\tEe ڕ*)7G'~,]i ijB<>JtO \+V^1 U耠6QAZ/mq0LnX�%2(;Ϩ*d+w1sU#  ąkA,F /,V ;aD˜v5}X;=ϣV)I+sكY#e8a n4*CN!:173|;}]ÛBLE% ¿SP9طUm6䚏%`]򕪹"U忟 Wz̪oIJֵXoהX0/1w8XQ$Vb;d^5.Y=ՅREL&H|<s.7}CH .!ND0 *1pJCKVKa65I3l/ex3?1]3vR/bh*^e*<,< 9%lwm}su3h|JmS=<ؚuQcs J6Kb*?@f)];cZL"8#PЃ&Zbbjn^3&[dDlpƺ@K#HSJ;aop ZDYhݟL=6Uj7K˘ps0BnP g=0h 7IqFDzO XAm< w"vi[p餕רm,X*k_~eWnbq5®1"l*F3%�Qx $G,6&>咦G2Z:=N&y 5Forz-=6c.qbb1Qи]hV�oeq%b xDSb>�Tp6#O&r7sxfhaQ=qwGF5-i񸊨Lqh%7D⸆S]*ϱXҬf^`fM$I\e{;'_UHUTcs0|1w^|% �hD,]7bBo;əG t[f9CƦh>֖ ņ+જ~g:Hbpk(:-C%6w53V qy|nPId'��b.?F_.~?%|r[7kZ=skFV|51j l3Mwc̿Hqs頍5 >0d9*L.?1 N0`1P{?Pq`K7NיG-vv^ qS1|BǚPx55b7-H sq^aQ.5<kf)CS�T*ym*_jE S<-}Gb\�|s&n s(_Yؽ[,ɪ19Qʣ#Ϋ27=\%J(>(Jf�#j!`n5)YFbSz'h,XOk<KG q V<C)N+7:q5)P 泉iF/kt>>�+eMըU@Z:91Bʯ&iQUs/_z*G{O .Ui?ĥlwKwX _ct<e[=10ypU$s;jZ5(3f%b< �hI} 0"~<1��w7V&A5]CdrKQ:#'2|m%!, )~`/0YGG7^fˎ3mDxUa3`% .dK*7X 77�4,eln'KZ~y-,ψ;6gi0|EK0E#Qh)+ _0D';jq2Rx/⋇e;O6խl*瓂cQB4(yJ)I!ΣuXufCI@khE(گb5L�{ʟC<{w0!k*[3z XRҾ�rEwf`2m>&57P2[Bz4}i`ܹ6SMːs#:8,|QA<CTEJ+Xӵ̏qW ll 7);Dd!rzIq Ж$(+:s88<"Ya#"*q9hwVld>ԻeA,qV"LitcF<0Yj%Q� Zf>io2f?!_uZ* e@FZ dĜhpYp[ X2fq9;B51?*<`wفfUQ+˅> ya6;\ͅB.C129LͰE�i}e§\[4hyj!%g_}ns-w-1cQ{L!L�+ႀ2 4_S_2ltEt3_ɀ=U�f9,s*)ɠIeÈd2i_5Pv% Nc|1(^@)�xyQ;c}~ sn. v߸RFBe ,F*%d<dž# |�qE/k *,�WKZ~oE啌G[LkyS<1t?aU-C*3d[ߤY@Z+nP[eWQ՗ybw t퀆,薴'TLP [5a[7n>BScI}ʬF eq(qy_0PR`ĥ)`6XL`yJ]mL l(SdN;v\!)boc1 [*]/r,3ÚԖ*iVפ y){5�f:ߣĮ=c0Qj)o<KͭA P֧SBn{GYc@|DZ1ŕs/Ĺۥ("_p]UJa{]v-9S�0\7&ic< QRA'Y:ewha7�{"ءP0AR y&4[hG#*Qb䏯"> oW!Gp&:/QF!K]1;20SvP% T|]ÄC,oJ 1 wxYpiixb2T_D: wQo1" x+l(D^-1nwJigscT܋,x[Ys*>Q �}͑پ%[afXWb1thFk>41w< u mw u 7(6 (!Dq60 K\ t/$kheJHN=?(/,1{b^PD FB ^ 5 Y0N+Td24Qc=Ee?6#QzXV|B卛/ lϙ W%## )9,W9̳ C'oi[mi &{s L=CVG�1oD_ _A L=Jdp2 KUz u,(.iŴfRB@qzI^ZCCL7%g�zbzVfU[*h5*Q+ScQM^Ɂ4n 2ίS#ĥQs"?yvq{C@&\lkfN 'ǎxM^yj%]ev$a1ZkVg<ʯ-W_X JU|ʹq�`iG39�1{Nom%.j�/lx$bQˡs�Xy[~fB`z.poJϳ\~J~\CZH<͇ڙ`㓙q[W`.H+$XaO, eq0T*^^%lYnMњD3zRenh^`\A* lVbZt:S|2W !u�7'P]#i> 8hz61,_{Vv~e g>PAk-fs8HLL,BZʱ WݕrY 6q�JYE 5Z5X.ZAۙw =fD!b LYK 1Ykk:|LfsP@}?+.pX/~\k�|'BOcwN%LE~bV!+/ l Mܻ�o'gʉ2̷).+*=ڙ1x!9bQ[gk [ԹKn|,5ƥX;J3BgcQ<SOpJ]݅Rꉁ*)5KS'- ۇALK[96bRv=˱emv&@&ؾV�[ #ҨV_Y(Y�7{EFa|2{V阄H\rsS%ߌK u\uÔXșۇd.42'1}ˑlY[ר+k]es0Tu�5xF1u8Ĺmp!�bbQ<2K|WgN2r;qxfNߤa fT6]N"#3 c$_bzHS�/*ֈ9_m+Hz %PX6+xd $lds?2fT1~Gu3X9^L'6KP]CQܻ ͊nТUb:2r܌(Vk:R%,`5(x&5<T,Pr.f({{frKc|}Hlۖ[[ߕ[.hQ M<5 |Z|cwK D&*mŠ<'Q<Sp1x6(CZ F`3t~a +_g~3=uί(㰲bW\0cCY`/#`vKv3)SeBaUO2q;TR#` b3 xyN ;P8AGycQ|tPAp6}M.[xR{a{Uc nLĆgVTPN{o8& V2Z2c=lYPcps17ք;֦y׻)ȫ_S]۬yA;xss<}ϧQLPyo/w0AUςsqKcf?~cVGHM2-QSGFrUnN,d)PyӒ07j.9pq.b($`!=oa@' 5H %8G;; l̔dtÜ{x͇Y4>\ uQq~|Ǧ KdJ?PKy.,L Dx1'3^|86!egHNkmE+ٝG<;!#fԽ[O#D2ԿQy!a99:�Sofޞ\pq)<ӘPHQΘ=Ė&e5}J W3K7QS.5L)N27%BEJcA|+(PZИ(&~b,nuy`b8.-z ̸i_7`C+OpÐ oWx/.)ٖ/]beHK؅5|mx<[ E31ܱpwdt�P]CQ7fN A@{:uGpP7dYqU) a`473\Cܤ%F"5 팁k%1u N/IhcH+U_EoY{|(by=̗mG#r"bUxÆRS&>�z�3upCizLH]eOd0փ /1 ԭ_"[#%w9 /zKyEb,El]TC'퇔&eR I=M7 WEҝ .2ER;9-q*k%9Խp`*auOG:�[~X`-j:;1|AP;/J/?B!i];"g'!'0`W7b.Wo[k ػ .Ef�/$^0�m8\V'#Q6/J/re& ʫz2%gfǘ.?1 TPB3 S.ؒh<8z!-Ũ){o)lxL'F^H׀P\M�%4A˟0#̼8_,Wܧ? L=O f 4Ǐ"(PWY3φ]4TZn:Lq:{5T�0߉XCwyfFO }MĪ9o}�.MĽj o3eh5/9&$Bx$spJ]^XKmJ&�U1YlXs,aJ0<g(e şi[*끹y ^,l(3_]=PJeMENѯ%`P.]uV<"h=<Gz.X) mQk;E.~ 2M1 GVʳXRգwdDJ[U^%116L۹Y(Ӏz" \S71<c0oc\a�!ERB&HW9,�QK 5]EٲzHou/.#5R{jm_e'VévwW ⵂp%c:J6_pxh�\/Q))̰ ^~gjB 7x`iP1eT<юL}#y3H][L#L^$̸-�em 3ۆ\9 ER ԽCjq|b[_̓Vose9/dK۸ p~ pu)\uq�&98vg &b_ {4 xkr h.x~Bb'&)Qq#9Uش+n7^%(pEcVjl0Q]lfx5 *={Q�AR7qYe&-B^5^oa -;J&4f5t(0#*�ؔxE "ܧH&7jVuZqGQJke{SIWX ZiRS0Epc 8 |S[ܢcxECf"XQ_ܲ52Y\X'}8pֹ 1ݩ>ZQm߼Nެ gk~ܽaE ,VٻmS5ui&Xf8怖&5-a{%*1Ve,;u2t. P~X3 LzD. #ԍg?R L�ඏ4Vrjlߘ!9PLڕt0(ɸuO[@W RWQ|Bg%Y-4<0y3ixLrJfCN-sLN-ժFjѾe�K6Ql~ t~#w٩Ýu3]/̠na9w*ꗈfw1}T}񆢹z*:l5@kNeThk4-M$ mix`D!5biM;A =A$E!w֖~@-o512˅`n=8ap5u XbֆP#G%eĠUˉQwW^OYQ w,z U3 lUg-.Eq0MzN:>Ѓ͇P'u]7xjB*8@1׌�RF8ĠP{ +C%x1M{;x"[t (}eU,bIrބÎ4q LH7 lF´t�2]%C-,%ߺoKy-]a܊Y&\q^/uAwtXBaE9rpKyf`{f 朵*-sG_;!zuŖ A^yN�ӕ3c3AŶ~# b=(̸WS."(PxpQX^w(nC}J)R <Gޢs51|`+eDm5b3^3<盋^dc3ܰ/lp<Sg-S8!] :9#uMM9L T |2"lA(_rYRʆ*tZFb(? ;똘IXTl-hjX̢HQ>Ӗ1oĺO#b h|]&@֙{DY`�QAaAy7.J a8fNxr6fG=0yst5GuG(@7jk5S4q}fjj)+D(iN :Kx~q*D^X!Bkh Es(g]4=Fx, ?S%\LSM8i1 oc*ݐa_Ѳ^̃Rr { QפVM<f̅k!+} &e0Gs6368&Vp`Qhc#O/GCш)P'DكaW+gqPl0U\lKqyd/R_ϕs*w) _>%nWsڅ\V耽Ol? s̿w\01KJ2A9nmW+ŘU\ȺUщa/WHc q1{[XS#or@aǨkRG$E* RU*u7bb}ق+g3,2|ezϸc"Xӆ/U=Ys#꩐tCQׇ �aܰᣱCX>u0\Hr֞NM KU๝K�0rҜ*,A <1_F]Gp9P`r׳~4fy}#UB``yr'l%'Vj+\|@Z@PG�=fn =K$u,2hj`\>" 2}K46e 1u\KgBZ]VCг>g,|G:QU&jR/("RuwB<ow^vc0e A\2 |e).  :Yk<�32`v 62ˆ^'M-G &Ds05<榻֫Ryg#1+cx58L8U-An&# ,وqyE%@f uY ݠ7xŰ#!i8TqMjg2n<Jm'<ZG`V!B)[eTo4D:x~bJD@}�L8�,,PLn/ )\D qqCSϊڃ~"5 :M G,L2Z-KrL.~KĦzYǘ࢙l71.w,ZWwX3_"k@OnoV8&,<j.(/; �%P@RV Y:hH*\pAl'm0`Ժ~%.8 f<T=D0,fT+xV/-%pze2RJV{Ђ=Kkv2S#,\j-@WL74eL16̨N2zu4_`)z[}8X)^yT) 滋/2U[-kI,O,!H EaGhsA֯<qGkh'h 2ptE>ѸhGƱ~"ӧ~&saKr׽e%`@cĸ^<m=̲�׺wAԭXy6~sXB2<)\vDˋdaBSPkf1ܳ3Xz oԽg@ {_Wġ�n,3i WApYo5�P+u*yh5iq Tq9@�8f JNJB~%W?QlgSY#_HuW涯q;zaAl`AŪC *VEZ\C@d\o<FTSnD7URj]DP;rm_w-:ge^/w`݀%AM&é}_DI0Ak f!oK S3 8XVX`(3�{$SrRȡєFas}3 َq8;K/#c-RGthqqjoev<JԵ]Y:o Y`#E`jE,7�j'҈c3ZV M~mS-$%#KZjGWnԭI~LY.p*5PﶣGk}&#Pr˄n`g gqTwM+c#*^  Uv^<&6Cdb,ز4,6oT\{�doԮ[{f/aͬRgosrg4>!qZRB&c wVoPľM2V [ղ8I4lH4k`'ۚcs%&�37ejM*H/]TsqȈLj] 6j]ܾ )S.h-KABj;+V;A=P^sZAx9ޡ2)Z�t<4֤Xݚ®Q7AcȱUՏ5>&(9(mkrY)qfP6Vq~#u>`s (`.o!o<N&+^*Lʐ@~<_Tp27P1k1A,:n& ZT.a{YqP1zQmNҁÜԳYfۮfUg%_�gB'^f>Pq�ac.1.IIf^1}q2eGu1pw>&j^~ aצ1Gն#]33+IsIf] �[@[GX*1J\U fb;ehJ&LFu+n2k,아iZ㐾f \ '7. k-�b:+f`D/"gOw;�vn/qM�\{v9OHZ]L2.HTB~ٖ> g uhQ}0ǔ9zF�L y!h`]G_5Z<JSf�Sǘ_~/DR?\KW&r9b;APfC1tW{OdL5pļn "AHJ_A UG~ⓔV۬.~.{&&X?ڀI3}K冶J C`!r C|j |K{zi3!1T2Neh|Gm.*s-f+л _cw5PW G2GQ`.�O 65rֿ0+,lg0Tʯ̣)6QqaA&Lgq7_,&}G9etQܣ<B-A2 PS8v8@MW/4a-MXxBÁ$&ʃyJ(U:wij#\-u= Mkq;Rc08ܶMTgpID2=G|sXV // ^뀖 ?:pLb{E# )1s!g/5 k0X3�й�*/@~ ȯwGGp ZFH*V. Eh”Y *s&2pLN ~  ^jjT5yTE�UQ[`>xBK{_(F>8Y\#b^d\z4{'<xTl_|K�s�c14ʀj E.t- �81CX>?WOSvu+qrsq)8u⎇Nq9@ CoXR)ZYg0]Yde Kzxy�zxQ`Ql Xw�79R. C1[pⲽDs̈o%x5(x5ŎāV9ǩC m-k#Y]d\L ]� ߭nYo{:8$bֈGnY07)`0gCc#Z8*6y>{NALBL|W�6(²9=3WIx"7$ mMHoPoqh~F<bXwf::%fL" Ƌˈ GtΓܺ넱*qwWFܧwQVtW"1N sO*NrT<9QHP_#384$0>aS3uܩ́ʷD.HpA8.m8ˤL'MĘၹ$X1q(_4[N?'YX]RMU=JU�z2Pa E xRc\[DD  @SAT~ 7Qm/M!p]Jƥ  " unVۢ 5Gخ%;ߒ!qrK/]!bN>e'bV\FD5,& D51"tLLosPVq/نDy*2J1; N 㓏3 ӟy'l)EX1Ģj fBͤYF}U!~  #)*^}_B097.%=@x(B2bu=ZCKDð`X`qVf𤵸C;·(; ^21ekxi(=cp_'fa80/mv;T[5qCIX2HJ_cd.srv8)k>*)ߨr{^L#a'ij]'N@*A`Ti||KZfYj' N* }V[LKD.HX ,c)s^\`HT; Gïb R|30:ģjuE߈0u "N-A1l9$5֘.u"|Esd7}mg'e}cXUD3.��Z{|MI#sJ`uVn2JwBop^+o' D|pubUEUpLcyA ?֎" 5рwԫ&\ʭP9t`<F*cH.i%- eL=E;lK.v#u}neәN7ku(kpp+zoDΏ ʰr’۵m�jmj ;DXBTz/<xGZJla7zGI# <ZTr:eRp dPv[p CB^SĞ{M!<VB.Kq D&`AtGWC^hs YPe\++0 kӘAQr<Ug"kb8mФ~p/uDcym-? bdP?b@ô,oS+KzKڲr/rgDll'/֗py-;%ڶzCyȧ3 4i)5B,YH\Lx62`=3X/_kQC IAZ^slVoU�œw0V| 3%8 ʮ+4jeG<GYܭ0}bQϭ@TlG :_.KJA(M�#-־z 0˜2=h6swsdm]oEj"&rN5R-mh+KuPkxʅ] Mdx^cjEW|c+ ~%P T.5BJ1DZܢX쯩gt/D­i]]Z-(lØ/! G!xl,\E3g`+𻃎4 ;52]5(!Yfp&',j&�H[,2ޫX0 Zff=7 9G-E4:Z)V Zs =b�,9`JI\7R͸Kxfz ptdYB50#FMY)3["g'RaiZړ2 }ʈYKIJph+l;q(my`j/P5JfҾg+G{Qr:Qȕw3\]0GKMLY'RHn26Tr=2e ៑mtԘMöÂk(~VxL%.C~^o_Xn[s ~%YFO0r4\˩ wm\ŗ>2AI[ -l<K|1b_Ei GO_lG0K zp R``3Y(7QOs�CN|¼sJTOb'f&qe UP0|C֣Yr;5ܧ՞. E*JȒ^V7�;Fw,]50E2qSo }fY`#aJΠ+;cfTx'[G8rR`͉M()'~QJYvu%*1^c`LnVv}ˀV5r'@[s.Хa}2r.xi `kAL^EG=6yr R1,3pyR^W^"2?.TZT(fL&10Vw)NՀa5jR;gS Nhcc-�@sy B?2`~|\udѡcSCHY G9SQ15ϮIR1SNIQ5 An., 2&-H3x jF/|wpe:.NLU:q*.Q LH}P&� {nk`v.W7*t[wы Q<8b_J0#ٞm/w# .czoj8TW+�-L7-p� i4>A ACk. j-6ur۷>8AY:3nZ"O&&oDy\ΓL7w(Ev?]VWbUj {+-V-aD8?NE4Ej9Rl%zSA1 ͡6zfU5.Xg#(`96foA,PL&M-yVqE EZ:,r03q"dovLo*W1YTnV;-Ԝ!~!jS ѧfܮ[[טnU:7&Rh uiBX=<6JLH+O]r(0"ҢC[-Z`rq ĉpBB^b.åEw<EAaBn!ZKrn$lpD܏:g\2/k.z7q1f`Mr $V +[9ay>U/3X}@  e@&S1D@`Xc[%al,AFu+<KurLx7 g(7@WKt]$'p U:L6lh+3hs k 00> r^^G" hq�X@@AQ:P!т.=|ޗaXpJcdS^ S\wj|J>[~yיv \ċ 1yuϊoqTKK,mQZ^-˅VWө`R1�,üa\W @C-<% 2DC"\ob�PnKRˢ3om8\BA`7o`<}C9ԝe YMܳiREP^.f㌹�2N7 gf*r+M@cb }i j��^ CpNhh0 1Mpn, Hv!8`*lqY9 OkEUA`M^ #C?x:%U_^-t[I,92Ri̽�x FǣrCY5HH H C݌I*b_oOS -PojqT3eiuj4t@5P@�+-D�dzyPP'dεm<K6泙E6s%ܡpK !DxxG$l&̰1=^*-ۉ[5\$I ol#n2 tpTB-l0yA#�7ԫnSEB3iCẫ4FWCqR0K&"~fOED@h-K@&&ǒ"ew/*R73Dln*c.zq"Lxe2*NרuL@[ZDo9*I 4^MJwV"i�v=S{8J.q+ϸ6s))4@ecwn>;k!X7)Q*1c�,JB( TS-]1!B$91~fm\<ht�倎49̭U3Aavv# 3E"!hKD�sWT �f)B1ܵ ҙS̺QZP.4Kj]XD`bdr76c4aJ:hPmIPG+uw6X9.튋o(—#wW\ gqyr]ĤrR"ږq̠<FʈpL^bB\&8q/S IR5zC: .QFb<4g…jk7UWPpa XJ@!z㈛Hzޘ)P +ټGL"*Qc`WJ~NW1J|NV%eAN,rUgb! #S'V^c0ǐK`:dY̏0e9f (ѱk@j,Eg V^'+.w2ϩ΄|0\\j'7-\*04J:S USK j̽oq-Uf9jwEs.fp- qSip  \h. ̪!'v la*k|%(*l{J륗*l: ΠhJŔXfm8b]sd4̶xsq�R RŁS*^`Bb\;.#'. `;&sji~`F_Aes,'*o�X]ęxbvG1[-wW{ToD{wzh#ٱȿ<}k- ^XU MqxB# -/'jNth+u,G|opY(W0<JOHs$lj}T3UpkU 7V}Casyf^R Tm,L0ve7X^WÚc=w9w zOr  CPi_`sgPWd0r( 1i}%vZj%<,xJGiC�*s^ uRaƭⲑ(PLn]1m(tv{-ayX95eAaow.\J[cYlFeDQ kf[W51{bFbx <e,4BLXܵ2`y"b۶ Q`�Ɯ%bGJNQT*;'~"sSenX`V*!53F⚎ \»QzS$>[S* h,t*غ ⮠09pLB-̻ss0J_',UAh٤ftĪ+!AG#Xj6ې]Ru ӎuPU?f#-T,h0Zu `{8;VDEC/E9%yQ>ejfU�N=vԣwe LW-e\3m �Bd7Y|[+K԰֦W52:&xn2ZXg-qLsV5%lDAs exb UGV=$ %9 ]Fs\D-, ms:<2D2|%F2Bȯ$\bY=Ÿ$c퉊Et |K(]h7lfLD� 4M/fWiĺ ":EJP fL!Hy&"8bfngrk*КeU#e`UՍ%ސlivfUDVZ+�bWm`'-Q('9`Y_ qLS)ܷqܵR9sTGN<peBÝj"cn/( `@tz=5q#F>T` 59iRR3e6͑#̵̭f]Q/l B,krP  ]k!*{.SJقګ-Rrt8%qh̫ y%ټ@Q<* e-\Kϙ}KS~/Q zmdkNk0/G" wT)a*E1 ^R'i+Bnc@#+u1b yY\;02wԹbX{E u .Ռ 5_Px{V�X1C0U)!;�hW K^e#FJ副K9@螁<ȇep6ܑf[2&�~19x/]B <0pF kzMM͓2/DfvfRUD/F9C?4e,1g07E,l.T=$tϡ>$Ui`P=Ln5]4F8Y.Paf쎮lB8?p$+9:014ƅ+[Gz5\U}}JG%@8KcRל-›ǛO2"4аBA̳|�az,e@q#DݕU1 ANx`]b1C#IVg /2/[W91}� �����0 o�JW/ëej$=.\e;o:?sQH':$m;`&G-7l:ˇA69LwZ劜 2N^\qϸg H6<xrI/k/7V7N1�&9;t!oɥu-4.4>jNJYl@b3idI拚8LW)t=`pnA B"ibxMy9"�@Q1CO2O+0x =d\<^ JD)͎qW՚\L@(An2cO]7z)}16wY!X K-.WG6xL;�')- AߔL&.5Ƨ^Y```/ehaLXV$s!BJr 7"4~FC39H!ocBE.�\jf3tA% ǡۜ۬^f܃�Q,%ApDCwqMjX ᗽ?4sBD򄌒,pm2= r2/ lm% T1k 0'9fJiCl"Tt)C.fLf,g7[fB@SN VH-&-m$@.Aӟ,Xjlē9BKla+ܱ�~rNΐ'5^p<y't 㔠[ e5kL6�KOj qt,ՠ}ZJ,1Ʊ]N\iacj>xTZ-5 ϣ/y#ې/W8ŞSS hytmℨ#\6E'D"D; �j?%i}6JM6QEWt<jԋ4f}$>(�l>iItgb8 c_рQQoP#X7Ҽug#5P"'hd㍲w|L븼r t"P$ Z@ '3c6~q�p$�4a ª]۟NQi ͫ@lTcM:Sf~Albѩ9, >+H VWIRN8' UnЗvou Ee?1�ׇo;ߧjݛLc95a߭hVG2[2Fц |3,ff٭^_G LuPDę<EgU/fF IBX|s}^ b2<}rp8\&!CEԏ>鷑l '7AzXݩW'i;Eۇ!J4%5rD 6x̏Y.M:5dMzٞ"$(FAzH4,L:.Eqw+YUf5G">13$t^Ǘs6#kԠW~ӺaY Α'B U2 J0>hb̭9RzQd|%cQ_F.#!%E_%G`Vz?@:g1 Z˒Z7:5||y֔@d(�-`5c!utB>P|l'<3+MX2N�K-CzQ8uJ$dswzMIWvv�j%(:Q `#IG@`u7`-/vѧp A b@Aldh^q!85 !3d`)>6P7*vSzIraf#uAFH >6ȗ,ΩgIK6YDZp1edF<T�4}T-BH\i$g䜦 ϩ�LsUHsQj0 �@B0�uD^%G zfֽ-L�|,TE7]たQ O%΂Epo`{-P݈P�+  :nHM-5s:F� nw@ Ea6aI8x? r6 8! >qmjAGFC6ޔsvH Wgi$0)bߌhi2btotSktd|\4mE:)ЉNqUVX� D|ݑMn# 362UBKزɨ,HR0 >z$< IO(DIpE)p)D#tۨవJߗo  2 Ku"?}Ҥ~!L~ :[REsצV08¿X�1BwQ" 9ZՏ-e= yR@5@ž&) @8goO [Aa<!}7�=_+&O BsQ3 4(T`п"ŪYiqqR.Z;oi}gė:$0K$ݦlWTg6l0G|Hv:B۩g+-$1oX5!6dm` �1_Z$q:P 0[ys8Vad|tJAJtJyy@def0P0S.ŀ y K+WOSzLZodvbX,GJA&{a�Ii')LIx.1u`;gDF \}`̚; $vm9jm2C0yѼ=2ERt~Ndym � QYZ@+eF}3 `_mcfE(|SvLik,(vL]fd92K1|("x 4J +R2uOާx9@&4~C*FʎVfk76WItR| j.R1Q»mD܍2�0 . G3ڜf%[pbb#l] ߥx-'tfj!yaQeFn#^Mls!\3:6GwJ/X*`ފyjPGv hEp &Rn|BIY")A1v4*��5K@(62�W"&CI 5prߦ >hpc9*zUX.ԝШ|@0fb<#S對X.MN$T5$@JÏ$bB ^8Elf+=iZ @IGŞa)u{IDNLg`YS`*mO]N78d?'2sɄ Yӟ g^WW5߆C%\_g* E~!xhD3}KIoEnJg&]u!�BdBPmL-),!T!&8 t"梄N/C}:M"<ič^#0fPY`U/&ؐ(:Ru0w$c([7 F)dv`'*p`:BWKs;<WBfs.fPP^LMG6 Fuh9ki'#zPj�p9tAvsPd:}k``TEwoJا'!k*4{j 4:DV F u:̅SFSty؈IVf WOGթt3OKGPa!te2@a( іB@ eiw[U`Q /aRt6FyI<<\˸RAF|;ft xcCo>DSē! „_pUb_=`'CN]e lwaڂ摋h2pBe=Q+sӵ(v ɾ)_}Žrk1_Y0s=V#,DqmJ ٜDU_11i/^Q@[1I^ X5=y9cT>Px7̍$d!7BPmPn&+MR[68Gb^35',ҋ0"oW9+GjKjS9J?u74-\'!u"mCei;Od$Gӵ4{Ŷ|#4}y X-Cbb"6iȔzư "jD 'ƹ.&!Q,1Pj;;TrKi D:u&WBb&z %ٱ2\Y\~h�Q$+[_7H0=%�!SWb�K! 5͂PKV}>3217uq�+#Hrঽ�bV9;?u1eWw[nu]"WoI{rre+1j]?$v'8 jOU.mXvh^w;RE:2)?WCQ$@d) Ö7Zt2+1]3+vYbCtIy <E)齟j0 ns$w7q39 58i<"P4d=EtHO+ a@@WÌIίy^BW^d)H2*Gx<l6Nsbzy!\ L{wrQrROۍ+lhX F4fSW89`j)Y+9@\;0Oƛ\pq)?|3տ6` GH&iG.$F�oO5RLd1'eY:d{n-Թ'B<� �"a$ z2 5B$�ߴpI�g%K)aHg+0/ͥa݃5m*@uU ƊO`MdE? S-4$1Gi d ]%щ@v'�qFT* m.8&.4랇€F q*&0ab3L$(SH %x٘!<.|NJ>3㉧s.-#CE>␁"2\4&h_F2Kqm "B%7؅j}\�o|J NmL~+eO)`(,T\F\(% 2 E`n�"����!1A Q0aq�?VH 8ˬj -2R' `H\2<em#�o_1�$� h#$tb1 ,c&=X6y$PIcjO?a@m2ed^pl!8$he\%�a|OIv {&8l̇8IvT6�ً Ha(4|U"ǒcsY&Xl|Ag&`rG/I6o(*>`gc ' AEXr_H%zs Gko9@ fp@89. dOlrM$m$3?!Ţ2 0g7}'=:DBP}]O $a`aaem d0Y)d 6daı#)$ϲ'ݟf,<^> - H(/=채%J/IXD) On1ow2yM?Mmg; pi2c đ$D&1gT؃"%H9%hK25w#cR?1c~F! �'0d _~GGds@0=a5(r--xX[fKD>ì 0A͟71Inޖ88K&$D HcBo5&ϹD;$M,llO$):p16<86DmHP�T!ąNee_Lx/mg, @XE`G g4ɰ#3X݇a6 8ч]Ie'"%a &!; p8LD8kk�A2!Φm!?$6 ~q=:Odf?z^x iX[eHle?ae2&%Š,"c|B ^;hi9#o) ތxYxF_\hx&IVAd0L e:>K.(v-8|ә2~M 23 HvEobC2Gy{/MfY�`-YJ Bݡ! blSC+bi:bDݽ%|ID2rdK!r!DaFNä4k&O'?x69|q{icL ː!�!Iq59 #%&cCI6Tny`><#6Ar_{zaX�7y ,h<f c! xo9@`ړ'{0"Fpj}!=d(HKdV`%YpRI7<Mt y&lΠd ,tǨA`7N˼ y2YBsl]k1;e)-]a4`a,"&ES#*ćav9rYkmam,CHtl7l&jM,|l%ch.= pqrۤL BB%gOVޠ $#1/D^ tV Es=(}Hʼn$1!'Hll%a2w)fcy!c!d7M�) r] HG0d �ILa 1p�-ZJ&8#9LJJJH,Nzp~I !!A le뗤�Z_lC�u HdQ+0Z #klH8&,@@$a~$X1]8J#�tsIp Ŷ9J\8.Ͳ:}qM a&K "Fے_'$"R ld%'ݒI.HC6`, 2rZ3 Itd�[{,x#$<s5lBQ*x&~H� ?'021愖2;;(-J3 ;}OD,8$XxpF&rPDQ6z̕` c0o dB.I$*`6�V$[ NHH16,s}&, 1KMr'c'3R(Όzpt^'ÃysQ dɲq>Ij i`8y,!8pp`# B-m%XF6FSb|˿` a6demHlI ia`mR%@�, , 1ݤ&mp C?$~R~EA/|NK.B? HAAK5[n dX̓$g5m܁�JbD ځ,a!H`+xAVcxƬH@JHVܛk /mKX2 0H 5+F#\ }DÇN� IpYLZüK>}% ڒD1v> 8%F6y3&ϋܒ#; !9 AshH'LA2C6K ~HI5&L eEpՄ;kky.˖7;kH$- ΎNM2O$|y|.KkamOIF4e#yX#!Q%,_}my;?ddds7c�FshEH;hܠ{}FH?|?`,%?o`Yp+%.qX`$,lld IK ęcl,<'Ld_a8>Jxdi%2gI!a!MI'N3D IK[^`Xl,#vJQ4~Y�8'0F9`>K>Iet.PZLb,sӁw ~Bg嶟,rDf|n(m>..MܱXa cx[Yӆ&0ç7pB%$_.p(7ŨrqN �rO9nu|[D`� ɖc<V-Y[o~Kl &lH L*6ZYOHE}7XL!C`\`ǚkdzidžP8ˈрlIqi>>|{r 89Ünl".˽\ e?~̓K '/d/#.$(J7o-![ՃZBHş,ߖ rITZaCm'#zAJVi1陬lǸ�q˒d$kPy)%sɖm/g&dܖQ% `ONlvIJ̽ž<<,-$Je6]o1 FO"�K T'ck3YWت/g@lKp6a]o'jZOolN' f$#9kbbI&/iK&ɟK #ar`.Yvlԃ/")?#关.M93vކ,Ť;$[V%%cvM-l B`883,- :~H6Ξ;{C Ai9 7.  ߃P=A kĐɲ$3ߨ~q1m}!ZG_|ylW#,!Kpv8&� wki4T$Lo VE b88&1:-/qa I\.DB&=prQx#-編6O8q6׆2#{E?;f&I$?`Φm!}"I}CB\eV_,ܷcHLN p.xC +il'Yܱ`o.c| lL$XHV}H/0nOtBAxrbRL:ó5l|~@d$X2ő#g� Ƥ/N }2lj奤C%|"YYp/iu^}G|`7ZGIVe!%7%Yb9c-d2tE PLɁ|"2$9 3ò1j~E fK$dZ[ϩdxE[KD7V>CO&qr-{ä;|JR2_e�ykj\r],fD�@0aXɐVu!�Ci rvLɲ ڷ$ Y&lJϙd}H$5 d'0aɖ1?Π9xFM$�doG{ 棃nx6o_-_x"9^! ZRV\y%ȵeWa AWf(Nж`S%>C@d&G N[͞$,gD`XH$gJ$}DHKF-!Hv]I!:6!&C;ܖIe IjJ'g43:KrPa?$;.Jx̴eF>B=X6!o8[i1ZF8rcq6ӋQJ/'7DL#d|x c+$N~}X'Ke#&x%&d$Y,ĔBBx |I{I|CZQ>B)keIe.˒ܧ#2$)J\Hk}A^2I p_]52 ItA0y'rЄg ':I#x;[[ 9v xa $!=<:U[Bg 6s{6ibqoy[2ׂ�| ` K2\ O9?8lp>l&(gŅ "˲i�KD'2! e IFCz$1?r >s<D20X S㋼ $O% [=2A8}08˓J>ax[2|w ld>$J]g>9YY6okɟb)P9CKޘ!/dÐ dC3,ӻ!;䥩|C~~Nد B<I[rY �j 6y4g Ӄ/~['PcAdu./| - !|*cy)"I6@dԶKCO _x?;x1?$\f<Qi|Cr׈.&:_I$jC &6C7Єx沬9)jü'6S%wX^Aćl0;Jt�yN>a #>ScK%,sax%#c=l$3_Iw x6a奻>!N&u!&Z^p~K7-[?ӣ9<WeHOYsIsl61A}O3.r>A/N[p&}a%m6QV'Zt, .H|B 0lKp6`X ,vWaÍ2~e:N`6MN?&ɼ~ȡtA|!&3xO%cKC a!c,l<<__n?,oIVXCAy.b@ؒ$JJ~Q188?!mɄZB,g?H0"By4YOk6LD|A~6M,aHv~�_5~d2i<Yky;A/|J]>md3a{ /-%lppM۟rB݈qL3�x+&aa#$<Mn  p\9<^-g  .YJ-$glgAkxN gGKpYc5i'|M` y0Ӌ䌾962wB6lCdlt@ 6Ouü9�6wglc8o>:; lz=p5O &&<cs r7ZˬR>-y G^p&X?2c ,%|B* а938r!&Iak8W']u$"`ZG<l#E$g~wm<r_Zg奾o}%/O6r� 2Pk;?eR];oP 27ؚ<\B2^ 냒-z9+Ѐ#R BrAYM8Hd6T}$<(%퍐,gc X'ĦD?<J(`A CƤ|Cs&HD.H)F!"7 0[çS`z01"D mM0a6jԚ( ŘLDP=pXd32# _V~O`RO/'s:1c/!Oa2?x."|">–*rY5g�Zp0RC�0L/ (1K~B06uB>Ȧˆ9.V%{11,;ï6$ '%dc'dF YɌ; BNd'N~ɥ</I|B>Jñ~e\Ea[y #maBTePf?s]HpDԝ! Q/ ;)`9Q -KRFyXH .Xv[c&AA,̄1t|a#.I|!c5 OP$#zCus~͐>/" UN*rpKű=Y3 P2^JOl/g`G8c6=7y0"]S-xQ[HS6wcIjFKg }3 r/BO$d0v ڱχlv BI,?.pZކ1w~C],p|g5ᅷcvpD `F8B�*XNA6dK'j <!\JeFpׁ\)e/0Had&0$v?doM-a!~Iu2ǡ&0�=)x8Or |;>ɲ29퟼H otAei,ck2>_ZI"rAͤ[cc _N戼8:A XIKyK>VQ_p-y('<Yf00 0OI=&qDoqQ-LW-%v3+.Bm&aZGycq"[Ȅj/O`D8YBJB-$^TaI2 1vCbjyf|KuroGlI<" / ~2d"l񰝖&Z~Òv s$;q#gOe!n!/IVV` Y`˃f!Q((8g@~BO Eńӆm&E1g,o{nJ[|KDɇEaeXؼ59b!q$2�pajFM!v;'pn &�#XKa|?8>��q=/ }Y2#&c lp6ǡ0d@?dD82s6éC>Ge,%ِY䥄_M o6P1l`e2IB}ds`vyyÐ7[~ɶ="Bt).:|=8<FdǰYGs!Q@%dFͥ # G6A‡NY% _< q9xC%ʲ[=?e?xR^qчokf,CxGi>NRʹ%X e|^d6-H 6ŃYvA)'8,;?%6>Agm-arWm!*˖}GvKaY82.kMȲ^$HUկ02kR#iHyc^$p~MjԲNKKk �a<și O,,�9(ѓ"M<%,?m S??'C.%%xV>W O #r6Sl1Xf^ ~I6 M3ڌ3r'1edr/c0>1d_ha-'(G{!2I\ ${&I# 4&d4 qOoM6<u0a;8,qi(|`K?Z0!ضXF|E3`Fӈ07BD$匘@C,ӵCdD* m1B*V%~K'6դ?Fahg8 :C+Rʓ$zɒX�<%׊C7%6_*[ G`v${c(,#?q&v^ApE iƍg!:c c?/ʌo[ p}I`Y�98F\%zA>"1#)a×�2l,!L?Ӊ$q:q',ddfw=DZGxD[o,0+?"DMk=Tal9'dX 4;"Fdi)8 `'e#ao6ŶߐXp;/%🱹fs~^k?ar݌2~ Vjwe8`) 9&Ig}K^KHt~_9\%zϮ.i+B/X#&gL}!D3Y g ``KCӂ06c+p8 H&įae0R # ;챘\/KodCgŌ9>'m0;|Zl(,6@}C4c( bU3ryV  aH3vm''}WK;@mH"%b=p&!d{ {a!-$ԆFkI>l!�!Cx0ikY0-#da>Z xBJ v4o?mD -pl,>q=X LJͨ3p<a]03RP(G"< f33,%ݾoBH}|O�,&̌BHF7չ6HR38{) Cd$ؐ'j<}$y�ɒ9"M$NjŐ)䣋3;sM$v@a( 0ؕD3~\\.l5b@K&7 `U#'3<ِ!Ox6Av«iݾ%İg1 tCػ#CL *2 ђՌA!&0$g:%2]dWmD `0S&L,o{aXq H &ɐ!H1 :s+6lCc%2=>dEkEi*򼬞~7}Ϥd"jr�` >w?�oJσf:rb^ԇI͟# @QzDb7-f̔�`l$6Dߒr~CQm<o_{ C=IdTCՍr /fta1lu&M!qdJ]Yt0'3selḷ;okjRfI٥hK=HR$e3+40d [~VvZ(%,FS,׆#e�H1P%A'<9n0 AYşR{$4儃&AB%.^cQ f[ӞRB" ,-2 'k\༗.L9HgIJ’ Z/(m `~OY$bmmJ H!rerԼcKb~^^A,}Blp"B$?S%pd-6I6|pˌv|:z ?%E 4HZV~X|�0< bF.L\9xby ͐J f%/oGrٙ$7ĭ%5m"I9 dY9:ʰdHLw2@6A4B@FU3eb)x#@9a+! _R8o/jZ}@M#<@tkLϙ,8 &QldlFx-m$oxH}IGa &A,,?%/V%ٶIvXMd3Ά8Ldd,,C,#ᕾear!4ϖ�V }I-[}M!ac:r NxK5-d =@cP\DRԀ!E8m˥YMqpg H J@7eomK%ܑ䡕0`622>L$_  /rA'bVqYԄɲ2Sxo1$6y;�+&K08콾!AaIex%(rV)1BdfDEzM`Z:p aa ` ?# hW= 0#?cPD!#R>ñ`Dm!6MA3xp@d!3*!M ;:,]+Ē#.KTaeAjUo# Ѡih'3Tdrl%3H}YY^@'B>$+M Wu"L6dc}JSdģL ő  0|v+$))$PiC X:+-G/1!f1�6Ԉrbe|mN ">^$Y m:$�/Dc O. ѰodS山bHRrXܝM$V߃ lY4/+YZ􏰵-<Q*}%&0Ņ-of/a}GK2 &Sr@e& Y@ y&$ߑ/ ,c-9>H1-o٧�aBFdl3$K $F ca=Wb~#+ojH:ANXFN÷vP!.M&Iɖ0L*ő8H � ϰ 2~NZݸoSlu0?/)3U�yGZF FFˠ;;"#Q0�-#3'/WHK|_]>�E�hcz"e1B$@%a6=0r}#Ӄ7x8G`%VQ8ɗ&یz" 82%/ lY_m9. X5>FFd&O^ t!�#Ԣa>C2дsmC :أn2 m`Ŋ0 $$_Q! 126pн/7.pal <BX,S eqx{? }lv��%3IZF$ZEv\% &g >L #d/i @F R%�qCOpdl"8HRаlX[brt6iIBqg-667XZ[mpfKGl e|B V=BaۅD5X佷 dOa- ~Kˍc,ݟ 3=(2,2?o6@lv(Xa)rS`o8 �dC$fSB(vAr$o,$&@YijYA+,1:KB W̸{oQJ!!-d//m&AvW$gS|Iqlܵ?aHv ^3 ,d"N XC {&3{f@ /+Ջd a'Rr &6\w;m0g@mmZ8ĜC!E CL|5"3NdIP84b SA;Ȁ~^ ۬{cgO 8 EBoɘ@�dԌD ,n <6,mْd8m /�$do;WΎK i+m@䑐#EI,, gJI ˄:ɤpS克hZ1#b%= 1=k,,,#P#5+l�+a�F~%dZ_H NdHB$xZK%r_B=?cՎ $@Yc}Kc|>'Yµ8ˬ; ,+\cvl%ڼ M RhPd@Pr Zvad02YvtNd=ݏuǰH k._"ơ@1 [ $ 96 )(<ԧ3D9h!r mY 6ȋ}8nFIG %+&xZK.X|>m09$RRO"_hr5LA$dfNICAV 8:K] [¨� A ��bB O($Ցv `'#=/D_l="lwi-|H|P0KVA&</%,7jm? iH$X5ۧe1`]w]# r!!e6K.VE##$0oI``a.<Cl NO(- -$$soPY& 1ܩsV |)"a,x;\-͇_r KBƞZN'JyzC acͳ '0`!�cQJlCIvчX,#ptd)ڿr^Yjw_d0R[fk''&kzۗA*ʽSa:~/ JPY5ĉ116d3ZBq;}Zu6|llzce$ې孬Z:_vȧ`zC[uNɲcy OjY~u޸ APy/'[A,gvc:}XA>p0u1I!3 $c?crFMy#oRPvs83?RM<x,`,FMZ�A aq.JmЉxR@83݇ ȷL�gjPÚ��A=oZC&kyx>seK0}1< b<).O-V` %o卙bZ <! gSHx xNaqazCˬnN$ g' ld+%[Y$c^H>C4LL#!Dv ɏWrq3Rx=FL1 kX@mZ̈́H2 ԘJ<G ;r\R.ZW&JpfaЇoQ6^$; i҄Iv !PzS4X^Py1te'k$"3KN.K匵@X�?|`2d&q|2X̐D }-%嬹.Kml#9.5, eP[ĂA1�P.K>'; 6д!SƩ %X]8;k1p@3B�!8|Om^f\ L $kQ3^,a/.k}Cli.K㊜0ذd)zB0t /rv7J(pAbnjTj"A 5D&6]]meXQT>s:9kkI#`X l8ŋ ~ȳBk-#.X @d o{uqk |AZEԾY̽P.ma_[[XrKs]eW,X^% IHGހ5XB?~r�x}Pll1,t3\~Xd'}A&;12 \3ⳁ>2aH3[Pd5, O&.Zp̃ޯXa|U8j�>Goi>CN,lr>KM q6x,xoP82>Xc6�ÁdFjÒu0clX5 K^J=8} i.X=θ^.?3.5bbAe1 B~@;&pHt|sr {9 ~qrV7xpVd}G>C� 6jMD;0<\.&D�/tl2�-8SxBL!"A`Cke׃۬f[P3aA Ɍ>�{^| 4=t?l|$8]:9i(30!;k@6Č!dޑ}C 'I 7 ,$,.Ǭ.K92$A:.eZ`X?�oL=>JbiHr"Pk!lnj}IXDJPB13X2#"������!1 AQ0aq�?]>!%mkxD<cI�RKF`8iq} 2Cgi÷0Iѐ`IahA8Ooe8ZÌC:~d`ɧ[<jZbP<;)i}q!qbj}焾\%DjJf-A: oJH!/x9A,)&%F 21Yc!m|m/ k mGYs 4xmeS^ CH@B,1,aqx>P ;FతAKYr]Oe`NEw l,RLm 7ƒr4HfΒBV\CX'''x>Ag34"%P·A4:}8yKN IgSl'QMvG9Oi2vU #d|+eV8_\1}I#LPoP-qa,x <a4]毨,%�&!a ,81$Ԑi'qR}$8A>pqz B@`a0xd>!w?z.Ko Kr̖-is@NřL]6׀�㖜>GC# Fb8 Ğ፥$E,L#!&^VSzB;xV[/x ]�d2$ Gx;S^j\aXKXmedD#aԍq @3R&LRF-fnO.Kr>Hl~ KEAVaHP 8 Ad$|Ll6C$xp΢HwZز#d>s<aHva׺ڇxmVr\/c)%!!/azx;a,aX͔a6M�8A"HH3ω6$gԝa yc Hܖ=/iǁa&=$E]gg,Tl 9e&ci;>91(# 9~/IwVM$4~N)pF5daZ!l6M$xXHeBl! S ,Av<bL℺"zr*$�d6Šlo]GDQ@!dIg %u܇/7Ԉ<waլ*psM$q80Ò?4Ð�kkii*ӳ[s0qKMz&Ia$6ԙ*@EyC:*_oidCV\B}q .63%[X6 `, [9[ o%/x+&HRUކ8AdDDz;ä !ha 8?eɘ嬡iii ?9w 1/P8A XM$O#-`XSk$: IAjX#2Z˱KVNy tr&?2M$ǂ 9 !i Hÿ'gW�L}ImQdrx&0P?d-"gGCX8p2a z@,i?a uܗ80#FXd72psx[,h<Ƌ>0.ǩ&HRID1v!>z2@i^ @x% x}jCpAa.KQIw|Ѕ!p./D6HH,@Կ(axIk]cK̹p6Ieih^2leudQt6J'< <8`0i _d+wFKNe&<!ӈ 9~-<m~K F-ulKS7ł� Y-%~gQ͏SCdൗxj"ePQc3HNo``<`FbD} ZZH=,C!y3H$&21\6B&pR4K&N(Bע-m!6d[/W�lXRѐ`� \|IhB"s]y?ϓ'̄3 h$CD! ę% j淖7j F ?'Ɍ&5V0a&,$;tCcFP._^.JINx ,( Ar d FUdL�%)L8fRUiIvgd"lpOs!6,$2Xl&$9>dR]R$&6Hv #Ic}p09ml'CciG)x9'Zc6aƼЗ)M$"D$"ޠ]\%(_R.Zk*Ƭ39x~[R/,D.r9o! HxB&<\9: | ,̐d&3~qsrť 3LeԼd=/,ZXaxnzdV'PBK&ؖ]ewG|[^X_$|$  sIL८X .$BxKf̻ū�@c6PI YCZB<~ȜɤZiɥ?gHFA&I' GR*LxŮ1)v/T8D Na,zuⅈjYtƺn^ $^F jV H8O'Q$ðj!&O!h_�2 I18 &ÒPlI]�-6 :1mHyodk{Jm9k<!&"IahN:A)s /ABWx"HX-q9LB0>$=Hy0SΧO�w?oTa e+?%x A/9=`Bu~qR\YopqFK ; &taI3kd$$0$!B#l$<\ȑ8ü< .XXAG$$b=Am vPi >rC `ȍ aB ,al'8"gew)HR`W1̈́dPY dLgqaa'&M!B^mx$$s([XD( #�v�K).?/\?g KIBEx}p6:j$}Ӂ6!QP[3$ή |YAeZ JIIY rI}AyX<F 0Kpa I'R$CevaFZB�1 s1�S!`0bs2ׯYvM! `g,lxsV0$>AE ok I4s!9 :ga6C`R3$ D/s jC/x`lz9A1LǦOpZH1?x:B# XXZBä뇂DDoP�RU/e|gw8 3Kp>p Ȗd/ pkr�̓%3L]x0`2=&YV l:.K^!O, )%KCJqi< vmӂlLONaa#e 6Xü-aH=7K4ΉY1DZQ.KJk28AC/rz}a5p 3&L/Qe dgE2)o� �uKz2E Fl9x^M 3"ɓS{�a|p,#I"Z9!!34a&~tp5I ڃ >tN,@2'8˼]ep1a9"Y2v\%H8hI5aJ! CQaBzIc*064Ɉ$ɼ6Dـ>.B"Pm:2ԀW$�xo}M,As1AKYk)ueZ<!g Ȃ$Y5�$"RH%R] Rz!@eß t,[ZJ75IɰBW%XrN̐ɳYIĸ!*wXEYl#$^j s!y&luL`ɖVdGέe)a(J~*6 `0u%Ypf.\-ZN F-X#%zX׍NN8ԉ "]N 6 YX٘ti.<,Bzu2 !$!&Xu7q4$$6:0 ,G p7BSe2/l`,/U_Շ2\eSe)sm&6'ίx6D#."X| 2M18l8D d'L&ZOޮZwN!'󀅀C` I~ ?6#= 3ylaά%>J�ĺێ(p !$cILXOa$! bVէ$I6)RFb[ՃK~J gY$Ck ןW">p=#\%<&V^`޿8yȲaY.KGCu3'K% KVн2LLdl `l%Y&#>$3!` DyG BL8t.�_ GS#&azI%)Bm!ن=`2윕YY[Ij]e)l><<}$?8Rk!g !tfxs_Oa<+ma'�|w>KKKNDA,8o?$FM$?ނI9p=4i}O'-%Y16WgUg*ώ vUa=c䱳N2/xɼ$7 9 2D;Jy-ǿ]~GwNp#i{N'x+v?lt0 Cah# eOFc<�d!-'k>Kȗ"y?xӮy4ά&61x5^?eY [N {2gM!'ħ'$,1<m~q?^�G!D=cϜ&ɲc CC!}aW(3s5Po``OP^ؔeuB}l?(KvBI0!ϫ a6)," SIӒ%?dIaCHvAa�rɹ c7-v__ _ƭd}qquRZċH0 #a̰~LT!ӎ~KP�m0iC T7; >Z|Ƕ˒g'GW!pdX KBD!,C΃(䷊Zl|lL'Ii&qOIO+Ic  /_M|~SM3Mӈ3qfX@IKd0ؗe/8n^V1Fw.bq$^,q!Dvݟ_ßyN� m܄$L9cc 6A$L,yzYCCokM$Λ͆<!%Dlx z$Οϋ6+D%{1f[&p}JRfNX 󳒌2P( fg%ؘenL0/�Y?crgp)3ݐ,2O{-'+.~Mo0 Hi6bgpL$D._Pl$ $ܖ60(愌 a^l#m=b e[-\Iǁm"IdSӀm~@'i:@q?k79w$&0ѲMrԩBDX20ayc,#G`Ο 6OOAg丆NXpń8"lmFBtg 0pI,ؘDIŲx 뉲&0&Gw`+lX#"urv8=2b !>'LݔB?d<`d([}MR ہu �$`τ-g;;+0Faa,yN!Èm# ! A/gV2LC@!OQ!>IU2 10Si ڌv,dr~K L[N -maVIn׼[ FxL-7ݥm!LmF-TBdN[ :^B|sY -$x;�%6!bv/͐dR%e {|ɼ_2i&1#o\8rM 0j6d*mIhr,JQBF a<YhCp C #"0j[q<68[,HK!?^gk>8k%�Gpr_촕"=$OnC /d2F8=Cg]Xʼn?ב"d|BCbub8 &Qd-"uC[V #.!5ܻ$RIvBq= X;SlY62P OH;xdžgO>}&p}$ÙGr]dm#<Y)>219"p{ #DceLqO'?(X%-X�u�8 VFNxv!œ $KR0p 9iyX2cu6dŤ|LaZά]�2goaLXHd#v_͟ k(LaDm%w|Z! /{,&O=snƥ1^9Ivaʳ0 #Ha,)7 ڑI!}GmH)8!H%[l^RcXa^CƜ M?yq6#^ .0-؂˽[.aQꄮ^�GQyQli&rDG�ɬ-& e. <`D0/$68@6b{f_\lٲI4rۤKm RrQxx;i(t$FN|@)u6<:Dyi&~Da D 4dwd.@eBB C1H RezKHe0y \ml=ćaC6|BnL³#ܰH !( /Â,L!9V\~pU{ 7$ d }H')XtI!V0Opx.B}K90,[&~J%%X6�2-:rp#}_S< $D1'B-ccٰ) R"l-aaޏi&H]ر!ԓL#gr r#ʙ8.^r`ǁRf0%�f>&:/eO=^ey ɲd!I\� HldѰ~6^Q,gy7<Q $L1ѷ'&Rؔ|\a0vLx*A%&ɶX?+rLz6x:�s &[HA2Q^%ِǏHe|Min˶B"d(kyϮZ0_\|rZrᄤ/Gĝj (dJ ؆0gRca[M&-Ȃ00r#ĺ3҈2 a}Bٶg%'rFE[!c{d0|O&reJ_cٛ-RQ場#! {"p/̖x'Q SRܐHC<͝OW#0.O &%?2M!}?ZZ~t&ǫV^RD/ĤvCb29=!' [&$&MG �6Qdk�ޒ?%& r~Ad2lz\C6aݓ�m l$fKHC6ɼ.B6:G3|NgcWɥ%g86 "@x$M�=2aA8ˏLt6&}A � `BM-l Dpa-!w)c` | o i p5!) 1&7F ~ضh,ݱ԰ B0.Cpq(ɝ>pyir OC#xX;=D~u̓ G u& C M;6I',ehdž۳uAޗH1c=!`q2yFV]iiCXQspQ:|X5aFȑ,I rB dBKPs@3~KQI C Oa8x;-&{Y/'}CL8-b"XYH8$@CSd B`[^yA ў@�QdI`8$J,0ñF>pSHyX~�l?B5 PbtOX֒)F?cl|JM?)#a^[aHCԙ66 f @HdO eM=_2:ζ 1a[fڜ @<X0ArI;B ԙ37a\eK][Q?/D}a|8Ի 8͖ͣ7!O,G0ĽgrrԲ$y58`k lu#<T$$$G<;ݣ. #$e|y&2놛iC) xv>sPM$#~L1[|JԚYt"]緸{&=~L|-% i  !ӋؘҕԲy2I5o*ya--#l:^V9%&12?SX##kȄL`v?@'0[67`d;.!acIsR&d‡_7BlApLg\Ue.]A524/+2d&A -D<I6 $dBl@%)2Kk!BV dq|NHyC-ȭ9amYJhHg,!.G�9nL=Y[ACHΙ2X5 .j1.BH'ףgP,x$x! ]d|x KglpLm f0d0pax-dl*6GŤa$y#$dńnϻFǁ,,{y cIiLjG^Cc?GE.2OԵ0,!$26v50L-I6]gr~p XԙL}NHs!:!MN1!:@|ZKExuR۷ş e%g,/찳 ?e(Ai'Gc/Ka&LaZ@2pM:=c԰s!� *Kz^cP toIű &٭2<er}$K!P)zbEDc dL 5r&]qmp:{:rM&HCpdHqG3L(9BY6^YDLL.C-1fAAe٘m#cE{o6TF;<Ie[sxd d0ޏ-e]˭|_Su'#`F R?e/޸yEc ?z)plj&7$a^ i?:gs'&ͶL@ (ji}pR]+ Qoz.Qv7y0.AgoxB8#d!dv\ 6$DsCa$$P 9xK.œ28=:|}8va-ac6cg-a̲dHc<I6͸Z" c$u!5= È8rȒA d-}W)a)a|@0l ,X`vEd _wXD%RechoI1 ɓb c)?xvM!F8xp]H=ڥ-ԅ-gliLdif}FQIh %ЈRcH! 'ˌCB!Evo9AAa? XypFDزc>`ZHrBO l13Io!ÄdkE:-G,Ci'He .Ðٌ��ѳ),Lp^KQQVzIXBj3Dam_-@a,FxxFǜVK%ƃGaB�%�/@ B  0 s0&XdY B3W'm t!p9!#!=Lx1c$H  e.k-N/2 m8?e~×ĘgG!.&{>p5zr�C$%! D#cLt0 u%";5`تeFe{+" ԈNHHxG_!G~GGs0�A8/;}N hC5mw%A! 0MCd*S: v:Xmܗa;?$M Ȭ8HɌ2 !cϾ�$0F`i/hc sw1ID)D !$ cO �a0L$d[FSFf y%V[2iiof;p\X ܅"ڲxOOc6e!6?llcw=It;Ï�`B�C!C%Yy.Cbi͓zI-oLטD' %<#F:@Rɔ0dV67!'Yvkld;fiXc,fJm9'g88pV[-d*?9]ǨNI :÷ty e%!xxt-mJaVf\8- I$l\[b>/+_ͼňɴXj@YLjqL)I`WeeXk %%Yi} ,䄹0vJNRK+Lk[�G($m #}ă*0 "\%>qr`x K/�mȖ]`blpOV`FсHabe qZF ω2A !2'g6�`FSav :Ϩc+"(k>>%rUd[xGDHO`!׫hc6', !LX1ԛ! lԆcQ#6K(3g `-?-8zȐ͹=t%dXHH9ii(id7#$f:MpG&?k53O-cS%8g[6X d2 DFِX�eG$me?/1.! T`9/,fe@->q�qq &#!"j񁗗N Fha%"F.f *0dd&HňKa,2G�!A.=U JOLcQ=[QV2oCv@E1!#a Y6�|Iu-K$ii)K"ɕ¾elHLxgRx `6=p~_2A`t82FbDxߊ, ! &8ZA"))r. -B0&Hm$4ԃc"N�d7cP "?0�a&?m$A`0Ikk2\aJen*yt�%KKv ℣&sdA-'ǚ<Z` 4Z!Kĉ;2k)9)v$т j i!n6˕̻H$m0@6�I5vMa&qB3'vSz1S$gD,Id0PIr]dőd&<3ί2uD!Ba2s AdXjLa8ɲ{s\!"らO;奩h "Z^d^̜p$ sA2a,q8g1ZC1S(bc+/#fI̓H2V;V , ؎z6�XA~(OI&Vş8c6-X 63ь1rAk KXiLsIH.q@vd;>ڟd#bӂs  ɔ'p�?4O`L$Cicx`[vM$�-X0dO.¹b2@!\%�c>B|I6́/i Üȶ]˒j/%Cu"Q~T-F!ʜO9l4Hb qL BK K oBD]r@`Hdi7wc e;AO$}s $�H2~N?G$$3l$>-lXc<c}JA<3x(!|GrP$Z)/EI|L&j""$XxN6 @Cl6C`{ >�gW/4 nI'Rd6K؄ܟ`‘[1ucølHA$6D^0bI^ p8rx@L2HK4ԇl M1)Yd#8P.jU'maXrr[+a9C10`m<KH|r-XcbIia&@}&8/ɰJgrF 7O& &XXX@62?� )SQ$V 8�yf2i1\,ZA%\%v*5j+_oz.KYme,aK R ^$]Ӈ rW'b0(6M�%TI,X->-g c?d ֋P�ʝ~O3cL?+w?cjpر¬,SxA.v{[3wcx$?z&! exʰp&_~Ð�N '"63&X3c8Y0�Y=8 c$px$KXLcdLfi9PdFgEkk!A'\I l8%r�{;-!1=I>OΒpA'0Vr,ys6ŒFI9,-Hs~ Yw\'`&6M$:9)K%9I q&.q=ׇIp%>l|mV#zu6 93r8|J@ KՏ6c`Glxqg_-16<'1iPY:|6 ~3OưIe擏]e0,M_eÐ=8&[# zAoYl[<,3xn2g7ɝD^Pl{o( o>?.VޠD< 2h𼗳& ^%HILJ{8 X  x^^~-ztrz@l;g?xX`Asm%|%өg^:Ћ1%WeJ|~:I x�~V lD#Kx}g _0s [%%G#`B"9$2\%LDE wp'rx^>woqdDž P&K Nd3c2qg΃�@\%2Rw�%�������!�1AQaq��?Y$ݸ� 1x8Hҵ9({k $H=x2"$Iz^$&pKۙهAB 剴 84ok*HAY""ew (<I'pВ1PL %djC;e,E 1`)#p淕L8"Wl !.a&?ƒ^HYLőq _zKt):v6F0td{@<wO]K.,FX&�WtOAq|zIw#be4q59 +)M0@@J̻acɉQ zVO2CrDHߥDa CrLO&JE"O-RxMR [ Fь&` +)ڌm6rbEod3dH0$`9(,�6d�SǤ!d_83ơ �F@@ynk,{|V :*jyҽ NKh. _A 'r\"38 G4tC.g9PYD& L}TnDU�lp}8,`)a6{F&К|:A 7*ߏĶBX 6D8R .&(@F'%'d9 Ec BXUhH+pQm�B]<GTh,HR\_委¼e2`*" $fS A7!<RU896a^k 1ظ<n<$bţe5.CXhViެ)`wh3DO&>Dcu[+�eXП]aInkDs6�2{}8>W)@QQlǣGHP30&&5bn LcBٖ!hRȌ/X`s)N$4bHW^(^a RXvHB,Gl@`t-$f]| -:蜙v>0TF՟lcVŋ؅xh%d c)-_+$`NK1c񁸖0q'+cLJ$�*E L1| %'*XZf"Y,n&O?Be4eA�J]I6Nk 'ehZY0SP8Y`╯⛁A1#ihMpu4h;H�p ƎX R3LUYߌCc*s@I |b⷇A3}L@a*J%)md<"<8٨�Dm՞q#]nD %�w~H!Ҝy!"FqhOB|{KQ`KV`ʊĕ!!0,xQa?8*35WY묀$L5Ft f!0�d!',!xܪhAIa$5dL|ߑ;W0|*]Ǯ DUe<dvAA'PH)Ji+Ul=5¬P 0cMH\ƱAƾtbM]Kq�"GC� У֡DA$v3;TQe%/ord K@hPfV#K$,FQ2 J{GxQ\EI n&K(f3[#0MQW�/ǠHO;<@X#y=+uwۊ ̉gPuO0()P 3<0&JMj#QC)Ih"Xi}�s~,@4d9S6+!pn�OFźg׌�# ;^KRpQEn�Ip>IxMH <np#yBPKT^"%%x͢f\Vi50%912d5ϧkHQ/#3 𷢂Û<a DSlDz{HI bʅr<d1 7�=:ĕ R\/|Pg3ثI6$.]y�E8IAV9?j2Jt2!+ޱf@P8�r@ރ+| 8Kb67x+ :%VQ/kh$t^^Lȃ1°C=^0G 2YDV6yAPLlZLrz+TTIqbL+0Vfq(8H =ls3:8jstUH{-XYeS!FhI)ɠ(?97 =S'<5$yf. :i>2$. /V Q%H{Cp-Q1ʉ 9g,t&Ef�:)'EҾp�8)t$"j& &E$GDDbľn뮱g1V U@]iMDәXZ-6BVO�)1/G@D+_7r݀!YS(sr7K}2<KM$̋$qIfF{:eT}o &ad{D"x#X,0#%BH 5%Z(/GP-5'Q{-7:u �-+SUuc>2܋!*@asVt("+XP a#n�g'$Q?ah 묢tNV4Pk'b&lAB>d`T'8acW7FBxfUʛA-)4EdE>x$y3K*׶PId!RB޶xA4/f_)DJ!F(Ki-1<d0&ì@$4.[A`@8LЙ frBJ)Ldn\5dG$Ia5!  pli+�i7'341:t\zaL`P螎՚r)HK E{R &Yz{b`,aڢ'jn7>o*'M.6IglQ�^�EK$\7Y:rĊHCo'^+kO"vs<A!f)z"qN19,My�/@ Hy\MaǡJ g@ !8VJ 0P Gz*;oIGn}氁4'ʦ:saD,� c-D5%!&F?�]u&`:tl� ?8YB TPr8'lAg46 WFNR gHqh$1w,6AB QT=p@D 1  8bk7cge0󖡻cu\%q9󚬤AnLh6?v5ٌӝk-- !NWD)6�gezoDWhxiJ"*%GO?Qy%%JnpND8sNv⣋zMVb %orR^@XWoGmWwKvqX$ʨ^EYLMI˜9kb8gg; -;W3#y5%io`aQ(d9 j zrƁb uSFη`X\_ )\ B%qD\+8Be]xN#WJ@2@/{ˢD4%V\m-@d�1P>0(@ h VtqhNב7s'F!L/71K;#pϘʼnDO#c(P~Dz8[RMN:>f XUPVrULVC#`|hd@ӹ(`Ri YH<&'\o5 i0SB1 nT9$.p GMrc .E*OGYXRG{ܠ=c0�WbEڊAwQ(AEj3ZT|ibhy!b9*uy&lBcf^~22UyY#w u{C2@LB3'P< pXUF5 SQ5ANb`>Iv1*&WoFXE24-</N00d!`&'Dnj*6G9IPD+~ӂpǗͻ ]AMOp~1=FHF[oP%KgS׌pJl ^�`⇌NM+�:'D0h (o ^d�2F ‡N�ű0V-Hdgm47aBВ..R Rڸ̌4 i@Kⶼ !8(&\96|I*.5i; 6dIU!<@51JI4ffBc[++`L:Ί^T%[b6%߾o�%;!Hqd]'Іt f ;xK Ǽ}d)D,+?gC<>�i!W׷4Elg2�DTGd'?oav@WkehLǃұ` .Ox`9qA2~ 6j%^T 2rUT?\4`c)!:O/'E} S-E d,)6/<ǜ Af'FKp$" E}喌\s�ll$ ;#t`ЇK+Cp!qE'"k)^:aq*zW8H"|aK|\1؜,.A,X40|䛄!\K#A,XU//Y Δ^p`lyu ME M`AU]>1d/'xghpijb3O"v5e=$)f0~v<M*#{W?0? S9*q)%-32VLsC%I^NBJH!$˺@T>pmE)3! !^1LHD>cǜv~G$ܪ1׌z14녢r 0Ҡ#,6yPD.bA h[Pܖ<�Jg:& )nu$X+'@>yM Kgs1 I L/Qu. LT$?85R(;�7xQc# W�<mhtÜLo]aW@M-ϙLPZ�]�0ۦ8gD lDc%5KH`*G_4qӆܥD5y!.hlR7Fu tb6{.J4?ZiMC;^�^ x:!IfqĪWKG"*q<%7=RBd@�kCzEjn9BY&: lSha^u⭼ 6PF)NU&ZDXRP�EFD�A*aAL{)ۈ$~A6&]Vx[4X2A<Ã\{$0e&8=(ZLȨBJI;q`"zU&9 1*Ipp!Btό-d7_HVWη>FOCN2' ;Oӎ#j7[GX<YE(!YXFDL/͝`hEp R!b->+CP.a#I36B6b@qZ\cZ 6_5i$lj`:,Y?+E9i0%U30T ,!iV:qoIy T3i7.M`U x1m<&[@&'J՞wWKtaaee]S zm5"4@Bu@A 1$d޲BɐY֊\R$S7C@QNR)ڪE 8 Ƞ@/I6Uku}`Dߩ71-81X*| C A{>/9;bn6ib]`pxj ?u)en06vA i !% K7coj"A.Fz+'TR/`,p"``I5X E#&iʕ) :?MD0 I$ X(!x͠@AN=HRPq(�g@JI@H3G]bA˿2 \Q`s@//X.:A`?k&3a^hPf*q g[Qxƪ#2rr ׌(6ֲ(d)6ı I<�y; @L1$3'A0?9;)@4]1< 5O8D>g�<G72N2+P-. Q!]3uýr8 99<@m?1pQ묈ǻύ6Q D\x_y'1,N4!Ho6Ju׾i#&*xº u8�Nkdf<\c҄D#edIg;Y)jӏ(N0ئK%*;, |?Є6fǶI2V&Zn7׫ҡ !pJ<�8}kf-ug8.jDY \j{008&ULd3 \y gph _eMUG޵ LIx$LQ0M&*6G 5A@u̳ഄ&œޒr ({>IJf#U MF$\;S Ѽrĵ81D9Wq-ER!ĵ-(BL]1% [5| Y _pAcL<?7H@sEq8W|FYNzhJ(E- ؼ^Dە -8;nuT,rUw9$TiG ;X qTBd,x矜 Kq�h*hʂsi0 -=< �H2ٛ*ؿ&HB�ZAVO7ں;>IIZծ&c91Q&|]y0"gC/]d.XJ4lzETF8ADux~0S^4}b|dl.}Z6X|73sn^NH39iL4:F0)abZEK׌&)aOUG $5Zg(0ʦJ`]Jxⱡ d%8TudW-tşqY~"zkY2 $kQ I&Nơ6Qfu,1`Su+lTX螣N(Arb"Ư9CW1^1' OGfW`;6(8/dm.ɰ!K#HpRqJA~GR:Ƥ9C[T� 9<.D g, Q}7K+MTYf.4eW Y1Hq~1-8 \%@"*Ef=#)Ak^b ;# 5'UB&hwf%" /xzr/HZA'g;( ߈&`fi�m A1*qphn:nK>@@# I4",׮Qd'ʚF8?/p$ { Fۄ1o: bP,{<Ra_~eaG 9B x '2 K|`H#.=  $L n( RcH.$L)$ɗjzqR@i!\c `5o{|O0U|{ BGa \d�1paMx'"VTio7FMO@2R:,YrVcZ$%:U[^pxZ:Dt7떜$..x@+Ȳ''v\E?{Mz{aMn%H$ 1ox*t/`ƍ;8Xn7;N9I*ΣB+mtϿN+I)99H,x2 L2R0�LxC3ߎ@;|ڜ JH. +!Ǥi(G5?f%mSw!KtWh2*uYqq`P32Ut)ΛL"Tqa4d/xqlֻڔ0rkɁ|o�ҨKii[|=D !~0‰v%5޲R #|D;1�D PSrZ 9xQ3 5VfYU1@�PGT� 漵z3V R _lA@ _$R$؈|e} xS[0.;А0D%\{>{1ZCI@iT>�#�M&K L"*W3Ln2w;({:-BS:f:Q_x({"5>9o-f4,1GCps8B "c q)am 醯ˈLαvLji)Nk\�[>jDv%k) YfL5>#d0 =-z~sM5 Tlx 1Ƕ<#~|`-Z(X+!XϜE]┦-F ۼvV%}/e9^hIyVE3w SǨJz!͜ྉ)k:"$!yy%JM5ik=qMr]&HjgLxKE7_K1ߦ"VU2q![ O#Z:y!Xס,ȼPH܉D I>h#NKDQ9d~ OJ&$i�rx(^PyXB.wb��fgఖxBM ~Sm`$�T9~CM̾kǢ1K((�;J)L< k YovR+6|5DMp (b|Ģ?cZ2z3gIO8n8'E<ˎ@sIQ0gͼch&ནsW SfBI010c�FBC/:Q0k`Hw$4 C\hBo9�T.f @(&;+lp1$ THYHymDT �Xx׌C8�GxŧI<~JYFS4WGt1%N>2stfJ:ւ6" &?g!- w7FP3IH2jF|sP6&h#|CN۾ yA+7=` #5xB@6Bםp<0,u%bB7*Gr8,HF:?%(RΕ͖�*E%L-bbzϥ纙1)+TJĽ7  Q`n59(8Jvo�.HHEz�YaL/<㫒#N˿-5~/5H70nzq|B)yRpC{q]l޸;5&Pubd`<%]1Z�USnV8 }Ճ6HO8`[S$6aUSuDCqzBqs9!VhRI"SE*bM{shPhM&ܸNPSP}a-QU5pw95`܏XbvN$M1 $u "$�8f@ H 5=Ҩ{xy5>#@cx"YKDEUfzwܮHAS=>Ek]"fƪ9?k4l[Лy<a((}|d$d:#TW(yXeV8@CXg)㬃C 0*rznkq""Jh"vxکz d%L:Ň Pe+O"j]sF*[^FIG ) I n"� >=0@��HH.$[b=0l9x:\8)jSm}cɝT 'Sc[yca CK(<sobL: Q\ d YuԄAq҉kӜ]�d?\Z6WyueGpLO~efs\|助$S'=u<9o1)JXDtc b0dýF[6GL暥"sSXH4�ƀ(k8ɢi&nn1xD1IKvaڥ6E婒mzy \j&&c b%?7RH1>& `: P,$K=%"#\Rol2Ģs2J!J)lVu]@ 6Mj̤~ Z&y7gy5…1"ӿ\P c R[Rrmu>#d@|0 0SzQbG ,Y/9nR(77$"G&)( ;G<VaዦGuM30]:J4'cC<k"Y IR'Lp^k h)X䤆 >`-"{lÚ3qv2X]hIU$:N1 퓨EG8 _ʄ|XYdevFL7݌@!WVFJ q() Yiq%߶MU$ɔk= jFc0a:TT3\ hF8J_+6$v f u'B Ic 0@)@y*fv: K'=OYd�I/*iS^ƒ$D FX 2Vpk)lSW: k0}L #Y&a%1/A HR~'x,�=C_shVN_#k�1voKQd[3=5`Zɸ2B O_D:0{B'�>*وa!R J$#Ģf2aex wG;7&3x,)R=_+3#Nީ%&BvjvQ3.5M8ϸ(M3A` "+,#]�x&͋XRXt~2,5LWOx$b<N<&q(J0m? @Vz{b&OJP@!`GyDD$5T2pf20Y8:aN{,)`4Q*,f;-�5x}8~U%4 o ֔GIP(w >x@Az2R�9"+FP!ǓeUq>綱] (_x9d-"6Wa5ߙ�X|*ٵm8=Ei;ޘ2<T(m wv3$Ecx*[f:^2i$N4_a24jIu,yeX @I\n:e6G11=z6 :Z1,`žF/r%8H 7߶H˴?%nīRbP\ 5ʈ*8;7`^`#Iҫ\`- ΰ0Jǣ B Ui^2*HG:pB~;p}G8Qg `y8NQ#Ke^yi�JH-�VDr~r $`>Gx۬KoW4f+e5\aq+Rϡ3�p"Dvˬ U&:dB@F|AiR Q;A=ϣU4:@"{;o醴 sD"C)!CQ~ $e27N7$n``,߬&I0 WݥRL|%oJ€,$9m^ rod> �Q5'qH !Z"u9Blw?Xp bx!w;1Ư Y $hVA+ 42@b9捽d Pz7֪>rx u:),L]N('qd„Z,$Isa]qm!|hHcBѱŹa J`Z"fuZCIqB5OsD =eJq�"+2"<*wBi*Ӫe(j mlEH%IUb]+xLɱz�K<W|#ILNFi n�aH T;+gtB!o � ]{Y#_,DwD?sq1X!�wqkS84@Q�I`h ($R%q, 5[ %OD|cGhoz 〩�1XOLVrF+&@%n=213x|/LZxr Иܜ्R]kd$e.0BHmMV1@;0]@7 Mdy"j7*a%!8Q$,:|cs&9Ҁp,3#)%b%1N#@v^ƣ _J#1EqZJKPN f 3"~&0E�r#gz*O?Yw0~gAQr{$3=}zAa6|~^qX`1Tw nct@ MEx4<!0@J 7.%Tsms;qŬ|ioJC.Ed&DD!>=̀X):88¸bdqXğAY1O�.`ח$Qn5*$c"pLL8 -szAéȐ&qgs_$a%aZ +͋5f^<hBU|"F @~ , @>r=a &#*SD= �L<JAD\D-0 Wcq6"{ǘT3D�QGgHu$.˹]_TÏ96ۨ hC#_ᬆFz YLi!Pg[9Xt\CBJ= jR�ysS*:wy=}bź;B0C\y ˱_Qbu,dM&큨pm̤Tba8pBA/�3ɳ"`mP+jm ZZC^@pz=?$NVfI,ouZk*!P ˉ|H!&ybC}Jsc83!Ò';u?3悲y!`U& /r.Дh<3V҄ pB!+{! &A'MQ/5b-։5wF_I==r;r lTO˪J5;J!iQEN w ڿ_9Kͪ|m 'PH>@$yPO< [lBOD"IJ£beA�m G8.1 5'jyy  (Ȑ `o01/ I@PDJ<CtϜ*<ᣖ|x 9@G ISZHZHUWv=k]c1fjIL=P#@ndfCZ҅#b) U26D8z$k߉|J=09bRAC�kZ\-22L@.b"9d 2G0N#11;qڴ]'PI~r<6\A(*0 85w"znj""O0cMdLXk6aeqM7'*/8퐓Ϝh97�!6O)SoTJ}}TGb�SQAǡxH:g}d4 !Re%j9)mWs]�C瘝ga[ /[Nj1o8*:m ^J̅-*u 0I,վ#AM!̌^r!)B*Ǩ,P'=:qޗ%k:K[0"4-I#} k=2Z1tu _0 Vu|FQ ak'GK({="OĝMPPaY,_"dԁ$PTg{("Kc-a(45rS 5%{w-v$ᓿLDDԜ뎰 _z\Q^ZAE$oIP%ǝq9+Rz<} +H:96n<cRu Kpc`B 嵏Ll�2w!m|+L ,¤y` 1ESyHF,go 4r;nNQS;5 hT`6YoOtu:rFO$"Sd�BԞw DHTxx7YȔn�ȓ>;(%U{9Ê4ɩ9 XYֽr %RC1-5^PnG8ȔU}qN%$)6"Zc 3`d�@8̒ɍ'XTbDOr'o&=q8;M{eCt9n}�H;L{"cȮ; J8"K>_&;?2*Rt(JmӌPbfMW2>+9{�pdi?z#;@x턈WGP1~"�́PE4FMTuɁ|$fr(Rj9 ʃa-5c,̈́[%mY}94 MÉoz㲶u*�:M2Dx) n?ypIAb=9 ʈQռx�K=EϦ. &t8 ;\8Cu'!dMabq@s?8c%"0'HS.aЦ"XrI1QI1RA:1%`VMc�N"H/<I@"WLalL%@h`!0bbd !wn0qSAgSI+LZ`*4K󆢢gџ5!MS!bD".#mYu\ a43~ÑL UꬉQM$f`>c 7xT1}#4E#Ml)Пw 2$d],TG|e(d jC鍓bW0{s!OBAs2u*YՋm7ۋA`R5cMMū6E D Wd H:BF ⪜8!aGIܗ4*+D;h$7<k`dGKlj"@ K;e,$^#Feb5'd=jۼ8],T0109V5x&%_Rw _  I(GLt$B!7&z3ح{Z"2?L<D" =(6x.j9�p\*h5AIl<>m%B_<cz@׮Ah"ArK!xQ\Ǿ%F׌4P@Yxa#qts X &bZ5�#Ɇׇl1X1.Kw4m)<Ixnyz8$ y x@/b$hT`^pt!G#}z12m6d1cjIsa5fb>Q'�??8 D ZK=5˲d]r0S2AZɥR 8y",C9 Kerl ظ*x_f讣A,\r̄!qMXH1~kF%ItO_]Lq7adUcZ~(q)$^"y#٥Xd1DPfL 0s$_% |b3@q5;PlgHJ08҈H@ƫό qUP`cHELeH (yK-TpI$6 'eqKm0Hew7l B!y{ɉ@ojkHe}Ѓ%38v[ !~w85u@Zf!+iP&  KC&APC?X>F)̑6"B:pX@z>&b|G?�vO$ 4pbfb#PEo}Ru0ro$%5zUd?eo40D߮SYF ݛ ib:mǜc$ɑ^�b"Vp"+0p1d-aH6iAx˯xm & 4Dk1Xݾ<A*B 3F vx PІd+di'%|ܿ8R-=arf,xy)=_}"%[_ۼ(IM[c*9Zf{l\(,� B'2âs}Gl (B+ײ-jK3q;"mnn0�MŤ`ٔGE ӹȶ_#:X`�x&AEw:PRk[eLke*EL+hZ$ʬ0/ Ůt % L�DHg0+fBlf!WNp͢P"EO}d@w<sJQ#~qP (ՌLBHXAH 4Q,W w"zI6S(ߨIȭahnĒD, ][&"#)<%)k q,)a:.cyS$�EHa�e=uLP TtNF5g)9u996&R nx֒JKYdnœ;'qY$M2 -,".. NZBAC$CA# c-jIIɅi}?8.+f:6' XicKöFf1C{�nEJ;2NlHǚGJ {MY޲_͡$Lc/aaa7txMvsÂp}^0/R n]{p!O|L�a* 2 5?XņJB7ʒZ:2Jч8}$$G�qR & yc\؜[1%e/PX,b5C zM*#-2< P;9ʇ%+:;"Fd1�]YA1ܞ\,?тXGtJ}r8Rfg0wFF�aJ8%9H-;#Q:u~DOj0Ɔ@ V0܏3'DǓIv>hI�op<gx7V(|qE-v`fox8:0@MpG:#h;wSʴKC^p@pL5Ypd #HiV":R`B 4REC\ d ]"iJ"'_f O=t o @@H<akH}qvaBIrV`,%K.eF&K&㟓B%VSJ缌ȯF̒&(R恩1J󗳑ʄw$o8Xb跡G3ߊ`Tb Pp D/bba^@L*u :QA#BJ*MlĊpFp"mKل4(L$ӾMwP)&& 6z렁AakZ7Ȁ70謈J%S1g=dġU"[CǶM@u 9}ɠ<|y*&L-s=V= +qz'VwYɀ849  Im14> bA,.n=1%TFZD'&G!I;XcFߛC)"Ssa (qZ`#ED=:Y!* Bq [',P(S& P.~10#RIHƤ{O>"]K&ETks8�WSsA?8B фڌ kĂ@[VaF�?9@oG@4j00$*Z*<*@qEfظAQ LM#VJ\<d ؓrk$QkC 0@FP` <wHI}`(Qcvx &NM[jLx!:;:U :RYzWaI6((.f#Q޾1X0`ca)@{)<~2q 3n VK *E_x)OYz!6rTrfuaJ_2d*aP™kxo4wo*ljJ+aA.' Ƥܤ:DZMV(}Q*p  DCj^Ȫ�l1BE Yc^#ةÈ(AhiKA IDU93 zMI\jG/\@+]FM^%oƊbBA VCdbɵGn �T'O D�˞7<xVAO!H̗u, &!gWdU59L4EKuX�1bxew #Tu*RIǘ }JRO n'A<>LTS7}r2@iz[ IDij<R*+'L"iӈ& e9TMž=1I؞X3X-rk,28e D=j1 tfxX(�'qz0$Gؖwټs9KsprCz%vw8;t7O7c ]?Zk!0 `Jrj x6B@ ׹O"dVLI2O`V8TH$jffE6q2Z R1͵(&RfC~K v|NRb6BBw<e_^ ^ ^&WP"=15(Њ.2tE1h$EұGg!J֘|O3xq0g,j$kh!t1 12~/ 4 '�{o1L6T"�Mx° f h[qWG_x͉"!w̫+_q)[W*bT޷3A6,~u2KOc^B tOGZ$=T1UA$F= PhS12۟8M 2$A.좃sб,Tx|�> fKY"JihK#L7oY)'` !V{^x]桍ewD5CX/~*/#øb$$#4v�4`|#@BcN1/|H2B۹)_?.` 352G4O1Y fĜ'9YLIP`S| $A8$`M'k@ ^.uc7B�H CW&dSG’ʅԬWY,t(-ك*: `,܍N E(1IbCeu8xS{ A;xM"'|q s_KYy1Xrzt8}pm֏Lt& u\$OLI$ρ2=sS/"Os%UAX(iC-2/ƽ(<1�8EElkV/6'=t S0Ila_N=:E3/DZI:d& 2e1mlNy19q �!<.0PP@*LGK{wfHA@JÔ5$[$52`RkYpQ*S$ݫlPwy:fKr3O2 :>yFhwh86\8x̶u$�(DFO1Zu!nZ2&"98P.{gɍ<dQoDP187O\h?,P�z8`2}F2s? RH$9Qp t[n#h5x;=(yr'("&e+Rf7ٗ!A7*0O[-~MjuT E|T-dAְDx7cᆢ3\(u˂p2-ֶ}0; ɘfn'|7il/Z]de zYW<vMA`wHAuLBèBT4Q��6"(O~ea<@ &8 &AV |NLda3 : U| fjd)s<bz`.|dP @|EG(Bm�ޑrcTiuae*�>S]0$}qJ%j*C-.D#�Z|g hXL j j1PPI@`8z N(M:�rU|cO#o6s CLpp+*;ŠHWFupXK,0-uG1#7dT5Rd j}.ob f6 oU"H]`E ?Z$ 3<m, 8]d>"̪ s7qi!�C؜M;F*|�I/kƌq1AE$Qtv}2�xhKHJs$!yIZS,#(0kUM !,u$cEJ0!*1gMx�)IB\X5$@tL . @E!SDHI,a&84yITf'蝉,Ӻ ��aThcxD o�,.1J+ aZRWL 7+s˒+'8̬&Nq2V º=w!buYe2)]o [H,;+!O;0tkġE#˼,__t DU Tt ՀC{ĥ^.2zq~ (-EPΜ B@:t%P8m"&DD+yxaA/PQ%OdFKq|ǾS�4v7YSsm{9'|)x;Zee�?NPP<dJ`S-笉tja4j7) (B$,]4J*gE$ak1s)ɬX-Fnkp/޹jZ d#ՄDɞ}qLu=(ߏ*(2Kɜ7cXsJ!T YI�جy+G*ᰮ-*fQŬ+ }F(+6#zɹ+>#mJuxX TVY p!6XY2y'*\1 +tL! +JYSQx8pZQ+3YM"&wPljEcn&(ͰXDT`5D6wI׵{ 1T|`b rGQ08͊^d~ 0P Vxri|8*I/ k�I*6>n7a+Ƞ 1@M` y/w3w"hā 5& P6"GvwU8YPE7eOy6F~/ڬO(xp_7={; ׼%ƞqM0[,WshH 4kSq՗s;~v OCU^$5ˑ+>O*Wv1VTEG 1VRtpqǼ! %1 !>l>'ysPi^7S xŠ  vɾaP g$+[P݉mpli: ܚ߶-TGai� ζ/kr]25.JZc=5:`:2g"1 ԅ}6QbK߃×$@" kp9bPT\޸F SlDbtH,3}`c2vUmXh|sC@KNCϛBEJ̱6n4 WpÃ\ -B$` Niڒ\bW@JNf�kQb)?�$X&s=j(Qpdg p'Me;K{X<&Աy b:fȫ [6l?XU2hn:7\!{dv xOB oAY# Ib@Y7@ưTobpu4(88sSWw {XH 0԰?n q$NZh,7 W : ֤�i&.DA±%vPB:F$*Ld�nM>;Z|k$_*Ƽ{`Tq)-<|϶9|.2by%&̈́/CŒߜdwk�CtK7b | T?C./K7Dz|FR|XsM|P.J,mhrn328bFf\a)-:(sSN~Y а莝;eϪZHAR/1PXų2"OP>UwqK`+=Iٯ ]@fP!@v{`%G0;R m� Ĵiۊ)bKO 6QHxs8D\3`A9S p1.N B-mi_ekTw>aXVcИ�G53B7kYFR'%`BI5Q]+ؼB (L;w摆 ZJ7}"q_aM)Pcr*5|a AWNQ{䮣g86s2+V4c H4ыk2X+);@#na0 %|#S<D9=̏R^�rC60!_9�E l bAMDF J%NVBj36A} r%@H!Eo ΄=+L6kR! lmc ۃt\ss BM7gTfn!RRm* WpK\HulD#S$IRo ɔEAj`b\^  9 CjMd쀌¤0<}AV=kRġipjP6k~rȈH\u& (ٟq ^03Bt6` ( l.G]7 F*=+9Tdee .ձX ԡKGd7큃w*q$&cd&F&epAaD x5zHUbF�^ D�Ϭۀ6'fr>5`5 Pa!4n" MLBm5f0<63xhͭdhL:yU Lf@4<a41fVSEL ;˝}c*ͷI K2d@aĀ&y =WdHտ8KvPn d id Z }q ,NQMK\ldL_xK0}L4�p ,.g{˃2SӒmʤ=qmC0}$ ʖz2x׬ *\\N3D: *FEHG8Y)@A?\㏐f#R䩏82A*nMm V .fH?@'.�VDn:uh# =o1<d KǘBU_XKOHϜ�ey)#޵D9mvqBI^KZI^�jVrOƱ7*r^Tryq2E-[11q:[nYI-?cJw. h1]LOG2ULD�lz 3J &5\YZ}QvF!,C.. S%QuWec"2.UBIr0s`@[cCR  "v염eWfܰP+�1cd<&~}! W=锢:j8 B!,-45[b͕ЉljujZ@P61 N-rfwc;PU3YWvBC&0EK; @Vp}"1޳g� 3&BtOu`H/RaAUH23&Ea&^aC 7dI:Sx AUxq`@Xa!{rP(/&`5;47X hCLГ{WthE~A8g\erLpT<va} s8P@dqpNM'I &eH.9^Dv+Z\,)HmKTk 6{+u:<)i` l I3mGLINn[jr!B H>׍x53 y8 (`I$(((u?c)(L-�U ~qչ e~RLMN\azC9@8II %s9*wM#iKM5x $a��b׀\aH>1qCI z7Һ!w $ƣpH  bLE7fZ`ɼD N�"} a:w ~y*Njas�yS�1)n'X`3`̘NcX{I#^9a``ul!q:U xF�8G5/t5i ,Q4M4]R>b2/ yBجIV}pR5C!uNC4ș-!"bRGpP|a xZ)+IPRO2:%@T7X`VJ 28@ڥ�]q&gb 8"2HL sZ 4 CDNBI=eqO X{HWHGc73J88I #W2[h,r釗2<OTD$N1ABoCOxwX cz$\`Qw$5' i,>2J1빼u6|>P,x.Ğ+ɰd8(]m%]VA4C^&["LҍI x3CϾ&BzHϴ8=pRJ�\<;~0=77$�K'Z `ws̝Mp h)R!wadp I!Y&)"^U,z0BQ]|8mMLLLĔ(�AW-62Ył.+A^Fu"R "2J*!m@T_)#:w�(RKjڠq>0 AL\I5Px*xkJcDڪf2BD֜c \WR%qE;D=b U4̮dI ZO?*7pTqEq|K܎]#<zp3m* r6 { ZDe@ 1A4!T|X^ @,[gL�W2C+yDle.I#=b6>K \,s0bCu�(h߄ 92JCi4c$rCZp 4b85 yt 3 bxSa{cmt :B{=%B&lXY $x2RJHȝpQS~cf'jpP-NeF=.,!2pF:_\4x GO!yX@̢=�< CHe~<MHcxG]w鰪2rU =.NF=>=JZPY6A^edxQs BX'V$K:)DBi8ب"|!zetx*dĶ3ӅT` ɎI%.N5b.8GI"g%([pk\aȈW$�>qR7S>Q N ǟ�DLs Lc yrj+ Q%|cG2Q<c 5\~kT "Q!\׮J< eTvyâr�x@'!SxwW;4ʉxD5.LYס" O@S;KL@>\4&dJ_8�ߓAp>nt 0CbB(h G` = WWf9abw 8͓$Nz*8 3ltO7 1X`,"!O ؁iu`S=u6IN(P=VB!<d2^MyLp6wu`E� 8JW#;X䠣7@An 0f&Ko7C I6�ZQT=5iT J($Ӓ{ Ш|~puGף)\PYTn!,c) 'ap$^5Pq䓣^{ۗ.sy'=k,ҧRb�؂ZKa~lXFL}}fBB}HDy|ds22`׋iK6|#|~g&RRb~J  'iFs#I 0+%AXLI놨VTuNgL):DctJU bi�:' 3N|Dx@T3gfuS '1""6R yen']_0 cn<b9hQq†eDlb(hr;~E KY0HJ)QTD߮pϻ|sb$C5:,"7+Ym1 Nc*Aaɸ=QoB@b Pr֛.Z=' 9Ʉ h6[£ piw�5u;„  $m7P΢~1[LC̗!<ek9Oy+p$=k-ljFH;E"O:D|ĹLDGYb^q?X!a 0Dx%F"ZxĪ:F &lW^3aVc-<`a=o$InJ/&H$4s3cM( :.X (8۽L?;�ykZI2 R ,�&ExcP'$One_9h!yVYϛrgh^j!u < +9Y-Hk@:O#$3OF^QO%;͕;BHa3s'53u<�XC {3lOLaֺ̭4@?Z�`|rQ.>1 8ˤQ! F(#Qg4(H?IJ^A2H%[9GzT“y"'l25=dDLĢxw*:# '`z$3@ѹ"NO90 ]+g.m 00,+Ĕz{JcQo8(^rOA022$ )G{矌nhPXI1vFq,b"{8$bI@;yc)#GsۡE"<|NX H|w>%R%cfcB Q׮CĒU!ssKRPM()0O>s~cY$IZ<~1NIH� /1&.�yl Um�PXm&`G|.ANXQU4SKMOh;dz&+cD<b4<'61 Q�n]Pbp&Y7YmsVa.g{ìaÅ\!^q‘Q,%eҰ|! zx k`1 ͡3>fpvg<rLo1Xd\Ҟ}}82'DXouR%LNF-rNXdX�"AlSnkƎ iD�L DujeЎR9 uB:y<33\@FLZnTS4XQ�Ms.טZ@8<d:@JQxlqNřvlZPfIwxkG,jyȀfF;�& Krzam4@4(0oFm Í�k(xFH.X{u'V(D:mYZ;ߴ h57F]&( PQCO'{ R +$5FӒbHha`]ѤnP%f^_7&fʱqe< ߦF 4XJ Ԥ!K.0CI V. "ue?1Y#!:lE0`e#Ä B�9XJNkXg@�/l@"�R!g&(g2v�X `$bCqRTMaaxTj`x /1ϜϿ R(|5Q5#ƘI^[7̆}|ĨB8.S˕y,="kq<d�1ƫۼZ"˔�}~sټl^PO[ r-zƢ)SQxߚְ L巃h~<}O8N"" 0<{?*%(J*#i`Bҷc9b(0b � gZLQᏹߜKJ${%a=#$o"ATG3' '0J1L!hf�&&$v"9 h.,�#r6W&;+E4 \V,FxOX�Ϧ' m5 n cYi24ِEw}1`R> %/s Hj r8J$`hxGiKƓ�dN0MKPmy~v2t~+D+ƒ�M:wI[7X{R�9`f^}axb8<G(-bXKd灬*�4A+nmdsHKR? rdY&X$hi0 �T B'U`|DuX뜖."�UfG o[¹ŕ>p{P~x4$z &3z u(db[6 b0")a1>1P TW+ L{Uל<9@I3I'!Nir#[q [6#pˇ&A|/;n QvG�ǒ/񓪒 ʦa: R>qi킒 QYR"?La EZ0)RHENDfyJpf['Urg0p4{ehwN3f@@Q jkە4.f�9:^QR9N0W$RF݌8b2SFUtZx<%;<|XuLo\V9P&C4Y/ZADS1JAGS Iș 1$WXd>bYq3`]&YX!:'Za&`EVG'Q_N`FRtu펐&Y %dm).;Cz>y:P-]vb �_צ)RuxFd/ [;jN* E (t !HD|cfX5tS357<#a@AP^i^\aΘ&jeQ7Hgsa\)ZO WFD0Ml\zo!L%ͱLPNK oBv HfGej&?/*a pX>(T#7+R"y?aH,8�06 #`ampXo MЍY#76˱민OM3N,L0S0`EJj|0#*c h%:8W8:o7)y cmAc=ɫ2OzqA}[ɡJLZ~"p CUᾜtOc L;}n1>`4.$!jc~5׶]3(7XkϜ;y5Z1PW0B+`6^)YeAy(iw$kzmWu1D @FΛf?OQ ḡR3\PȄ L�,Ik H-ĀW88 j_68�"5q1rhB&51^BD_~Ȳ10 .�I)۟CqBP3xXQHįzy "+u84XIKC`FE49,傀^_|d8hW ,Xu){Ӏ/8OߥԞ0 GMlk=q>ɣ l@fmf: &  �KԾXBR$ys` }T ;"\BuɄnK%⃒[?%b0[4%O-͍'7+mvޮ0+1#&+_C`= nR'C3Y#{ްԦn;9|Blۈ |^J(DNɇ  9LLHa.VD"2RYf`xMNh--,dTJ3>C _>>N<J zo fei[N]<v\@Z )d'\!7O=f@ al!Ee#l~1(7Rć`WV%*u>d>Ͼ.ML8jIط=5$iWWQ؝B~+T!AkMeO>e5 ;ґ# ;{vWNm$}PF\1鄗A:lSXD"1F:"&,<Cs\8L|4CDj&эir%R5̣![qjdB TDPƺ=J]x[*bV; n;I)<!4d* ^Qj#-K¡5I&5]z]~eۧUAl=0y- <bMN!/~p3 PsJQm?-d$ 2E1.'\b6Exy|h춀`\uX " 8T 9O�(+EA.Mm/|PHʢ%2w4�Pnb9P%w�U`@!Ro 6Rqne:d,E~ K{ڰJ[; $l1|dH+]!J0 Cw ʺa j}2l/yH)B80 xC2[Gk?=P"-z_R,G!Mcfҽ΍EuY/p#W0C!wx*-+Hg}:$Ew_ H,LΜ8 1IZ S|S�x6t]:,\ӯLDʏ\J30Dk֣ TIPJ0IT2/W>!b TaU*%V@ٱ&_JɊ[$Bl9z*L{* 2Ӻ:o(9a}�B j-58W%g9Xt<M7S=~s)o!Ԡ(5!k5S<XbqQ -\d$j#Gb,VG=1I6Lx"*L TNBRڷcl Ya=2G1PI; _Bg�}nio2<vx佫T`0-cYbm|ॶJ294r}B>H  XYfx<Do_xIjxA;~?[BZ)vI޿I2QZ>~?9FE&jܸ,dUIk6`"8)Pk~H!/z׶Ey>ASSU3%Y�B#٩Ga!@݃H(bB&N%APع`PJ9+:IŽa#S'E x-Y'K!Y"HͬzO>p_nxN{TrۿN{ɤ:1u%#Y@Vzd| p1?ܕ\',|�]̟$ J-k,H%orJ74wq_ .8q(eDئ7qnIp!K?8B'iG6|"_HrCT.y 8Uu`TLϼz01B7E9а_d� Q*،E*nk&BL4**ufջ<w!f>9�>E_<I, up嘯* bCq+Lm (͞?L% Ap ىЛMK%l�pd.81r0Qs Syx:BӲ!ce^9lә  &Wɍ_ƅ( Cۄ,,)%&`Rna"Ǿ]Z!20RN= P2^_%W/0ܔ>(}2hBiw A99߯0 #_|LDl-T'9ЕU⺬B"ߋ,鿌gQm=jCL p �%*%MO8 [ Pgb$dA >FcǪ %7[f7mio�9f.)P #Yx X(:Rq%#RF8hBN\a&&`CsPm=2:tz0t SX-J&d0ݶ D-b~J.J-rHBOVK"^;Q(KƠAcRǘ3\Ox蛠++8IW:4_|C+*ZTl3z*2"3|PǦ.g"ķdx 87>1R�I14IRV2B`Qe(YdO3Xd m]*qҎ'ixS7ܟah78/48D@_X: I&dl񃈢`4霹(4몜Y( 51Hјf&*\T&f'RV9@xb;$$A;agX,%&&OEH�L�C:˳®q<(&z~0Pm`T1ꀳL†C^8܇�rl`Emᄚ)x cuF6j5hKܑ!_ \?"g5xf.gUpqcj!0k0JR104�is=dQROD84bR@@ˈ��*�rdݭ D72<Fd],FdcZNQ�(e^Ju'DdY#B=2"PwѓS q2d&Ա3>t@Oh=<C3R4%wr9G H4NT;1؆LZ0&.t)ZRErDrG]s^7:˚:$0Wzu}풾5qK6S"j&tw~rMb:59N"d?M <}F&u�pʚ~8ɍF=XФI^:hJ':zĂ4{ȎuD1d@zCRB|`%b5273f55.I&RqE`$4΍`fnV8𫍅CΙL:nxБl~qw@Y'9cT&xnܘzi{k7( hPlI3IǦ JAc ."'aٓ!1.X-b}\U$,p"i id8!z֪2 ţ8Yq[Fq +B>rkf~CH@C{Z}@LQ,^ܔpJN|�m1-77T SEQ]}ǜD[E4 r N0eh}TeqBXuZLn&`#ົf{Jj4E+aŴI\h`\lC3$lAxH>BȈblds_x9@�2< ۼZ%r`\\+_8EI^f 5K}~q@wP!zɱ<WN%pE[N1GX�l쌃wTqjxqS|Oq$wf3Mzk-ٱǭ '[TjfXם-I5A0_9-E -[RW*P!E0;sefǞ0<T;a)E 2Qd3\$ R-u+|T>u1Zn|`EUWOTհ- #qHxT?;� K;g�|h&tzj5E R?t*%fj)IqsX&n}N!pzW"8Dߎ1HTs x0A#\z 6˂j|a4A$g`4&#LDOh0Wf$8y<jMA02D�1M"P~|$H" ϙL``~]HqDę 43"< YF%!񯜊){@&~RbGi .6tDg*p̻;hD : 3K*&N>3#$?"~$R$.G~(!8s43[US8;(TP@:,0\Lsb5#-R}}4B5\wy0hf �DBI^ ɪ8UI&?(�Z }0cin'eh cѺO<y6Gl鉁B�6`ur3U)!Ӟ}2 #pXJ4|aD i6Orh 5_7;0ߡ�-LjbaX6&p"Q"(ڿ׮=cr1W1dW?XǢBLH۲}QqKA<zq>3yuI|A#pk3@zv..BEAeq؁  T[m-%4tmvEk-YAixSl ˝,*,mdpҏ1œFb@p "`-*dIw`p=k|a[rⱳR\ѝT!B$H }A!3RWT۾0w,fh:t�M2.! +k[+x2Az_�| `!4&%C*zLzyƔ  (/ )њU1P( c,E#M5Uck$3wgo!s�1F;hm~uInga3b@% Wc1QNZ ʣ\�T5)a3c)(jMD! @Kd`ՉYJ8&93h<b2l*% &AsB@@DRO3k502t2v9eva P+pHfR2 Dj-:Bj#)p5Ld![hy?Ů8/=M~p d9NgF.>}0XZUQN!HEB GL862}h1~<[r]dr4ϭ|dq4 N1 yţi$K W$EqglIsN'˓R*6B$L`$Fo-k !1o^+ ε4xN3ǜa`q=ٓC:>Ka�yk/g%"Lj]t@4"!!i=>2HTK 8 ?�T~^49 /l?#"KLzy (1mF#sxJ19u$ 0brmhۓu|2I-NX8qD&h-y4:.=jW !jIPYKC]lhp#F'OYCV�rn`fU8511SW *bz:#{_ErbqHט֌\a DOǜthSr@D=uА1q/?`5 [эb dԑ-6߬kX !e* [CIb&nnт ne׮ U„?b9ѐ-7-cf;Ιt>2M}_R & 9Sy3Sou N2H v .ݘLN.T<b ᚤD匎"]0WXneQB&'3 sk=1046-%rS2=LTH.Clr;{+(M8J{kYNcZɄUD?găq2Hv"ZCօdQ7lWy '2A'F"iN5:? dR6z*0ޑb@Eb"|sB"6+v6 0{EbV^uE)(+~r֊$ ^r}Hy0!-7,<$lzQJ"=ӒiEv>8$8d\k*yUaRS@9"-29  l|2iWaJ.f?mHԯtK"Oi46[ZFWPTYwCrkDSJe_32 # 6B#DŽTy <0v`ߊ¬x @W Z@U9$,a?2L?|eM9VV!=1dRz}+Ԗ__B)7 Jߜ>X+$#s93R_0SdL!okUD8XV=O;9O98)G\2&.cmp5`)x$G?G11θ!A~ ce$0!bք0/cG[f,-D0WD d[9E*17 r)32̖[uM$#J4DS7<ηcٰJ Ӷ#zV!FOtjO8bW<nLKj _ ш]"(D8e6 ]u'6EuToOc*a;w"z"iB'^dJJi7b'odJ"& <Jdxlp@3qtHGµ)ADn>r$ȿ18lf +0P2JG#- 1~0{qI$˹ä bEdU,u,'Cڣ"@`B| q1)B6[ 3j1O⎅F:Kh�@I W*UpT).7!6Fp11w^89ًLFܜf }0Z`оpHaY Y&w"(#)l4IfzQX]xP�+ mǜpnQ3=Y$oN^PQ%581۔? S۟S|e Ql$ EZ$Sg<d31_Xae5x-"VY#Y!!"J`L!?T - X@N"I Rhxh۳ ޛ|xbA ?9U-Y[r \G9=a)ceEB#.쓝L*X~1(֙2ICZ4D81 6-90"D1 =^qO*ϥD;[L$(/r ~x漒)m23oYR |VJT({T2s^t m{?r͍ #4e2JG%Ab";0,{d8pu<q^Fzl H.'uXy"R90XeI`~%3W:*B@-UL!ydn=0lI֝`F`K@ !MM7&,LJf  l~p ϗ^9.剘Juq9�E.!}`Nlyɐ,<W,Xț|:ǀ 9x Q.4a aAi4=�"HR*J5|R1 yD /* u^Uf=qK4|NX�;b{}'�.Iԙc!)?({猺mR5_3!< {# _WH 7"�3eƄ/Fױ~q1ώ1�ʘe+1E6Tueí 7Z ĉRV,�ܥ j�o8AJ$�C޼X $:7Zp(eG~3y"dQѤĥG`Q$E{gKʝou I*BHE8k[R|c"$ZE^|KK&#̕/-=AP=[T$K#f$&tk|P^7NbU\/,8OV{J) f'Yϼ532V>zVj;�"%tG9]H}dI&#Ep�ply)B-\XE6*;�@1_iL{XZb�0Pcfm9m|n{cUV7$IJ7sɆ{b(SlXT;bL)oߌ 7ᬒ+Q4Q}sVYMβ鴾yX!\ erLk COT,!1X,"V|PZ~}2*BWƥ N Gxe(Kǭx1`6 XR= ?CDu!GAAX-ONpDԎ@} RH1�\VN2(  27 n1&iEPjyJQ'%j'?ܯ{# 4 G('> $fR"!lN,[T Nu(C?71G �Bl!d'˕4h&r w�pvԩ٧gLd+y]x֓=O3#^.q%{y {^v6b~pdxvͳCpm\ѢuaE2 ncr)@=9Vh!RAdG0Vjԃf&;Ħ*RkvH<E< '?JWyPLYJ" Lg"+QO:1D=}ąJ|AܢA횴at6vY )ˉF)&4&A.wn*@ɋ}.ple<75`]H{&;�vonR>zL%! i/0V` ?3A#PܳOډ7|d �\B ?L( 귛x0G.Q>p DZ-R6-A#cd L/!6EnLԩ/⹃ƶds91Y'hM @5/Lw8M&D~BAJ?`d xKjXsH[}0qNĦRQQr3>.TN_&!b<!'<c&MqcvT ebJ̡E֤Z*~MqlGgs$21|V.5Q0z l( qC\ # &\33K {m:p3)Gn< K1qvKs^"� ĸd")j.%(+f6w`$Ҥ( :5 %Go9hJU*MEW6d/`p OHd?`N6E"EOwkKO[ ԄB#Q匲J+uO*e'p(Q[!܆E_81QRC+ _-(ž:sz53]`1tK;ɔ"f$@$^'p@-\ܬc0 SBʢ~0X }'#H" !$o Ub>0q ~>o!dǮBʈm>∖;u�[7A`RGW"ʠ_?/4J]ݵievTxшE"!b}qV|n`"ڸpub2n4hG0Vjt}Ád+Ҙͱ X@yCw L=#P[౴_j\!~O~.`:}rFP©wșf)\hԏ` srQ,Hn~Q+.P3m=¸ ;>*tb N T_EbTK&}k|w*M`advS+bw Ոn!�t`TU=q)�çػ2A8 @ I`Jq:eC�ƱubQ̂(zLIc`lR_wI1Zǃ}.]D^R=1((~C9r 6Xj*ʷ&;nKD?0ΰPty8oD#[;&�Lc[y?{d$$Ph ڀ`Cx$�2<d@QN&kO C}^mf{LF;ͲBά>pqAKƖA~&@D1 PLvXUfGc8E̒# %e+8R̝=̀TVCؐUǦB!1BH@}2 3Lm,0n"@4J}�1ʵӀMV-<hYkbxT|`(26=br@5Y%` @,D `hmfɺƒxBZd!(["!?<a *81 yox$ Ҥj"Ii*q</xj=_ό*(IHĽӀ?gU|bDQ$`,112-: -�Xx%7x ~BA2JdQ]BhʏX$.dB%)#>{)@@7Z{D0FxSyVl+ΫQh�/oMIJ2u}}ghS3g36 ʔܙ$@U7~(^1 oTEd΢~ �$ϡj/c?gBk2HFzV5.]G:N/UHR5gH_@7 @_o| I8u&:bAQG4krp'+572Lgq u3+ϔh=C#(hNQu�D r!8)yrM&Q2U &#D[0iH*ׇocG@7f >r+h󁐏ZvA~1%Q xyȲTWLg\`(읃Ǽ1:?)N|q�+3I�D$Id7cɋ77{J&wXh@`,YGҵ <qg0OL w5ndE)RRCa@D8O"%~rJekt@9לxđ3Zl[#\) OE)> a�пʙn{Y.�8IJ7:m#jb(DHaߧ6:g>9l^zxzBo%0L(¬OE<*M{-�S|80q@N82XЈ!֯Wߓ V7?ȉK6 [HPN/םF D0&Fx_栒ȉ9xȢ;(JJ.ߣ푱2�$)^u&czaFIyɣ[ޔ߯q[ `b#3 S�ydLXԱI QsC mH%x<.2!Sw<bp#q+aY ^w2l�!$ u+WKpk*٧<ⵢ MBRijo,љ*:ƶt!b]x�uŃb\ڷRR,L͵l1`Q%L)$* wmk#!l2,)E;#k+,оshxeu1 |fč8{4J6kxHH X2m8.Oa"#+ M9`#-P�B}"C&ZJo QEgSI0i+܁e# A& DBMKq!v�cHL+[54XC$q Jy0l{}Q3>1:VUS 0/)3cHH!*oK7+MyŴz?@g xbojpgVTS23L\[ YD: ӿ9*%0հ/gFeur) Ǐ\G9nF8O 9I5ZAw&Po%8_{a|)NwZ6&'ATLyZXpnئ*Xi!S30�UȯN!.½Ý]`$NӀRX+ vfp\&(#z0%A>X!TzPK Hb_nE( Z(yg~ z5:lљ %~2qJQ[vcjJ1_Ih"d " *xd+Dw/kcr1g\lKpk�b <�2�iްgplY#�޽rt1D yrc#?o60]9EjcCB%g X<<lPȄ)̏~TV/3lμa,׌% Z6*Ay?<j%9dqg{RH0?ĸ@Zu_yTw8JEh&`>z@&Xo<,wy7_0Я `H.N�̕"n OhLRR \w 2dQjg[A:SZf 0Ee9y,1ԇUW4 !{cɔƁw%HmxFJ̔R M 32 Dh&4 gr !'P@|s"QMd<"V<j1Q\{()uY"r^!NA{yzuh&=yL%BV.Y"n;ƛyDaC<.a~rk+ Y}LMD @6m$Fp#lV�LlD'O1_zd�pJ8"0%{{,D"̝/zR cĸ6yE8"Oy=ߴ&@&ܸ�JyDK Ӿ D &T5 &y"JHBX/O`O.)ac&&E wx&n2X~rPr;@X"kα$hqA2>9VD65L|rF( zYfȷ^�QbGe'!" ˣpl@@kEZE8 Rfuzղz}^'Ϧ_2D'2z y ̣W2p58-�`ol<%N++MFɨq 0ؑWp]I[V!:[r6či<dhfK' W\evzy8&ݑ8O0NǮz0ڴ� fڏPDT=s|'.hBBzVf0E,�xvCG8KC/gB/V{IBC%RIwwo�!1]㫽 Qd0eN*d+a>QfT\R@t1=Ǎ !a,*zG*Z.gO>M5Ðk-9a,x<Pqƽqh@B!Nf, x{]3bjxԒ1Ͽ?(R( Bc\bDuN'刂�_Y$3AX7Y<DPs$Gݪ5xࢮw/ä(ty5 ɞub6?"+$O jCѠ4ht1i,Ka1"D(_�wZ\PqZ68zT 91FnRMNǭ5#+JPTolCT&;j6#n4WRx>q/5yyy""CYvAXgL!<a0]Scw(AXܦ뒓),βR4b>S)6|^91BAAFcMeg5 luH�g#-BgȌ=fDUs; 1+=] $ϙc "TB b6H�/ l[/EA)fC@ԧ�0$ H׿1k<N(q;Rphj,b?k $hro LqYEt�HQ2"hPm�1tȽ0dD,�Gx�{I<-;X戂 {I@ÁI(R&Vp�"ɉ=�@,6D�(s/Nj`"8K 0<wS鬎(,8,9hijW=@p{0F2YbMq�#@G  x^8(o@O(4).TJ;�!D\IUuL<ʜXJq$GwoUxcgֹJa4^ KOqiSH$6D/bieMJ&�FbI "u^mB€$3@I)`XiI>(.IoLCh59!Vl92{3:y#bdzy`=dhy,3놼QnRV{DqsȗKZ%U_aZnd5D,Qov$u$ LAU$)L-j ,# `^1m[0f>{ 3GXi1n}[QbXz`rr8TqǜFQDvRGKxhӸ؂~qZPRQ N@[^{�sD#U 4_I<pMblS˄`\-Cʆj`T/FklYwOÊake&c)(u%`Î= b!|f#=zL*)ӯ<pl�HI[XCmh~\N{74ŏʨRz>4昼AmT4F+l:QK_3f`Ve M>bXb M)x 02De�9;O~�I뎴Z'Ȉ|Q�!d#IE&D6/RD�J dE%.G-#>h0]#W8kp'yI8۩QĶd7"8n\+@\K,@C* |fSlH\II O;0KgX.'QԻLqZה"w0hxzb%-hy9W>1( rB6"NMD?|Pya�aX xNLE&Kc<P 73m479I%W <7db#T| y# "_'4 8"<GlġL'~deF`bO|`%<_3< 5y'RSS(Uc7rA&Ң>')vA`_4@`RB@|/a">ό3RS HB v<"t$TJ.,dMMb2au%[ =sژP%WBm^ѳ|bimPAt1A~)p$zqICRCx.ZI}c e zIeZxb %f'Z )`ĖYR8x3A,Is\8̺߾wDXr8mk 屠M$4XD6,V"ֲA%-rl躩v[ S/ T=LZl'A)\"p`Ỷ0$PSǿchT._sx95�*SuAlNd�Pq,ׯ'SB91΂έ"0J׶%J@�qO?ɃY��|s #Ubja-3M5ȃ#wDHy2f"x[d4 T!t>9&SaZ] #C(R7A@_Z Yuv)5r J.=8=JtЂRuFA 1ל? K=umnn%|2dMAl~OVt#p d�/R.{P ,+zTI_*2YiTL 1%ngƭ_`;T2=xcm1X{9rӢa|H;HT"J]謚Pm)!@՝c D49Q�P�<|)"Ri) [ޠ+_T&=>q@]�pE)DPE9>0|:� >*Gr&ϮEL`.?$KG?cI50R xˆp \ +=�b % Q:!n2,],q@wL[ ? .H;y1$1ȶ5a[}bZ x<L}bIkR=xM=#ANaެ t]fD푥(=8ډ&&~oU$=b"FǽVBd/` d 疉(H#?c3P:3F>+ Tn1ـlu,b& k= &w&1GZ(2% ƲB"<pIp 6'n('O99 k`Jٞ8]-ҰXL(&eS�Qܗ& 􀘝r�^kcJN5.4VpyA*HI%QJaɲZ MwNxH~ 8]=ᠭ'C$$/t=U^0H"⬍wM$> 5WM-I4F}`=$ք3F8 o^*FefPIȩ>}`yF:䨴b'$'vk\(Ohe=_Ar I ̲x01X`LgpQU8@laʤA8Sv>xa&e؞{ (bXn+c`$qq25}eCdsm˻ s߭TCs[r $?2h-5)AG!)J[b by<c!zv!%&}8<@*y.hzŌB@E 0)lVףɮKPq T "dpdp!Xp1i &E=nr?qH @ ),V>ZsfaM"1,NN=phz-S$qʫa0 6Y5 Iv"lJ)łY9!jEyl$ T̫M׷7$@@O$J@Sm&E3ThAΌe$9kJMɍe,TCS:f͝|y4XX| 6OhWᓝJG�{lmO pU ~5m 'Pw4 'x F(C9a,4_%G1zB'[TS N֫%ڕ7#βhQ*ӳpI)&Nmi -"d`%BI;gHY8̊VppEDA֘!$[8 )SQEI1>WN�) nP,gy@ˠ1W2\kZ ^'+ 5e(auQ ,+:pD:xMFNXq[*$:hEvu94uX-a!?LVY۝ը`^BR}aPu7/rXA 8*dIG� %)xɑZTh;밌t|=JaYַ=I4&Ƌ)E(ŗ1{5щod'uxԵv�lMɼ:rFb]zB"_oL 3{OYc$LFG|D (0 ?+B#^N*!|X|R)5@0=S_0(cTd]m|'SD[~S X%Ƥ$#(*"L`hb"U&+rOiCt*$2RB9x |!g�2'f\%2& &!)e^rpEAl&t 7dJWyVtQUE<|׹Hf~:B|�a �r; Br9_o)d6!&"L%p{gH#D~ BU/z � B UqB;1S�s.ڎ0bxo2&c!?q�)Rua5.a^(|9ava%w?c AxAJԁVOj(+u,)臭F$P:S!rm6s`p L|`B͎X!qחwv1gȟLB~xR%%JUup`#,ˢ[ 8*2a`LHh'RTmP(T;,]H彃X >Μ;_L>2'@BOBR\�TB P3ν04CN*pX˫pqM�4=x++cF,M@Ĩ^*4sS\`(TW?aR,|`dڇ�^2 AXa5lw}( 64cGJRYB* jj=:$V  L$�LXAsWi''M6Y6rPM Em)ߜ s-~q0:o~#"#o )B&% ؛usw"kSqe6L?J=%0ϕa7G5fIe@ȱ.2g' `w<\ Tynblr>1ɲ5Ha? R57эPHbh@n/#}%f$DǮGdдk@.{qq?B^SPn+ W~g E1D0ky +EI>0w7U2K9I{90 (kC-C\, XؚHd 9%"њu%ErRg[lJH;S~LΦ:X FW4A˯Js% =a\(PlO $_>xsDm<=c^N5i XR0ZcDLߟ(|^\¬mtV$i+kڿ,LB&fN#xPK BBXoa)C�In;D$J-#ۡ4cTOjv Ԣ zȼo|nLX K[aD!|zoг{/-ZpNI GX A"x@6̂X?)J6eH.R!2XAǎQ>%F]F8~"k J7UH_IB1ߒ h):__&dƜl Uo~:W9q'r՞dDfLTpк9!Ph?&+XPE׮Y")?@#s"D ņĬߓO B�wdzfr)DIci;8DM&;-X;7yGn{ VOPTOyyj{r3j%&ɷ '8%9g;4G\; T;&5 >+' LO(D'(ΡQ"cj4, ٕB⽦(&mR~ه8*x 6=R7 <EEvB:H>ch!S0qhdhqϨPb$(Zb%g{ $2uD#r; }kza�9eO !*KpP ( N:c$h?xƃ\$͔rߌ~a2߮qqU""6cPc3\ hjc+l@r@ON, Ȱ<t`S%,}_W|PbSQٌPJ`A F$:u'rY2DR:FJƣ C`>A3_SO5"Ubn�zA%8@Y5ā2GRh"|u ~dDF‰'bp:I٧wpSm}q$Ԥɋ_NE/) 1dpq r`ygms#!C\e@l)qX:}+!/K#)c'#C<4+ULa5I1a̐[a1dO8%24DыS5Hln {wBzp{L6w%@ ÂAĘ%+ $zwDAqG<Ls?x ߜI,I cpIM̓~ι :7BR85#\w 6mGSjB KU{XUb�̔a|ly$ɡإ*k&?^)Ddqԑ[O=bvcvxPFMI|&ROQf}B~ z.o!a$OpPm>֤)8VCIBO�|0"OY*%Hxrv=w"�CDrϩ!ׯlqP"%|q Ą�5= <dVN "d$od-@w4<sf&'&QA DLx%݅@3^iɘ$+pe%I&ƃr_5C~F2M@](/yc1M`ɋ-OaR>0$ʺ޿kVG﬙fX1vDEáB!e C%SXB�wxD0l^%a-= 2ePͰ$pET2%K d蠡J& ^J@e�708hȂ\8Oll{q$b⿌fHGl%BiIǴElYՂN=1X`Ei18"_|QA Y@OGP̛dݚ 6OR &W+Lwz>pE2~9 MUFȂ�w|JHBlu EtV 5. $x==o pS,bILW&F&E,VBg0ue[|,1+Y p`lDR>�[R* 1ND]G4cP@mDHv'c-_8)d0 @*v7{8AּZ�Mnb d{Oαe,$z䙎R4l1x 3f σ+Ʀ[;(i^=KHp^!|1~R3#'N1lfMBW+ z2wwspX ;xJJ*0/X�X A8h l5ji[?Bd\Pq~ 9:6s寤]nXI<'!: ؤD�@IM& )Dn 4DE'~]o & {0S^cG!%MHEP.#;߮`͕�3aZTN~0TU cCȖ *k@`=r|d(M)}�),dw]q0id,=7b 0-H@j};Ò!ID p$F�yL$EB(8m!J-l8r`hR' $z?r)2t8YKM<F0hk%` =)x.ʉ"%s\`M cpZHeB"&SHEFbY(`o>?*;Š))àն8#5utZ\kHpd"e;l;+(ifU²lZWN H=d%dqHZh-#ͱvcTˍH M* /QB�#J 0 sNCk!&1ds43bN*/2,Lm^0D5(&S¬XYA.?V z;"Q:tzjUXp~2N"bV>ͣx;f %kXJ(E›Jʛc汘I5-\6QQ䑃Dd\Mu^ {7?H9huo$ `f1<0$q$ bo5m'\d"(GQ66 <zߦx 2ϜBhi6Hj>w19=I$ğy4W޿yYWv6,]{b O(4tF5dIHsˎĠ pj|q od8Ɣ#�Li2!*cy!9 �\ c%4dbS8䭁 wW"{ʼn[�)->L]RR|ZK�Hޟ>pp35S&/ n!2=o `/>k 1 pY6 B+S<$l0!ޓdؓ'B G ,qNd".~~ I$3 -ϟ4L&5187W J>1%.7xD˺USʰ T~% V5/!0am/8J<0 2̷O�{a(83824/߃Ij%L))<'%az[f do z䠔?9 oˉ,>a>=:Pa5/"Qܪ<b��1:.BGdB+Q2A8 gӸ;%Q50~4Ub/ &a 3.8vv,c|8BsA?_\k@]U8v~?U +%3rq}V�qIV.Es`bl Iԁ 0*'!B|R*>cwOz61G�'h*2kGnreË�ħsF6'~SHȑ#d9.f? "*r�B!Byϛ[WKL/em8)$$(x._!±WDF㌅+[3IR+mz $ 0X|oHj=X@"E94H,/B 'L#3g)e<Se Hɓf$(E$a-檺k۹5^ ې,oOR^AWc ~c($ѵLdV\c` |*co!pы@&xD"LVp1<zV$^ufMN6 o -%mJ,Jn}+&'@Z�rkV1e.[,2j 2]s(1So^0Օj�YtaG`@H\cjV1ߙ7zx|`P\�@z9ȲFy0`j*Rlw7ؒGl:: $oyYap8qJ!(0nxPbiOO'ԋw5,9 +ac(ɼ PRKǶLd0'Xt@|)~֍a�]cHI`O 88%1'!V+,U2#vD*P!yuY�Eks*^|Q- *&~2M auy^qBE^pJx_ JT8ɞ"b5RSpZ(QȰ̈́GS{}IT7D <b6(~ڴTi5)Z0[�hf{|`%Є}{II*b"BH0޲ W*0Q"^"J=rZ7 D(֙ "Û񈠭í`ɀ!bt~@mA&c*DСƿ7^؁4y#Oyd A13],G2I(9‘:',EiQ7eL{{3EǙ)L1HMqMy=42t=12\BN}a gw&(-q bAVchl(W(hY@P"I>s[rEI+NRF4(�e3ɭJ]c4s5)O3$G"iduF{aIkaSNqڻ'c(`pRRtaC9Bcw4G:BS3PI[h4$ ۗ<fiؐ$ĉvqOb ׭nƪH[ʈ:s&."�$ 3SDK&|$�y1P|K@aEMB^?c iWY2QȝW-g^A&p(/^ I:5&aqݐRq2 xDIRaGTb[ɜO(lU]/u#"é+4H"s_XAGo;j`%๾~k& 2\ڄdmkB kYPP#[' v-SvXg TIפ5'co3ux"JAA$!<^&#IiEp'JSDP]xHDZ'3QOɴδMn:ᗺȀXaX-{F>L\Я񌃜IaɅTr~y |ū~ǻLf ԈJ@gN 'LPJLP1CtV&<)U>>  #9 zIx-]wԑzࠣ m 0NN"&aOf(C~ < Κ3Buk j:'9-1+sKHcK!-} $L6jqڕf/?DBh0\RéۆY>JK%PR8X #L.*�(r (!& �_w&`r lwE La,1NN1!CA71:Dy"'^T|a㤉&'U0Ht_8�xv@zwbhʾev`,C(`>0+[7D`²^.y'YpQL6AW2�D%2nMdf 2QU.?xVSqUЀ"�@4o uP 3q !"u _&[*}XD%cRVk`p*{9R4PF6(b{|b?`۽da4GEVx!e[F8BGXC ̕b)>.L"l&k}+pɾ M[q,24ksݓ+'eI9F1!$5D]@6+E?;=(NEK냁�I|A3"7R@>` ,fi L#>0S]hH.Wqi$RV-JO Ksr@~TCu|dEOW?ᓫ=̲MVHtƧA$}yd{ ŲLZ>c� cUD�{!,xqx$!<?QIяY�-1ocӃ Z;'4�@(û="ZTs8C:ifkBs: ]󈫖/( 5J !zdJ.? ) tz�A68p�PRZ1Dď ۯFr`t�"Vf?E`lT olhxq$l¤1#yq�&YHH\$2 >Ydm(F9(%L% } i� LPypd$N6<s_XɵjZ>!jmHNB rF> )6{u&ZW#jF*Ɠxdi *#)<jޖ缥<نS벎 ӭ2 QH&4RwA[[Kj11ѫMs嶈!Z%koKYc#M޿\((E:b e7CcAmNbmڌ{yP>G %Or%2^=Xq?"d{.:<0. RE/ްL!Rw6"�L xH2α;Ì:<nu&Mm^'QS ^|4F�94 H@ 9pUB~ u 5ל6(^q(K !n}f3Rxe F@}$ܽc'DHn^ϭGX�h9`FU8hO_m:ZɩY ,Ŋ&^f(LQ<d#s$^VC\OY7d!/OXbDYp}"d^1#:ڝ#v2tyZhK씌Rz�" x z`y%Ph5Z& s'!$R5邌XUE73NL'Erw(!UXe�}Yߦk€}E/%ɂ gR]N(VݯLj!ba**'ua °TMa\aVBtGn."zRfウg|�rQaDq~^DZ �-|vh\EneI#rU uް! #qZwZ5DwPQz&-qTR%e3G4`s$ɭ|Fih%�sNdR(\Z+;LJ]ᩘRN&L|ǔrBn0T lN28U4uD|i\d2 y% *5bjxc HFUsI/XI1HbHy0rLu @VfD�#<@v؜,L_�B-x4d W(�8}2la/@U<~x≈B}bqXãC 0*4Wj>"y4KSRW^=XWwK5V)1sg(@Ѭ/p:$ä \'ap 5_NLH̐ND [)5a DwARCe<2GxEQzy*BIq'%8)]JH3RjW>zƒKBwCܔbZLzp0L[@38eJท&.B0|`H1d"Hמˤ}cJ"Ovzɋ,pBK! +'4\N0*S+q]  8x}8x2Y1  %O'` 42 ,}z@? +Y�*PmFq%7!ϜHic*%  W/R=E _}rQRt( &ڪNU#1aeB[r06 he"&; . !x`3hqTrJk [%CKLT[^AqT)I#UHWsɫ,4 :"40sjА,q8HH0h'1צL*VQXl9xoB*OL+!񓆍 XQklK(G@`O~X;1(~#;xPI.[;@%i/Y8e,(_814I" 4wGd){JdKC@}dL1�#fEٚg)5pPB<-o 3!568� ~ nd nm*^笆e!Fo~?"82  O$!PQ7Z_.]t9�RMd�a$러Y�ԧ9ɓ* B\Ȕ=0�@K0+W~1 |d-!oǶ1x Zȝ*{ Kk- L%m6V0hZ{RƢF3-KĕK(-acq $\|93 4K! c.22FIW=((pN'}X�J�C"+(�$*=bPgE#'MoJ3rs=Tq,d!1<d %.vfytts3֔07 q!6`BoQ5%Kd֩D .6WlSY�oYK=7(RJ*B8�,E"GQ}}ɬ:r`AkKtDe!mN⽲+KPG-r%oϊpMLw$OKIO8"u.&,C`>wo804R|sVB5X14,,>y�}8=5!Co('w]~sm~8P2^$ɝDY3$`((>J@evQTk5l!w1Ǿ CML{;(Hf2+~^1wH(Ůn9<\jd/K�>n!*(A dyQ.Z ST)e~0vt3mjߜ@�j *y�njsj!X`J`l'f&CF Bu 4ݐ:d%pXGƱ-..mȼ0 !^V6;FUBԈZ@(�Sa]6|Cד"�g:GƞH@M52M=cgimK*m!Ǘ2$뼖/+=P4X矼{$2@$'}BV 1f.*_X!K[u*LqQɀW{pX23`\\0A$;7ʦ`7tc Ї+&~Eb>!X,njg@n8&P<@=U0�IY'0Q!|۶yNғ5\ ]ǃ!wd')UC, Fb?O . v߾\aS*VSFdݚɈ*Rodb>|k%׶Fo3>`}󇠧Yx6}`%gH<d�T&/-Bul`r+Q0kXUM K9( Tm!1::A^6a'9Zރ\E]hi$=k,蚺gfB\8 #l!ܺZcp8VOPCXA&dAH Jh +U0)ޑXE!2fƗ:DXprW9cLD!z92( {^MH i4Â(:03[F* Rz4Ȥ笏KM`< `y b1었y)N;%R&Dgx`Cd4M~ �VEO<"nIk/O\e8# "_eO-:qǽ+@U|)Q)B80Gjn!ljz༗ԛr,@�RHV0 3XMU=�%Lm�`l5!85ϹK�m>RcU'-NL$ d d'dV `,A]bT |u[SBg٢'!͐Iz 8p89!>)̏'`rXJY{L2bj�?.J7b(=ZحLyV D)qsI;ǒ!=cy<#8,?mP $5c"+!tN9I�iew0!1& r*2i,&$PZr$!O0Rid<#$X�Qj5'p[G8Ii'Ǭq'(-W#MJ]e( wy02�=j',;�J,`}�0ԔCW`4/v0{`|$jiQ\c@�g;:}/{qP@jg;̂"8naQmFbq!U眊D d Lj5j%I0޲]鵯L*BQk `!+>/.KSHL($dCkvO""S"ne oع67Y2ipW#8HEíAGw NbTkrJe=zaD[+&UOAIqؕ T댒,-[{n}I~ b餏?BE7r*R"Po pr ^+!oo1MV IJjrH�VU G}&1'[笄֜xD[<6م=krHWHLI^2WQZhaP � Φ8)l\RQS4 H.@ȜIro J_A+X)9UOr~<`^JZ!tמ6�:>QH kUd||4ͥ`KyD JVk0,I\1!3IQa%N+* y͕e$8(QzS;<35 '~QCt320AwǏxWR’ #h{{AT!FhpXE\20yoCBZoFRBEO_k')4mux@�~%drPzO6@OˑHYK̹BTT*wVt}>$I$�NJ{_Lȩ<j9} i߶% _Y J,k6(f\ѐ;=:^ӆ\Yixwu0uHe-E4VXi=;q-H!όr6?5\<a@H''�83αɣ p(9ܗ¦K3HW1&E8|aҝ|fU*'X&0%0J^߭Tx i%b7ch T 6ݬ&x{Ux�YL %cyY_(eƨ"1$|_LZRf#/WvLLDGm C{ J7q@0@dr\`+Hշ_rZv0gD,O<-$ց=TK=H<@EAOLYÒ٧Ch*&h9,@NmDP!0 ! [02F@@I)�8k]�954^2O(+,5ƓD�sLd�h�K^pa2bX.<{dP +>dTK∃Գ~\ 娮5_|F-b<%o89#$@iv,jaMWfLX-d,WG<a+c@>yq9!p'M2; ;Aל?Eg8@Y@D{y˳H|AQr82(RЩ %d8�w XqSXh [R.c~r!J4ߦ8\,{!5v Rt󍠓0 U8W7SdlE>|`U;R�C/ +F|YuΒA#3a[P=pճde)�#6 #D1꤀H&(t^ lQ=N ]7yrYXȳ]|y2Ore'%5MKh^1W= RJEFu'ܞQ% *WdsF7]qo&K ٻ#HA$ Zf@Ln&c8eB�'-*2P"S : A] ,ι`]G�ip:)jRgIr Og)]M`[n `pD@<{$uw`4/;+HHT? ଜn @ŝ DX IAXT KfNTǦ[*Kz�=8ry<b(HQ#\<|N7QX&ZgA kfBrMP# 8A2҃nrIΩ=lR<b41׿`@×"RЌwkRz``q1'5%aLG�0(zJP%06:a/묤QY+#Q 9j"DyNE//]`~-% ]by`.|Fa(Ce'y&%☾K\$$mK2h$؂e �F4##"~o)Y(X%DI/�+I9J`q>IHb2[>@`T}Ӈ !}R=S'aqrTEIpsy(l+ӌ4bEVǟ�.:I ^�p9 ]oSv.o_LnBbHUSKˌfʔ Y 4+�Dox) 1 a+03W?$e4xI ㊍+@#|"p&r@�EõP,.b YAƣ@y\8Vžr^%1%Ms1Ow(c5q=wH篌 �8qIN5CRj +XX9:6ٞo^Xa XЉ{MˍNƹ�@i$�L!S5-Bj!�+ FtDF0\{Š�\ҫo޲?@ak C}0dlߐ픩vv(dхR:*~{$#ƋS4oW=oNqd pG8ZIHS,5|V$n5/˂+a AsK9 z~' *SÊ`K6^ '(}-K%teA'Nn<to$$*+s4W}dA p 1M{7~&T 8`$ v]=`2HmzVCc'/S78hl0GU :0B(2BM XM$&()xbglyF:̉*zqR(RraM1,i|Vk! KVuy4:矞d,yCy3~{VB$ #!#"{Zf9]J%WVP: `1W9 ~z,g % a|n7x0҄Z!ft:wU|1|�8 (nڜe\Y�N)UxJ�BfB6KqmrGG06A7"E 3%TB#0#0)O$iPV^)nȂQ<Nj֣YJ3ao%�&ME{o`5CZZtc*Y"3X{ ؊<8$X&gs|�_YKz[ a4ID0hO iB$ VW\Q)\Ba悹s5;)ZBb6cݠAտ#4 g�SNgAE'\1Bp 3m\¾NB\vQfI9&F|œh!eYX|EFl#\FWA>!�݄I\cH (KHl0#@ry88(ɔC7jT^@Ak[By.ʖ<Nə967WAU/d+q4ֵd>xbIt�d3K&@N%ٺ8aG:_oxT\-<GSȀ zki$LH0(CћNrp$g&'~&2:&;6'~qD~<Ij9>^3R � KT*1:Y")Msx "yTo'sS<a/jEd )&&9.H W[%BNNjʼ( iKt&qe68fqWn-",1jU`X"= oz)�XF Mk%9h�$i08߅J3 D#(.m( yfyT6>hQn0 8Vú h"bG87 ю0�H4o D+Ib*t.crTUhC@xs fZ$CW@|6ukHy £;1l35DYDߏ8˄"_5$l&|hqŠ +[Ɠob71B$G ()]dvrR.J SK:;@l �2ACM!+y°5<o6WOS s82F8HDDhq8�5�1aXE*Z?k @6[s}2)5:!!0l 0O$b2pig:)v|e\ �!ˬRRQy+GMx $ %Yh窪^'zM.ۈ%r,UA[EUƩ% ˀ(1|>2t n4eh  %D7h@N2 Ou/cC9a彙YK‰s6l*`lGbzY $D- 18AFfY?`$tNv̜o OʁWpˉZԜ$CEc˅1#Z\RQQ(� w3JJ]rWL"v@@WP)O))sLfڛ�ge2JdKXpbflŭ R#9Q'_O"H m!ּ�JOD/3Nyo%UUȁB>I;u ]f/o3 _Jt^{@�ox"3*E Z /{ 3ϜaI4ڲ F&+ia%ȅS!mH9X5U*-\ECڌQg^*]xn%;P%d sz(Q,>? Kٌϰ~y㱲G3d(k₫0ra;' @9g bC,7$hj&oqP~2@d"™PU4qr}cH~Eo+LO6X`߮-JwÅp|*}%% C&ZO6ȌY&x2_�kϿR.yHȝ}f|sGm3}"pX0j# I' |9ƑaK*3LL UjI$"uCr$ kX2�KH|d|�c.y8mH"ot}c))V2aC)nܙXӃg)Nb#aC@X#jr9Pb{G i~1YɃMo$QD :!AC]o )C}l,$�@B{o K c5X(PaH9b^kTcв%DsD!&+d:(kxjEFK G).gH?AF0pkᄲOPT;L',:*Byš[JyVpL6 S%ƵxH"ˣ4mj|sUD#]Sxn;E3N;..QWjDot}bB4Lθͤ;Ӗ b^Dߟh%}$@PRd �Ff@ }y Uħ ON- /<ᙦ$iM71IN ؅*ޱ#ep kTM@!*O8R-\ _8$icVBY;RF0:\G!> U1x2HәPjSyz$ͅ9|q)-%‘X,QO4~6�?ɮa {c 4$) N@U <zBkЅܝOn#ӍB3WtxuH|a ruc<Os? )Q `E(6 M=iCxqb0c3~ZZ='!^zeӖCAͦ-+z_25Ȩamx~vÆTiOMOPX<BV3v>šLA KA"(J2Tb~F\3 .oSV0KZiknjIW {zC"m[*m{*&!qPK^D<GH0 @D&F0& ‘" H��n"q϶8DohAl*DIY,n0�Xc|K:2: ;eoGd޻|IˌCpE}3pj\ؤGBkZ/BMSfN9Ze"7y,Ȅ9W;uMJ&?w" 0]"d"QN45s78F,]2\i& &Z� 63=ažHPbX 6`O8}ISp3 R%IˆE�*) ;˝:bƉO�1|\ wD)%{QF-l I?-m ADPNH4^90BoLN17@F^CXq$59,TX1ds(6%@D _%A4t<䝱;�,3q|ec%Po [Rq CޛӂF�knKUӅm8TL�<PL %䲵'.1WZ `^R=(+r`Ck+"/8�S lI4Dht:%z}9a'C>pS s�T* [CQ.>\ =gXDĠSjwbuѓt.rŁ Yf-_HU)>Suxr`H}x %le1 ng;e 7F㼜ⴘM�e :+"+kp9I1dWqA|{:}tJch�@sTF$DL!N9?wxHׄDJ1!�5oc12`-q!_4 Mku IN*6N_?` !� >L�0H ZmI% : @t1X4=YE>?d ||(.<εx2U2.=�q,NYpC)B8"+#/ 0 u^PbWqtqy&>1->EE Lc;3XA0 z\14(B2br'nOwY pJ"b\ي$r4~ĻqSn5h-_K$|8f$ av{xIlN$ru.I!< Oΰ l`959v~@Z0=5̽OJ #( fJOj9ffU#w0l0`~0�j5`АY&R wNB€hs�+R6Md6DlS)bzw0{bG߮L$7BdIM} m72^5@8<pq($"L^CS IwM0U]ÁꍃU*%=eC0mАI)6ĤHyHߏޱ|2"E\E[U 4 ©8|l%<Enp[EHd>�$}$׌EHsXL!!:�{1Ł~h(!RF*%gk]a.$#Ljo�PR=h h$șWqM-CLP(OQd+7㎣&|yD=^(/@AeIBoDg6C^*J1e!7 :>TJo B'%}?iv"2^ Q=pX% qK1 nSB"@-i@�"L 7I�Aw툈A[TCC\$:,7hpkEӇI[D FuC1�Gqеh{ 8;HDshDEB xy:C3_8eUa'5BS~�E&HSɤSOpU)BH{+x 1:~x)dAt?8PHGDTz0'-A/[ZLt/iWTC7˜X�c?rq-À& l. AV)3q< m9 )� , VzߦYV\:)sMML2 ƭ1 NGhe߮N0vu7p60!dM9}lIl[Ђq$$~F\d~1 a"v"ƫ$Q'3丰xyЩ3YOFԉƝ"hr.;*`fAU'dA!-):#!>my0&$zQV9ҀQ$HKœK>1\@qIm$8yɰ3xJJR&H$]-Z;žV(',&4z*='ײ8qq:(PA/+b*O8 Y!ɏHch2 !uk*rqð3AeK0`xd$ğՆ<g;^�qvTB\\u9Dћ7{E9y�UحY\815mq88SeWi�U%hɴ4iQ M<�<y{pA@ w bގJ]JD$XUJP�=}qR8m ;}r')hu&.Ma W6H4Ȩ80I85^ȱcQ}y;43&âY*-Td`l!7흗VF>2m{C*a>o2 ( Krt)?M L o , i}xiqg%hS!137'we :~iB6E3咋q+dSDZJpZ�UA T!d%%%IJׯ-`JTD!�|*CyV!|bTuP;I-DD@3ׇDO]o/1f@5C%H+ $$/IC:F&`'nj%%ucS/dS71Qp- 20ukKYdOӢ.鏄Cxp!�V"$ `|XoB25qx|a29 k`e:G',! )x�>Y^!*!lNLA+sF:?x "|WpJfY>VŬ086P/LO'ъ9j.JI|�<~�.d0Ҭze pVX6�q)|J"ALI$xb+o 9�MA4+d`ZʒZ[e1 a$x@BkCsl,OKBܶ/wP dSWq<4�BLL2u890ЀLIЋZ}o  8.񈞼R&}.nJή\iYd&,_Dng$5$9]^q;G N1RAtQrdM JJbenpqAu1 1Aꢂ8>YIDqdQ4d5uDQ:Egq~!2O E/А$v!45!&M/'|F%$P(c':[Rl.rEByV%%rރC SϦ8HD-YD@Hlhg<)03&9/#ɑK D_~2D>LDHo85j sUS|U<F{1. f^P)if�[ΙbvJƉe# Z(^၉wG)pA&E[2Q%a"0 0w:D1x 4|`2 ( @qRh xǣEUL9MHH/ьn q@jA"xy.쐂\ AQӔMb'*1b�(R<Az!GYDbToƧT.4PwF4i;1n~o`$6-: $BŴ, "ijn5D)eqp:D�Db3g&ӎU@4vP;dLlL+#/tF]bE ŷzN/"^cv'x�YxNDjQ!& >.?!{Zk{"@[$3h)/I2ĕS ,X2*\xOy2BNhC6�Y�*r MJ&91C j6K_P4b6{aʚQ2&@+<8U>:5Qbn:DӀT.�!�Nud¡�y,t&e�$d% zⶀ%`lb`HɌ_| P TN[jb1b *#DA?*Y~)*nN_cB^7l#-& "(z=cL=CH0N�Do c`\3JtgC,69m r�ҿ)a5E)`(&\5$:v-D~uk xacwIц(S�@ea%=qfF�@pp'7\&.eR1N2VgсF'DvcP@t2AlS &ֲ`kXA8\Xmf^zѨ)!FZE ;rŠ(G J뜀6?dhBX|݉j]e;Gch͒1]̒V/Qꆋ?HXH<'d`[p(SK0r_CiRwD1;>1�Bѿ*Xu_{؇hBziR=" 4&ȣw�,/w�]a~X?ngO@}e(}#*q?7ϯPV@>JqQH |)VTRvK `Lie#o|iL &/?eOw)bl9ِ$5y,Iʏ^rU< 8f$`V@<j*3{pVL%S\9Qx珼 Q9)p"?)%) 7Xhd1>}1Li%U &%|xT$,L2Rv|e(ؖQ0YW"a[B 5+x*u(Py&E`£=B7F K;�P<�cvmpI9̡.vBTjutWX1kDXď+P9thvp |\ᾜc C0/ 7h"j<DeLL�$H+ +0uNy7Z*&7yIR3ZrN Q03EnWM8 3k%3)HP;(ՇAq|pHy= p2|aT'N(9?q UߝoP!)J^T G$o %C7&fc‰ׂ%7o}.5ȅ& Aw^9j951AY ~^q)�h{chHQ2�.A]: V NKaBau3X_֫noVƼdn|dZؼL10@#a~~1TLK.,I!)"^5U.lrYRBOgiIR<DLfL)|k_@K%+MuCx$p,}a^u'AUyQH0>&+oZL͈J23ܦZ`BҐ$묽eW#ʰ!$ ? 4(*do¢L=%Xs[,V'Z<66^6HeZu` �!ceߜ06"7`�?=E+-WxS4qβ,A�3\+�7/XIHag17⥞̂"~"2gprJ f 8� Ps&h$oӌQx"b@$@h,zu pu1ק=LqxdN|p`K"#^a8'=M!�Ptxm0W +V"Xr2Q &PZR 1COC^T-FUƲ'+ O?aP Od 0Q1i¹0DīPUifKd d89,#\xL{y{Dnx cɷ.%:oyZ;q#{Q)y`Cj+~2 µdJ-QxeAP_9@EaTC)3Z 9kƺ! p.\!T, ^5pEs8Iͮ�ˌ2a6"C{t$1Q'tG tLmx @w^fjHeGU eiB1 'bݯ)Ia#U&Cygjmb:log cL(Xb"!zDo V M=k 56B9,š>8EL24v<U KHRc2 ҉! M.6J"'YP) Q3xlJ9 Ei:}H HwQ6 x8|eMDnN'|{ J<U&TO[2n׌7XvtT2[.Ʀ�8w6`6b>7<D"*sxM[� BCd1$}ʧg(RE{kΓ4(),Z [z`)-UW1AL:;QkՖM#g5AfGQ[970P$%jQ/e]Z>$zs4Mz0bx<[IxJ {1ԉ%b B&ׂru͏`>p,;l1z�1%#~ʇ 2Hu�TsE' 7x iq@*[x+ulW9VX&1 E (ߌ ygtBEz`PO mlbf0i<"Qed BW40r^CcѤ1= R=H8qeU$r9!x͐#A'/`H$m$=1 .T<$nr+ ^P<Ӈp\% >P[ %zeEZ7,f"!k $! "ɣtEEzRzb 98M2KAq&I 2H\_8mbٔTEPUF5# g4\ɡEK F$9P2q1Qqq o! 3 }wԊN:ێ2FCX)@U),V]7ՠ ˹䃗i-YItaSYaWc铮icS*=rT׵b?Ax4+Jj U24>@6bI6yKP^NM7LVG%d#gTZB/(̀ð8~-RJen!tF5R<zXM8DHǕbcD3}bPCb":QFH"G'!1,tzL*,Eq0 0\-Uoc" 5`u"q*@LϼZ 2ixāF* Ds,@ Kz{8@SF*e@lWo%0~]Ht~8 <1`@�LyS4B 9a>U$A%)]\><b"Mc!ڒK|L9i;OA]~+`Cyy%$d{ɚ N7pCG);v Q'6|QWo#E^('g\+)+cBT#7sFC8"RF;�$zfcZv#`lNg xԢkA —. l�D%Y߶$p`S-uxW<�`ip2^@׮%`'{xI; a﫳M$aE8Əh0t|ߋE"i>cun!tRxc!q m%.p 8�ke~qPjfkxJ%xӴ Q#tXNW3ΘC%Yl̡18 RӼhw9 jLE(GACzq D_"rBonN4E& AnB-fnB\O< x*.=~pe�"Z`_9EL`PaRbQ'PNf%u5&+\k8 2 K",s;o./d;'-e)7Ϝs:T|!�50z8Bҕ@+1 I iS�_1xXH 6AĎz`:+K!t6}b]D!J^w:pˡ5+v#]V* I i3s ?<8 B6<bT]x+a!�8;Gp) AlXŒf?o#Cߺc^79?":eBK|M B<Oc_yH* t ($낖 c]L&m{CP7�`HʩE8ާ�*t0ipr_fh3_,d`Y!8�D Li`Gwb7&J۾VtC㩑ouF#L)Dd� )X?XbHqHqȪRAFhy򣕀*NpэyF'*.(ۻcU(L܍߲\".J* A)=Dh #enYU ׍́99hc�Y؀|T*/M#1a뿬0ֹXvք~0I訣q/-ysc2 _.1U8u֐ (Bl$ w©EvF~/6|*#|4 .JU8 A@J;HX "e rÒjK/}wIgXq2 *c8*?8BձG~g;&blOw~0TS,%IB%wV9Fe/pZRYevL"2Q z^":upyoj{nðiutM䵘=s OI/x̐=V�*QK~1ʂBJ㐕0oŒ q񒁯~̏lm �Y7 $ǔ4Ϫ�ֻ}1eb�92MYCޝHhn Mo& ŵ!3$%9P{X ؾE8$D[uVKlV%NI(LbDFVSN0a؟lU.zFHv&Jߟ&IY=tAP8~h\%LdzX!k) &+] EM`Z!%Br((H$ '#LHd){uq淈;F7#�=5)k $ж{dB~dRQ$N O^yvՀI-Kp-$vu_k .>mʯc`8fb.~2 lp׌s" \hMqg,UM<((58(Oߦ-0 , "4P&O$L;Re9O)FѼ`Iq?) a춫& -wQ#)=xf9\Vu3dCdҥ>#_+Uiޱ a\(@4LTRq L?̗‚T%R � wZwgWOR;N6]^xL@D@?#(7y @iDFJ@f>܊FI@v3f8A 7ɨ$*d>^8S.uREɷt do|eEhd\_.†u8 M'\ $5 %=0 OL$o(((6y:aiT(,Oe/DLArh=G3ԤHkګDh7~Հן!Zs�ܭ-Vbqa fM~4]#*- N>M+S6>:'/!G=1hh>=wdě;^K-<=L�4XLM~2e-xzo )A MsP��PG`ĹX3LĮf߶h` o^RL{pPwP´MΫ  o!Fh/Y''!uL7�R!yUG{'Xdx))UoXU2V}tĹd&`f[-'G"1-KӬ8 XzƩH@_ 0۝z4QB6>奱 7(diFFԸ zkr8maM,71@ "4gaHV{fB걸% ^~0DUy#`Dr24>r+v$ .]pMZȓ"*)eCyP ǷF#'Ňc/լ�p$Q_</`㯬`yN"&+y mrJ+O|⒲"!QY:k AE^c%z``ct%-ZȮK ;/<h _b@\FO7]̠[bَD!L d!c;J&80qA[X:,j'W;X>q$ēʚ+a %"9 &V8x2EY>xKM, 5{I.rAرu# ^ 4qraL~3bqVwJ1�2F-Aj߬`,l>Gc&Gc C Ud�T%%58n޽Ĩ7$B]�) MɑXYB(GI53קxQb"uI[U?8֣(glx'>+Lk`H7~q�{#Hxu C�U(P@Mi- {n}2YFRt&yax>`RpHv0DTVi zx0e2z*@** %CZ0I_Ёq"cXIm@9 {i4('xR4 s!*"E"Mi$ÆDt1�14a"Vw\c륆­sϽ}<�ݰ93pY\䢛x HWV]x2q=˙MjydiFbˑr"UU}{x"YgE"\|O#!,,FVZ BpF?6Dxb%|S[ Sa_5& +/,([[K 5Q 06OmzaWQ\d6k6O[ I5D42Af?8ct3c/w1K bO72a~`ODzo BD+"(3$ j&2{0r?fxI/�Eƾr-+ @'`ÄŮ\f!F-�IYf"!v6d{cf X|J?@g!M "O5bW ! t.2 8!37=V^7:LB+"U1*wy0m(g v>LHP"R)HXTN %+}T(b1KQe3eB0@QxZ/$+wYrs! Rez`�A_=$n8b Vzf(J!',+:͂ (|zBwE <R5kb LǶd&ff}1ZH`N (>KI02!b8MiXPQkȊG^&=?b5il7Y1E.{ LZTpNņ ,FK!dtl#]>T106+sCemHCVrE3!GD׮"Y%ŀ$ےA3yuS4)1'.V̟% 9bd ‰['3SZO\\L!㜑l $ $cL"DUc勴~)='  QEwi+tx5Mox\ }C%7rL1#̕ptn5Xh_ÙQͤk]%@PyH@ .Š`$Dō\x:@hjPZq >\tHWަg/I+U)�Ȳ%MS A1Jb5%� �%yz֫%M ~1@6Pzޜ:M1g>݈nOSr<!7f}p"Ms:ęLH<�P UG!gmMDs r lg˧ e�L i¢.d@Eh?~E '(Q�Y ~o 7,S̒/|[S%k[;#- Oa&LQ+"NnT2'C&ʮ(k2HY-9 y&]7\\Ya%]cIAYwe;Z>_/C@D\Fr= 1xm �&wYvqT@g" y� NICO&H\<Y5vETp0j Ԅ.YZ!`b"\v57%Ducd2 p8XAh ;e_9@)=ټ�]*, V4<)D_P< F?^F^fȤ={$XP; &oi Z8Q=[@EIap DI?gud" �''ˉcA=Hǁ>F; g  ?}3ǵ�t*l�[BWH~vDH'B"lDyi+0K0Le(rh*a'eB;;+lG 3q:<{PSA NB48m+# h}CҶpI3-hv&K-A3XLm,lF vS$ǒKєƸ,7\O HYy[⓯PH>mK kP#l*PHPkdUL}Ǿnu3p N Ֆ$Iy󂑘xQQ!V3J�KP+VRMrY] ~\ܑNYtDn๰!!$IvK "TIZqI}ICǕEOXb4e\136k# "F}C΁,!�=z̮8J4J{o!P .€R4eIt9|fQU.MӒ]̉0Nb ,�}atCM,?mC7-g70sI >{%Bh3"G'1A2۳EG 隌cq VF# D_sFM`@LDJr.ُM,�;D$̄-$ =q2%% _l)UC&ԁl3=CDpI+)J FV{!E"ny[Y5;ÖD~0Kvsl�8!B2V W;64}VAX&AA<g6UV+$8LAq )wRéq3r& "߫pQH1Rz⣕%|a!ڣW ;ˏI�卮2\ "%#`]M-N((8LOX]y{�f޾',-gtVUN"ߙV ?+AD:Pr̠8ձ7 d-OH~M<f8փF* yU +6.ha,:+zy$bJ0{܋$KU%Q'(ALn)R6<d4EfIdeqe!�tî}%2h`Emki2lEJ3<nE<aގ_ ")!q1 n[8'"6JXX̣q=b2'PWchk&bx|ab`+N2r1YځͱvjJS MOu2Sq=IF$2EΈY@E\ .+ .f,2rJh{өZJ'$ߜP+$/GP6T#锎E[/k�A13�M :(A@n# w/#ҕb]CD�,Lϴ4d8L#&~~r%0&&)4oxvm)&٨׈z]Rs B^Ap(["Ies%ZZn2!HC#V' :p+ϫ^.AU4%YpicF:,2:\.KdðfV(9DT>D,A.t("h L"팃ʂC2[A`BIX"; � 3$XZĒx_yܴ)i p'[ʣi3/DwYy2 E߷,H&텈o1k"ŶBuAP fd3a*SFǦ4j4AH5lyet�䋘%ܦ^gR NݰHĕei]A!ڵSx*!BAny"H *7 ر5,QisXEED%!q{b !!; Zb09UU}`00C�޲L`"l5 q`JUI,o`I"R~c^qDjc�(&hm�ʐ_PRVsX $tcYB [ˎvP�8sq _dzjv)7^toJW|w `'GXήU<y1R(풦 <9 ?XJRjF׾Ku lq4X`$ 3�+7j0*U1AaW_9V}'w:_Kd(h.-^ Y"r$a A۵ Yeeyft920Ii)@$I,]̤EeraB$dOXSX8j -k$`f>{&@?\jDZQ݉ #ٟ.d}5BS`*uqʭB�'9g! K!dD A0l x0Vm<Ƕ[)[!bY)حNzb$jpajY$}l<cYZ*E~;t &+c Y!l0棈.BcoX DlKӬNN9f%[+sBFʾW#X`@񢽚rHUj( `Qe8ӠO)�:}`$) ְg�9�L !=`D^@j(c2pd sx�,rf,.!P " PR"0P uT^@3hC+GI`V! pȓ|@Z�!GR#s7S0f~]/RK4yg(,$4]LBb2P,K1MFŊAl[`�ΈG0Nd6_9N wPu�07 +-DLJ_y lhDSA4xyCʓ̀¹4K<50G_!+V)<J�6D2iű%7ᨨAH6U UVxp{6yJFo$Q.b%\ #4by=p�8ɻ)M cRLd{*^F2t_95([ d<+gH814A,1 45 q0י|aA$쑓KA)M*)¤Af2X̡tw(ZPoZ׌hV9O϶[DKт7LxߏhɴѾ."NItˬ8:h!7?Q "Fq q ei3?H?κ!"wpR}F� !)@&OO|R0Z?F(y> vj) L<0f%1:jDG)v{FI#Vś;ɿ dK1qr*n7|�1;͡"| ř on#>ב$A+|u8Aa%{)R@:uh`?x}nyz [@J}#"woc!E@-HX +hAZh Bb^�<n4@u$K>'rDF*L<8p0A*rp"E^;h /]dlK<b;o\bGc4OP_rHbk{@ h!!v65`Q}<@z@L̡*x -z^"bcJLq@/l1#PQ1uDMcS3:<YЕ^1lM&%*Z T""Qx4RgU#,Ef8x}`rt.:PQkX=Az95i KL+duUt$o.(hNٰ$6)�0%Grro'!DC`(TX G-S8 ;<ও*$iܘɕ4Sa(Ht)/TsYɔ5_4V (0e'%E@!qM); @MEoD�x M ,2j Fu$8Sh08F)\S'_H 4LYD` C8Equ]#3|n�i^�3Y t&! 1_|*מ+K87 p"yuAxH!fo%Tϧ)qpj .B v!�TTp\Hf8ZzEq_> c=2ُ7KGɲgV39#'6UgA "P)DXK놰Y(FV ͐4H*~FA-;"kT1E ]kfd=mB*b=H'[ki`}I]ca05*Y19h Cm#PIjb*9#%Wߘqjl|M+2S1M)S�ēLLd�+80)Z+3@v"&|`5!%tQ.<\FHu\T68R 1Ud.dx4q7P 70AFx|IA& ?qY,B0 |;DYo% Mzwuzӆ)NLBG* %cӒĀ\O]ƨ e̦R"&Z! Z˃ zf�oYʊhtd^yVK0o#b۵y' >u#$IER-%Vyb3 NB7ḾSh)4刉Є'$*l4I/T .Ώ`W!3L,0 _C3,Y.!@rylH(l�[hY |anQqT5Fͭ:ŘQNF �pDҌJQ"xĴfXM  2@~q8BYzтV4 BuX%}6&I"\C/?,mn�i $Di/=#7%p1TAIԝeC7sHBɹV!|^Iuϡ9hHYpkٖyс.IJ&<vhvA CA $(b14b ǾXAq13e1ʁ+u8S넓ȊIiٶ@=�&O92YBhu4x 8ɬl 10E9(a }ba BDoÅD:B;�2BBk:xH4L6D5 0 ijͽcP0DC$&M- B$T!G�id*(7 hgu#w1n$EH䧚 Cw9%dCƳ3bllmRh?\ಋ&#|sJL턎4›=M_]&=E А|~.qM`=&=\"X0f!CZ!Ǎ䡄 yE8b`d@`�8Jhz"" 73d'4$'UAۤH"&�zm,0Hc0tĕ!V&@Y^h =p=/+ y3< !H(8Ф%1)Ǡ/QlAϏ 4a'j'4`SMcm]s dB^jYD1@Y#h)2y3 ֹ <K/7i+9)4&|^'͝],"k :Q'"&qqB?Ő~=.x$hz ~=+)qxPL^k "d�U`t!ce�sgÊLUL/vH޽94d$|u k +;d!Rw.`DRd*D DbeI1i K?1Q9  s"Hk'(3(J5WR&pJ+뜚yj)$z8&m(Ggr3.?b|=R+$�,`\FU[g 0M#9$8=Br:A6\op1a3|,BIXR2{`]xETLb癿M̽L`abH2 pNжxK tNM`͈u hK&S@kʛC_xQy3"A/ ^ e+˩Zcz}8D GO*DBzo 'OH)S%bA H'�qЖCq u8b85#�,LB3Fʠ!) 62N̓X%2!9(` Իe]#Y*� fi ﬏X(SUB0!dA7vS0\Á;qs,Ad(::4e:^a:=[1B<XK`xɛ-X5Kj`OxBJbIcО8%I+d$5rf'ww%U,2|8)M9+@AG>d ٌCal>0;1�행"=0S`Wz'KBW'ϮFH)!9�ٴF !rxBѮucF&\;qikКSARDSmPAP_g^MrqH؇-f e> m-*&; D|rnjf}ɕ$;Og|do 5SzI,Tfs2`& AxU ^Y8AKS}4NPry$՛5޹b2LvpyynL}rVPѫ[sJ& ) 2%5L`D]Q1Z=ET9 <fXE/<yD"LF٬(\uw 1P9,^3N[&Cc/]H nksxKNJ;yuPP�,H->BRnIC ?| %yVcMy1p{DǧR8H]WFY7"z]<4;ȴBdT174G`Br}wj`Hi=pY(%xa8)g@ =8]�P(DoO)G�x'L~q|Yȹ1К)UAK[(vǾ&tMV!|kNGR/ Ѽ<~0!&t@"zӓQPe8tdO hhw"@A+:Bȱ-d1('+CpH$#PSU"8Q$Jtv$R\Ӽb;q.7.T &19=GG"3&mC![ٙokCeo S˰! b5 � Z!@w&9΍`T?*GEޫlX6X0E>&hq)G]!2&>G$5gQ$d+re>L i! qFXDvC ݳbT)K<qd(&5c &At�+$Y?!2c;yd/'Hpk{&c�H6O ȁA)L9CS';2 #mʒPN1`�= ‘<ba2oh\o) 9?g(DRO[ *$M|w78]A8o�NVFO.CQ$kqZ#2JDyJ}1q׮ 8LE$;8cZBAR;�pi%it[8r+QZ2IyMz�,QP-CDțb~Ĕ!G<cӬNĆ8w US$PKj2R Bn"^ Yַ#hŸX9ZĔ+N/{н~H*g*.NK? @)V#n6E1|r Tf b2VaQfkpX&z&An"H*25#Z=+H(̚Us-{NnH;5( B,> dMQW6hVQ" t$N2 TQu9߂/Du5b,1b @aG�W%o?}H2!$ <:Ƕɍzcd};#|3Ah}"a4O�<3Ì~(c(e1n]3!~%r,l#)In#RѢ0cKYT>ёˮr, Ǯt)bFe|x 7׌崁\)2,»:tD(}V02Ҥ>âOoSMn<`._+H&OZGt]B>`F:$ԀI/,rh yX0ID,3\Qh :GrY9W" eKT)T:pN׌ B�8@pT$*As'YS{1' %} q_2zF_Ʋ�!i !EP.CT;ВBbIz�_bx%p 8 0mD7둦)C P$sV'y X4tERʒP1%wWn0qdK+Ok #G `�mf+X%LyHsԖ-+9(F ̅!QX0%Ci< WԋQ,9]cek Ml{XOK!c&�"J"|k^HpqF@Ȫ/(^ɡ2&X1x06d櫿{űS^(4vg 8D%7#Ҡ]x)h5&*f38YP5__YZd9* 5  fA01]G9]SJox '[!5 mHC5T;vi(S ?xȈ׹X"�R؉(5_a xzb�@Yn(D T*J$@Lpf7=A|d@}8BsƸŅRȊ]9m5zm&BHE37 [LI7,7ÓMЂyo@*C,`KO$RDZ`F1 [\JtUē>q)i�UvHvaD \Y"K�rAv X%S�@U^ͩ1\TQH!꽼ᐵ{p5ֈ*6XC[!{b'Vf4L;׮5 , �zp'K_ Ȉe2) H7G o;_Ȍ+bB&fë* ]GCӉȑ}10I@k|=::檱R/!7϶XcЃ#kl#Xs M@ %'="hbYpL-q4BQ_A&H"q% Ѓ @ zk%Y?$) >L-5OC^pȩ<gEmPF!$;qJՃ}%wSCj& `JHRVb[%xO)KGFP<c[2`!0;8@2wq/O[`^KUS{Ј2@cw K"B3DO�)+O'46Tn䄈Q\ F$ȏ=bIXHGN>9(X jC {r}APu`E -M.D0W #y*�C$MY5cFXrդ-d fq:`6E~AK"Zq;B<iь"}- 9 i|epFBSɮmq݁k/ f<K\X}t�y5YLs ,d~EH<LꉏၐL{oZo�.W8BJ+٥^IaquRy $$}ZGT$GvdN3DGaM,KXʓ=ỡ>@=`%P_/1{{W0PB[N2?|B1im&8uo:TfvLY9e|CYCe7qq&z1 Kv:8gI$N}ňgu5r�XۀRL\qQ0aId�  V`o4(sH"hbB9;|LpM|dN]e9[Mc`vXKPs#3vϜkӖ@{W$&F`sdx|bFPka5NGQsj,Xx(`܋SgwW3xnDa9R +mo ˜/(>_lLbL4h=k:Fɺ0TkJPQ,)G3J$3 Ie$3׏jrPx 1?|w9]h4l34qȗLlȚ(wIȖډ+ğbZC-Ƀ% <֓h60q?ڢ—aRݽWpl"r!@o~4phb#�1NG_$x nS 1 !DCU+ KNJpdU=_sֱ&Oa:zb|+x 2t=ZPDA l#CP6m! ӐnD{^銕 _qDVb'V͸R9 ]cNl:tB9"#ቌ`=6beAiZ QqN?[P(TÞZ(Țr3SY)W:dh,V@ h'݉P’6d`*9K-P')ЧC!M5as_m%OEDܕXss!?{ԐRYհ n冄Nd~FmmoBKpc:8 쭈A`bXI]R(%*k 4$2!ïJKB7n U2U^Od X+c0+N?x 6`M*HLpI#Y.L ߚt ?}�R�'J+�3bPRRt`l$ax$z )^ ߮1�g/ΰI`g,磃 j2@Ȳ%�0yj1P_[ńjh_UϮ5Z@HZqC432 lK)ZƇ�^ͨH$-X WWKaPO+g0D6|U\7;`\r)@3x0ERNw2V#@[I4mp`褢Ommf0$\QU0ٵ<!1Jm'#!ֵ{3d!ܽɌR#8xFUk$((Fc;  BC;Cru)tv0hj0JZz+#E y33@<F @T$1=#c4B%QײWCEB2}\pd Zg+%!0Dc*G8x+L:4Œ A+ }9V`@Ű'2rJjHǞ&28JJ,L=EɬI bYP#aHL D>qJÀKn""+avD�o ZBYkeIV:v=L'y R$V(HVqK(6)eixA)1ҩjb:ȆHcOe癜!2$T8-ƀaY ?%# !,0 QQ.RtF/O2l:9_<S bG#ryD@E}c(=E cg�%>p ܥ#LJ|o&';kǃXQz{Ɔ}zKA |0Ĵ Eךa6VX&r5pه4G13;o4\t*߃j$zddЃ{ȋ&'?x-ǿ G�  HU3Gb4j ~rre-|Ȃ ƱXv R$#yP'FS0X+`RQ@g[m$^x:]'2c{W#x2FѤ3D*}pd9v*MA]hUcDS/Gے'f>TG62q؜D9D\͒V\8H5$U/:�g RP �ờ07N&0 J&qߘBq"\^&2fL.&8H"z5Y6X6FsHwf\Itk`fT*&+O=p99Q^OQ~3XӮbD&B�Jwv�8IƘ{ȲH 9΋dX/:#I%j:9;|)d~2qBH=sPmgHH+=pC># ,@eX#j iԘ@0a6g&}E ngI$}vEj'I$Kp6Z`BY VGW҈6R_y|wrnq\ـ�;w5 $CQ0XnbbA*B9#RIIf66zVcPaI@"J$p �Id-{1R(۽zo%.'8"L7JLF0qǜHx:=̌ouqGxqDؖCp4�셞Mw]_GhDZ@WqBLoMȈ9w'!P+f� 3�sP"",>|`(L /'BEo߯D"ɀ}xށH|*LWLMHRD|zctd3O鍠psx. P_i0r~>ٶ�ru'Ɩ;Ujv P$mYoҐ]xv@qLiNqpW}K}VIbͯm_?823"hzLnu8H�=Qgcx)$~0&oɱ.(̞zG�|RS�%%l:1Ue87UkrF<aQZ,\VNAȖՔ}aKFJKd= 0y<QҊ~�|�Y*f- XNA9�W){S1Dc[o1u`HTȒzXO"g\V41h %#xi~J?k<Ŷ )( ;s>I|4O )<snG L__sqV”X2#W: mT)|y$.,uhIĒ ƂLy?�}C-sAG5"5\_+@ an RSX� �aM8R8\D9Adh\&� a4XUѾ1H=E` 4q62cJ?2f 4>gGP2o* %]+0b'|> J}MFj ˕4MSʲyh xzF5 &pMqGױh]dGHQ]tUqo>q!U~p,fOo&�d}+uI]`(r&6 n#}L{;PŸ80(._JL5,Ǘ苐W:"[e'\lӆdHbq¾MOXf'=k@$̢7T9 !0ALj`#̚ȘN㩂wrOab\6>I"O*$b KG�T60T꣬;R/4{Z�b)w�`[!x)[ŨALwaV]]|�#2oi 1L2Fz?&\ of+ 3frԄ v= +k1pzaqAwL7)gĢ1N SĂg)T+w?8Lz012Q6y ?Ӎ,!缂i�j'OMAFoZd 38g)$T_liDd;2n4%SNJ'^IJ1ܺE%@v7aRDRI(FORJ"'&Ay}wADҔ$�9S CBGVC^! ǐc0)ROAsgeaaxa'x}l."TX�L4L3>r~6TWx oϮ!=&K'c#Ca$ ylhtV}3|9A[}$C1j(b0`ӈrP9YpK:12.:,'1#D}zSU@JVpН 5QiBCgEo4J#b ܇`4A-'YIˀ5rny84Ev#J^?+  A -!b)~bX!NÑ3?+b%t"TD?1{m*键!{dn~0 =u`| Z>9S"de/Kd& '׃LLk*WK%0d=R" %Xa׍�q!FDD.=x]mEkgɀz9E~1e%YS%7d'00F*u~O8 EB!Op4Xΰ%|]d^1YNx/ %R�#_1Zl<et4C{G3 hٿ\;)љ4df_44 4, baJ2`8c0ѼfY(|=-Vc}bzXNJ-dǿ8B*lj)uDA55YNM9∢-S ٬n2# A(G457M休r0xz(#u*#[~p�gdI�ό%k5,^Ȧ�'3p5KqFB" v1(�B.A m 0"@gp4bw3 �m7\BRjcA;tIB_ !uCåCW*D ~b oYdW^,�z)j۽_ae&>pQ۾<caI� MD:{h(+!ibrcS߳4Ć{50W@BB8l!]~UåmaBD\q#޲@~F42#<c%mq앋>$[=w #Hyʎ9�]1SDѐ$G`o`JcXKP2)6צ&t]M'~p�3{R}2)�7> @(f-ˠ".Ha0c&NVfCRY-lȕ"L[:U0g h1P]=rs`Lӻe/Ι 9m]ppk:e0y{<�c1 ;D:8r1T[b.<&XȤpd \r^ l<qS�k8! q=JEz_y9e2 tUu�I/-f0N>&1sKJH͐ 젃¢49[�Vk+`siL,GxR'9̖`D �0%LRX7fȪh%Ho&<]w |d$C"9A7׳Ff{Ś�364ָhT_oQJW5wL+a@Z=1;1Z?$08#>rKaZٍ ČY܉'Hbdִ~1<`<D&nlf;$PA#ruTsLE)wH*B&1,Qe`q,8%+bgef#mӼl P"mXa>%j6Ì*'d/jk#"zdh>IڈgzS37Ux�+|p8䩯"%):? +|RC2_xÁ.^Dm ]D.yaVGcpLCm+ J*d :� 6D'jp aPڭjp�;`Y +\Lw˲dH6\!GybãZF`.N cb6E/~Iʂ01J7@M\vաU8% <'*(kK0D/ 5,o8#Cfg{7bHo&" Yh! $*ibwr⾾<k 4 ((\�Ģ8~{&`q/7ɧ1\]�|9N SI�0.kXsw@TeȎo 4Z E{<ȫFښ&\k:�Hu*L�gs=z`={|26!!ah�DERؤ 8ǭ<!m;ljB Io-@o�V4W!l1VRݧ8,,ځj2A3{ő씗|i@կۄD9_L=~rW(GNDo>wӥ#Ek$Χ%#_y@,e 2~?0;D' @4Nc: LqVng6mRn%ω3`O;Ocq"ShT9@&O]FNp$?I[,Ҙ#Pu*m_l(jİ*^9slF6JqxKEΎ'pq$]dXЁp4J4t^I)'*<nCh' 4�j':&-O�yh<0K+挵:c'釰߮חFE4UL߫\`zN`lHI 3^Cy<T 2},$*M$l!8J@ҽ`�3J5j)0aP"Egx#�5铸)iF%0CJ'Hd=.Uc4A!챕PxJaqc@D_*x| CqY= ^C2d^ giO4)ɰ%K@y_9.nn'lM Iȏıw[5s5~JqhB,qW-[ > KvhmVUhDK҈f@HJ8٠oוʄz )V75$=Sƻ�@G<O ߫iD`: lD`0KsINCXrH&cS)F pRL8S&Ϯ(:Q7FZYfobc)*a a>0Q!*sa�G*r@H='t .ij65GkPBCfM^q)L%+U"Ȓ =)3̀"qOi\O&(xKsWI|yȥ�q}<uA%+фd(%-ԜR& bNq'XdC �ߡԱ5 )';�Xs3 Lh ~rgmrFEa`�0wDb!B+H]!` l؇0BTd,�rt#~.'f)3`)WL\ U>8xx� _yˣ!)x9Z n#oHzxh~U"M1lK -,Ƶ}?dW]<y ?t~ⲋ`HBSޟ\2m5c!$&b@Cίؒ�YW+ma&9^w} vXdaupβO]ORn\7 J>�ݎY]JbX-MƧ!LEƼZ8JX갧8g E(ҷNe<Kw5'5T ,Xxv-L]B  dRk&`" ߮�=$ذޣ*`˨) ǾjBGO2"W+Dqrn s׶OKDgm/11\RL!Bc#EMgWD{2:PqᕍMF. [ɢ4NαH;VB�NoNVejb!{cӹ}7�"Mx` z{7]3ā%AH~Yc/Gl"L{% #A`la;)+2r Nɘtb7eQZR"yɓJ u')"S}XqzdZ,2.V�|u�qBZ�K $)%HS>Vkx[]t6RFۓ}b Sv&<K$b91p'tkAhmGW&Rb3-:c|KxseudJ�~ ڿYbAb1=^@ruHL(]# g+MJKq d^,mEk$N 謃ߙ"0ܳ�p�J85 X~(13S%1qR�MWP>d:x)3Ê�$ɦ�!;�1 q!sbܮB Kg2 UZ, x@ĥUD·P.EN)shLox� @]š  V 2E7l-dTQ{4ho1XFϜ# #azA喼?YN{z3ACu!(a`0!Jq"dnN]EQ8qWb=&\3=P׾Xlo#*8|"[3̸QX ;ʥ<DńKdϲ0ȇ4' ^pֿ{MH\db4^_1 b: 1Xh|M3s1%Mh,Dnd;E2F Zvfg$(-UszÖezZlܦq]I26!ֹǶ%!'7,%.~cHBso$Mwa5 @*d9ȢeN'!)#gY,.e'9F.ƑGbC([ h:\tBOFpqZlU۔!JQ9)�HIocw"N1e[X#.GQr%ѩ=0 |[p`rĊg 1 xQԀ\bI* ~7+a7}1DKV8)Ęlkgq>2%p %\M.zB!~=�v@OG=1%d$1ˬ-+E/6*ȰK0o`W@o1#ͣ}Jԫ8ob. ܅o¶%+hoy-{:ȔY]*X˂(n8@ö́5,3aiw2?%N%&Ϝ$."3{M$Ѹk-7�Bԙ?kM3@`x<@ 8!^ׁw?t�Ḋ^2CAl’)4 ǟdj:c�mոsTz`B*UuPc|B㦠qsآ@VGRDوF:BE2!vzQЫуS 7 WxBB$&32Fw8g"?f)e"w'8H,&7ֲDWRY]+IJDE|# A=qk)%ס+"#ۚȧ1iEHg�A`J^ǟˊa(=ĨiHFCJ$\ J&^EyU;uTbU% YVGQRO7$U!v>F&g醺28K0ⱛß/)TFwJŦ"vC:- ]}ԇJLsn Tꌊ3NֽpW">r C92:UgSHLOė1 ey=l'E4ycH.Is±ħc|bǝ.ƣU{&SHmKE0_xLFK|b�4C'%FAN$h H'[f!�&)ץ}b8t8xKgd|M q|#Ad9@Jߤ9j&;aъenp0$xIHPbְ1 (dhc3ȟl׎`ƒ"3 )MQ rWi{2Q:x0.'y:8dYx!&\4#ꆼXQ79�Ź<?ҥPDc&4@lix%eJ0v}+OT]ws�k/Ǯi!e�OIY`4.�&Dj#d �6N4`"Xw @C0�/"_ `ahB\NF}|`"I/aOW <q<ah+~h)ߞ#+Im앒<bɐ%+)T~QBWPXaE2{LRTu0@DooDJ_]w =7ޟ&^ppG@R&KY ZK^¯ ;#$iQd!~8EiQu�cJUa5) gC n/nk#@)CLX"Bv?1HNeK/&ů~SƑ}9Ġ$u {EZ ܁+Ja N}�~D4*Ls �x J>SK(SƟH Jz!RVI_sǜ8Tg Rk%58q:O7T}"dLl!+~g80LA&q=_ ho t d:Ռoڢӎ&! 8Aj޽sPF?�Di�'9HwZ h󯌩5)1[w삻g"&Lg3Xb d_eKk[7P$zC?( AD\0UUC<ü=E$㈢ۼЖ=K$<nZ�o'Uæx�QҢGظ+8% zxQz㌉ EtuUYoy:;}S,a̬m:0/?xȴ8d'G$jv^1zb3r�PU x"`KSIjUˤI%s’�l>u8ujHۆ"d~y)t$RO o׆48e[͚ zƴFGCQ(J^(S'P=q sL`Q!$~A0UiDPΞvӸö$<2³D4^l$Whn;Nnk 1"B7z?Zjk8?bX[(&~0pf zW|aY&8q*LTcrv$2yhԽ 6:c<US�\HeL|"+6;bdZo Pk� v7C�w�ef^hiC��?Ad5qB=~_.,V>/Zɽ&k+IP1 ̲k+LY[ּ&2Z@*Iwǜ4(Du-ȊICg8l͇ 6 $A�^x"gو@Tu&Y8r ʽ at֎0|̘b1w'3%C(!gل+k);r'~-ĂI!!}_�5 <N +q>c:f9D5;.  @(]{^QE w k!Im2rv&U'Iz>$axQ \Dg wB6H%QL;ql*PDyIY&T_dz΢ `sbCR襃8(BO0D:B6#[:$;ǯx94 rbdu˓ЁYPFܿ9 G)q°&& TPe$e{mwbDi# KqT~ AfN7J㴮oۉ*-1,GAd y0%700Kn)LRa`ۼ,]LpW#1hXH;?[_raNC @c퉉q,BC3.%1BL؎-;"Q@Cx'=<%+YUMOJFZzGIW~N(D<4SG Y[ ΗJaN>2Y1. kORnjOdw j@ #"0#=2۱@Lz\"G*qBQ G�X: Ϗ8S :xr)őPB+(3ְd"V&̚X{�qL}8Lx _U+'"bJx?IK$JX $I�<Ji Lo!AH#;xzQiJ (nS+#38A034r%_^2AxY <K둍i _f@ <=&I#1&*9Ba]dLb%/ePDHV7~D&c OBRj֊B`p$o$ijx|}<jիqb yq0Bg!V<e VI_9 ,)zzwϜm߷8RDn~i&_0V8Ѧ{p5IZ&/X]pRDoAJ ?krj$Ho8ILGʊ$)|@ B'0e/%3|bT(%MB #A3`$U;P72q:!�KWAS7#,2~O/yf�ǦpT&{T{r |&JDY Lha{ņ*Fw5:1j8;~y"\/ J& 90۫x2gʎ";"JnjeFh`<9ȯfC {7~BӠ\AB5x7Bʎ͟/eZ=/X!r 45g`Y(ȑ`*k*N4N*?Xʢ+Ծb xlWo!b"X1C.X|C5دm^$ 8as5Q7~r$ 'H&HԥMM^p{d(;1U89/. N8!U~};tb#-t~q@<.8دr\缀] M,G�Jqaqp&L(e䪆KA/n.N'$ Ѷg{KI(TYdE sξ# Ho&~2CPox9[$jf!A1F1;Qvy\$6CO3!ljQIǼa$$[ :bq݈G�kYSUx,چ ɪrv}.=pr1NŪp: sie5Ǯ(,ʚgYW c1I;=rOXu_\G⤊�hyĵ lԙ* ِ_�KԦB(t+Ċ sg Xo<bG:DcG #Y�KdG|}c-3%1c&;Ď@u Q:وHf}} ~ Mh1BRbɀsFo'n'DƯXHxϛAa_|<HU!WB~p&ChWU[~z͒U lZ=0B2DQX�!hk3&'j}9ڐ�B.IQ%4X'Rz99VLd砆:x�0n=snFbbIѼ*8a0ED[ pH2I BFR$UMY%dOPVLsD*�c�qc0|~Gh#Pj}Y"M̞0H N)-dMS1>1H"&`3[5CAxb!mD§Kƶ ~s:\{㗬p$tlpjN𱃲%$vf@E0'5}8Dyͳz2MO .%{p/3d!J;h80(x0*g|o@b<1*P ȧ[PysBM^Eeq|zy*[8 =%cr~ fq s8ܲJ.c�`,;ɡ�Ym�^?cʡf° |; v>k + c4+3v6:� C3GR$] Y`}'{= μy[@ nu "n r<jt^JIH}Mwqo o,JP41ɂ yٳE"h"k/>La08Q^'#VPP9y8`%!\EbIwS.GQq18DS&L1%myMkIV$fa ڐR_W$bJ&bK󅖐E釬S(kq7Npeؠc5sC ' ];Y+_C�ɤ0h"\Rh.>r&ŝ2z=|� ]{r i$bs=ႈo'wQ_kQ%Dž 31f%�g|pWν?I[T%$a3}NE(i Ī+\b 2DR e4ęo An}x- $)`B/n{lx(Wz !;$Z,k A)~nĘB#Ty7a-W"sb)`&{c`e_#<z`PblR�GR>{Su" wQ",0I3x3s0& Qի㪾2"ߣ{Bb` &aNM""_XP&/bԨ>| (N�ܕ ޲N9O0V#EǾO )4R >n29u)10_�RY/ƽpH"R*-=Ӽ=D#A+fX=>L$qbی"?q׫dR$pKmaAJ"U-q+ t"DV[WǾ 8 ~5;LU"@XJSzuvCc#U,c9<7qPҚ!: ]OT.xgd}/<%=\( 3c JRF@JtM󄺰ʲWɊ+b)3%NEO!޲ !rL 1ʒ<'Xq\ IuJnE\ J ʊ}01RQdp�X��^ vv /|QID4TWREĬ63{eԔ] z` Ǝ'S3*YX vd PWׯP !g@:Ä8?LRZVuR($j;c|@"ZH-Sa:.B :/Md$$W5sKXAr ίp)~x% &0[0\eH~1 ܽ` ;%<J$1X#C!O]I_c_- o N5u H 2QxZ X(J0G2Lχ\H@c_|`F*P9 P]}\n ~11D r#C#Sq8(zK"2-!?PUUeB yTߦ!0E">*iPGCJ0j$PTޟ\v�@ %W`: [Hm1/s'T"e$f_Ԅ9@>~Mk #LDN# )b$ {߷(Sh;_$�CA ۬D6@CNAlKR?������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/favicon-16x16.png��������������������������������������������������������0000664�0000000�0000000�00000001623�14502137606�0020520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������(-S���gAMA�� a��� cHRM��z&���������u0��`��:��pQ<���PLTE6QDU<R<RUU��<S<R;S@`>T<S;R<R<S<R<U;S<Q=R<Q=S<Rvw<R=R<Rw<R<R;R<Rx<R<R;R>Rwx<Q;SF]<R<R;Rww<R<R:R<R<Rwv<Q=R�;Pwu<Q=Rwwuwys��<Rwʴu���HtRNS�٘:g+ ~RʴŽࢻ}>a_ c{LmIgbnTKG0-i8���bKGD�H��� pHYs��s��st"}���tIME|-���IDAT]PC#TT{s3UY%�B]1>¢�D$<JA J> OR"N1(tThTʤS 9uV( L#EIQF+(J0-U8j2QZ`hu;Ԇ0lXgS>џVk`p85 noy�d'3<,���%tEXtdate:create�2017-05-14T22:07:27+02:00<���%tEXtdate:modify�2017-05-14T22:07:27+02:00MH���tEXtSoftware�www.inkscape.org<���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`�������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/favicon-32x32.png��������������������������������������������������������0000664�0000000�0000000�00000001510�14502137606�0020507�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��� ��� ���D���gAMA�� a���sRGB����PLTELiq9Q9Q9Q9Qvbp9Q8Y9Q9P9QZ[r9Q7O9Q:Q9Q:P9Q9Q8Qx8Q7Q9Q9Q9O9P9P8Q8Q8Rw9Qww8Q9Rywxywywx9Q|T���.tRNS�<v SC7\2f$#ɛN1CVh{]��� pHYs����}��IDAT8Sio0\CBҤ1uעU귪,/1 ,õvp5 N+ǁr8%6BEBM9[4I 2D՟) EGGI!8Tdp6GI*Z]bo~n Y:(wJvD5w :P;3?rVՕB巤wjQWl &Hw/Uu&+y42 $]$՜~MV+9aDWwEiy|G&qC7K^^(9MgQS H"_.&j k@"1`WW-kv"UԽ3@9ۖkSr]F(<<lC6*oF|zӱ%j���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/favicon.ico��������������������������������������������������������������0000664�0000000�0000000�00001514176�14502137606�0017740�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000��������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���� �( ������� �(� �``��� ���(�HH��� �T���@@��� �(B���00��� �%��>T� ��� ���y���� � ������ �h���(���������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���> nb,])= G���<���I���L���L���L���K���I���G���C���@���<���8���4���1���,���(���$���!��������������������� ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���%<T%`-]+b-Q!& �R���Q���X���Y���X���V���T���Q���M���I���E���@���<���8���3���/���*���&���"��������������������� ��� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%��"Bd-]+]+]+`-c+Gs���U���V���[���Z���X���U���R���O���K���F���B���>���:���6���1���-���)���$���!��������������������� ��� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���! N`)^,]+]+]+]+^,c.](8 m���V���]���^���[���X���U���Q���N���J���F���A���=���9���5���0���+���(���$���!������������������ ��� ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���$�+N`-]+]+]+]+]+]+]+^+a,X$+ d���[���_���]���[���W���T���P���L���H���D���@���<���8���3���/���*���'���"��������������������� ��� ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���!- `a+]+]+]+]+]+]+]+]+\*\*^,b+M" ���^���Z���^���\���Y���W���S���O���K���G���C���?���;���6���1���.���)���%���"��������������������� ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���&/Q"a-]+]+]+]+\,]+]+\*\*\*\*\*a-^(By��[���[���_���\���X���U���R���N���J���F���B���>���:���5���1���,���)���%���!��������������������� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���!;d-]+]+]+]+\.X1#X.[+\*\*\*\*\*\*]*a+\%6 n���Z���_���^���[���X���T���Q���M���I���E���A���=���9���5���1���,���(���$��� ������������������ ��� ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���!C[&_-]+]+]+],Y0!W0"U-U*W)Z*]*\*\*\*\)\)]*b+T"+��d���[���^���]���Z���W���S���P���L���H���D���@���<���8���3���/���+���'���#��������������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������������ ���%��%Fb-]+]+]+]+[.V1"U.T+T*R)S(Y(\*]*\)\)\)\)[)]+^)N" ��^���[���_���\���Y���V���S���O���L���H���C���?���;���7���2���.���)���%���"������������������ ��� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���#% Xa+]+]+]+]+],X0!U/ U,T+T*R(R'P&R&X(\)\)\)[)Z)Z)[)]*]'? w���[���^���_���\���Y���U���Q���N���K���F���B���>���:���5���1���-���(���$��� ������������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� ������������!���'���'P!b-]+]+]+]+[.!V0"U-U+T*S'R'P&N%M$O#T&Z(\)[)Z)Z)Z(Z(Z(`*W#5 �g���\���]���^���[���X���T���Q���M���I���E���A���<���8���4���/���)���%���!������������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���!0 oc-^,]+]+]+]-Y1"V/!U,T*T(S'R&O%N$N$N#M#O#T&Z)Z)Z)Z(Z(Z(Z(\*`)T!' ��a���Z���_���\���Z���W���T���P���L���H���D���?���:���6���1���,���'���#������������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���#9V$b.^,]+]+]+[0!Y1"V. U,U+T)T'R&Q%N%N$N#N#N#N#Q$V'Z)Z(Z(Z(Z(Z(Z(])^(H ��\���]���_���]���Z���W���S���O���K���F���A���=���7���2���-���)���#��������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���%��"?d.^,^,]+]+]. Z1#X/ U-U*T)T'R'R%Q%N$N$N#N#N#N#M#O#R%X'Z(Z(Z(Z(Z(Z'Z'^)Z$; r���[���]���^���\���X���U���Q���L���H���C���=���8���3���.���(���"��������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������#���" I`*_-^,^,]+]+[1"Y1"W. V,U*T(S'R%R%P$O$N#N#N#N#N#N#N#M#O#S%Y(Z(Z(Z(Z'Z'Z'[(`)W#2 h���Z���^���]���[���W���R���N���I���C���>���9���2���,���&��� ������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������������!���&��*O c/ ^,^,^,^+\0!Z3#Y0!W.U,T*T(R'R'Q%P%O%N$N$N#N#N#N#N#N$N#N#P$U&Y(Z(Z'Z'Z'Z'Z(](_(P% ��`���\���_���\���X���S���O���I���C���>���7���1���*���$��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#- _a,_.^,^,^,]-[2%Y1#X/!V,U+T)S(R'R%P%P%O$N$N$N#N#N#N#N#N#N#N#N#N#Q$V&Z'Z'Z'Z'Z'Z'Z'\)]'E~���]���Z���[���W���S���N���H���B���:���4���-���&��� ������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� ������������"���$/U#b/ ^-^,^,^+]1"Z3%X0"W.U+T+T)R'R'Q&P%P$P$O$N$N#N#N#N#N#N#N#N#N#N#M#N#R%W'Z'Z'Z'Z'Y'Y'Z(_)X$> n���V���X���V���Q���K���E���=���7���/���(���"������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������%���"5 zd/^-^-^,^,^/ [3%Y1#W/!U-U+T)R'R'Q'P&P%P$P$O$N$N$N#N#N#N#N#N#N#N#N#N#N#M#N#R&Y&Z'Z'Y'Y'Y'Y'[)^)U!1 _���P���S���M���F���?���8���0���)���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���!>^(_.^-^-^,^,]3$[4%Z0"W. V,U+T*S'S'R'Q'Q%Q%Q%P%O%O%O$O$O$O$O$O$O#O#O#O#O#O#O#M#Q$U&Y'Z'Y'Y'Y'Y'Y'Z(^(I& ���L���G���F���A���9���1���)���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���&���%Cc/ ^-^-^-^,^0"]5&[2#Y0!X-V+U*T)T'T'R'R'R&R%R%R%P%P%P$P$P$P$P$P$P#P#P#P#P#P#P#P#N#O#R$V&Y'Y'Y'Y'Y'Y'Y'^*\&Dh���=���9���9���1���)���"������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���%# La,`.^-^-^-^.]5&[3%Z0"X. W,U+U*T(T'S'R'R&R&R&R%R%R%P%P$P$P$P$P$P$P#P#P#P#P#P#P#P#O#N#O#P#S%W&Y'Y'Y'Y'Y'Y'Z(^)Z%9 K���1���1���(���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���& ��'N f0 _.^-^-^,]2#\5'Z1#Y/!W- V,U*T)T(T'R'R&R&R&R&R&R%R%Q%P%P%P$P$P$P$P$P#P#P#P#P#P#P#O#N#N#N#O#Q$T%X'Y'Y'Y'Y'Y'Y'Z(_)X" ]���%���'��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���, jf/a/_.^-^-^/ \5&Z3%Z1#X/!W-U+T*T)T(S'R'R&R&R&R&R&R%R%Q%P%P%P$P$P$P$P$P$P#P#P#P#P#P#P#O#N#N#O#P#O$R$V&Y'Y'Y'Y'Y'Y'[(Z% R���#���$������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������"���"7[&b0!`.`._-^-^4$\6'Z2$Y0"W. U,U+T)T(S'R'R'R&R&R&R&R&R%R%R%Q%P%P%P$P$P$P$P$P#P#P#P#P#P#P#P#N#N#N#O#P$P$Q&T'X'Y'Y'Y'Y'_)5 ���$���)���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������������ ���&���"7e0 `.`.`._-^1"]7(\4%Z1#Y/!W-U+U+T*T(S(R'R'R'R'R'R'R'R&R&R&Q&P&P%P%P%P%P%P%P$P$P$P$P$P$P$P#N#N#N$O$P%P%Q'T(Y'Y'Y'Y'\(S!:���)���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������$���$?`+b/ `.`.`.`.]5']6'[2$Z0"Y. X,V+U*T)T(S(R'R'R'R'R'R'R'R&R&R&R&P&P&P%P%P%P%P%P$P$P$P$P$P$P$P#P#N#O$P%P%Q&R'X'Y'Y'Y'Y'^(/ o���&���)���"������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���(��&Hf1"`.`.`.`._3$^7)\4%Z2#Y0"X. W,U+U*U(T(T(S'R'R'R'R'R'R'R&R&R&R&R&P&P%P%P%P%P%P%P$P$P$P$P$P$P#P#O#O$P%P%Q&U'Y'Y'Y'Y'\(K1���'���%��������������� ���������������������������������������������������������������������� ��� ��� ��� ��� ��� ��� ��� ��� ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���!' Ye-a0!`/ `.`.`0 ^7'\7'[3$Y1#Y/!W. V,U*U*T)T(T'S(R(R(R(R'R'R'R'R'R'R'R'Q'P&P&P&P&P&P&P%P%P%P%P%P%P%P$P%O%P&P&S'X'Y'Y'Y'Z(\&" \���%���'���!������������ ������������������������������������������������������������������ ��� ��� ��� ������������������������ ��� ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���$ /R#c1"`/ `/ `.`._4%]8)\5&[2$Y0"W/ W-V+U+U*T)T(T(T(R(R(R(R'R'R'R'R'R'R'R'Q'P'P&P&P&P&P&P%P%P%P%P%P%P%P%P%P&P'Q'W(Z'Y'Y'Y'_);���*���*���$��������������� ������������������������������������������������������������� ��� ��� ������������������������������������������ ��� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���$9 uf0!`/ `/ `/ `-`1"^8)\6([4%Z1#X/!W. V-U+U+U*T)T(T(T(S(R(R(R'R'R'R'R'R'R'R'R'Q'P&P&P&P&P&P&P%P%P%P%P%P%P%P%P'P'T'Z'Y'Y'Y'[(X#D���(���'��� ������������ ������������������������������������������������������������ ��� ������������������������������������������������������ ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���$;])b1!`/ `/ `/ `._7']8)[5%Z2#Y0!W. W-U,U+U*T*T)T)T(T(T(R(R(R'R'R'R'R'R'R'R'R'Q'P'P&P&P&P&P&P%P%P%P%P%P%P%P&P'R'X(Z'Z'Y'Y'^(1 }���'���*���#������������ ��� �������������������������������������������������������� ��� ��������������������� ���!���!���"���!���!��� ������������������������ ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������ ���'���$Cf2!`/ `/ `/ `/ _3$]9*]7'[3#Y1"X/!W.V,U+U*T*T)T)T)T)T(T(S(R(R(R'R'R'R'R'R'R'R'R'P'P'P&P&P&P&P&P%P%P%P%P%P&P&Q'U'Z'Z'Z'Y'[)P  8���'���&��������������� �������������������������������������������������������� ��� ������������������!���$���&���'���(���)���(���'���&���#���"������������������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#$ Kd-b1!`/ `/ `/ `0!_9)^:+]6'[4%Z2#Y0"X/ W-W-V+U+U+U*U*U*U*T*T*T)T)T)T)T)T(T(T(T(S(R'R'R'R'R'R'R'R'R'R'R'R'R'R(R(U)Y(Z'Z'Z'Z'_' e���$���(���"������������ ��� ��������������������������������������������������� ������������������ ���$���(���+���-���/���0���1���0���/���.���+���)���&���#������������������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���%�*Hg2"a0 a0 `/ `/ `5&_;+^7)]6&[4%Z2#Y0!W/W.W,U+U+U+U+U*U*U*T*T*T*T)T)T)T)T)T(T(T(T(R(R'R'R'R'R'R'R'R'R'R'R'R(R)S)W(Z'Z'Z'Z'_)D�.���*���$��������������� ��������������������������������������������������� ������������������"���&���*���/���3���5���7���8���9���8���7���6���3���0���-���)���&���"��������������������� ��� ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$��� . ch1 a0 a0 a0 `.`1"`;,^9+]7'\4%Z2#Y0"X0!W. W-V,U+U+U+U+U+U*U*U*T*T*T)T)T)T)T)T)T(T(T(S(R(R'R'R'R'R'R'R'R'R'R'R'R)U)Y(Z'Z'Z'\([$! Q���(���(��� ������������ ������������������������������������������������ ��� ������������������#���(���-���1���5���:���<���?���@���@���@���?���=���:���8���5���1���-���)���%���!��������������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���$ 2Y'd2"a0 a0 a0 `/ `9(_<,]7(\6&[3$Y1#Y0!W/ W-W,U,U+U+U+U+U+U*U*U*T*T*T)T)T)T)T)T)T(T(T(T(R(R'R'R'R'R'R'R'R'R'R'R)T*X*Z(Z'Z'Z'_)< ���(���+���#��������������� ����������������������������������������������� ������������������$���*���/���4���5���3���>���D���F���G���H���H���G���E���B���@���=���9���5���1���-���)���$���!��������������������� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���";wg1!a0 a0 a0 a0 `3$`=-]:*]8(\6&[3$Y1"X0 W0 W. V-U-U,U+U+U+U+U+U+U+U+T+T+T*T*T*T*T*T)T)T)T)R)R)R(R(R(R'R'R'S(R(R)R+U+Z(Z(Z'Z'\(Y#>���(���(��� ������������ ��� ������������������������������������������ ��������������� ���%���*���0���5���4M) uK���C���K���N���O���N���M���L���I���G���D���@���<���8���4���0���+���'���#��������������������� ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���#>^*d2!a0 a0 a0 a0 a:+_<,]8)]5'\3%Z2#Y0"X/ W. W-U-U,U,U,U+U+U+U+U+U+U+U+T+T*T*T*T*T*T*T)T)T)S)R)R(R(R(R(R'R(T)S*R)T+X)Z(Z(Z(Z'a)* y���&���)���$������������ ��� ��������������������������������������� ��� ��������������� ���%���+���0���6���3LOb)O" y��N���M���T���V���T���S���P���N���K���G���D���?���;���7���3���.���*���&���"��������������������� ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���%���$@i2"a0 a0 a0 a/a6&`=,^:*^7(\5&\3$Z0#Y0!Y/ W. W-U,U,U,U,U+U+U+U+U+U+U+U+T+T+T*T*T*T*T*T)T)T)T)R)R(R(R(R(R'S(T)T*S*W*Z(Z(Z(Z(^)N 4���'���&��������������� ��������������������������������������� ��� ���������������!���&���+���1���8���3KO^)Y(])[&Dq��T���V���[���Z���W���U���R���N���K���G���C���>���:���6���2���-���)���%���!��������������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$��� % Yf0!b1!a0 a0 a/a1"b=,a=+_9)]6&]5%]3#[1"Z0 Y0 X. W.W-W-W-W,W,W,W,W,W+W+U+U+U+U+U+U+U+U+U*U*U*T*T)T)T)T)T)T)T)T)T+T+V+Z)Z(Z(Z([)\&% [���&���)���"������������ ��� ������������������������������������ ��� ���������������!���&���,���2���7���4WQ^'Y&Y&Y&Z'^)Z$9 h���W���\���\���[���X���T���Q���M���J���E���A���=���9���5���0���,���'���$��� ������������������ ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%)R#d2#a0 a0 a0 a0 b9)b?.`;*_8(^6%\4$\2#[1!Z0 Z/ X.W-W-W-W-W-W,W,W,W,W,W+V+U+U+U+U+U+U+U+U+U*U*U*T*T)T)T)T)T*U)U*T+T+X*Z)Z(Z(Z(`*D��+���,���$��������������� ������������������������������������ ��� ���������������!���&���,���2���7���2]T ](Y&Y&Y&Y&Y&Y&Y'_(Q,��c���Z���]���]���Z���W���T���P���M���I���E���@���<���8���3���.���*���'���#��������������������� ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���!1 cf1"a0!a0!a0 a0 a3$b?/a=.`9*^7)]5'\3%\1#Z0"Z0!Z/!Y. X- W- W- W- W-W,W,W,W,W,W,V+U+U+U+U+U+U+U+U+U+U*U*U*T*T)T)T)T*U*U+T+V,[)Z)Z(Z([)Z%H���'���'���!������������ ��� ��������������������������������� ��� ���������������!���'���-���3���8���2]V!](Y&Y&Y&Y&Y&Y&Y&Y&Y&](\'N ��]���Z���_���\���Z���V���S���O���L���G���C���?���;���7���2���.���)���%���!��������������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���&6Y(d2"a0!a0!a0 a0 b;+b>/a;,^8*]6(]4&\2%Z0#Z0"Z/!Y/ Y- X- W- W- W- W-W-W,W,W,W,W,W+V+U+U+U+U+U+U+U+U+U*U*U*T*T)T)T)U*U*U+V+Z*\)Z)Z)Z(_)3 ���(���)���#��������������� ��������������������������������� ��� ���������������"���'���-���3���8���6 fU"](Y&Y&Y&Y&Y&Y&Y&Y&Y&Y&Y&Y'\)\&@x���\���_���^���\���Y���U���R���N���J���F���B���>���:���5���1���-���)���$��� ��������������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������&���!8j2"a0!a0!a0!a/ b6&b@0a=.`:*^8(]6&\4%[2#Z1#Z0"Y0!Y/ Y/ Y. W. W. W. W. W.W-W-W-W-W-W-V,U,U,U,U+U+U+U+U+U+U+U+U+T*T*T+U+U+U,X+\)\)Z)Z)\*V!<���'���&��� ������������ ��������������������������������� ��� ���������������"���'���-���3���7���3% tY#\(Y&Y&Y&Y&Y&Y+X,Y(Y&Y&Y&Y&Y&Y&Z(^)V#6 �l���[���]���^���[���X���U���Q���M���I���E���A���<���8���4���0���+���'���#��� ������������������ ��� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���  I`-c1"a0!a0!a/ a1"b>-a?/`;,^8)]6'\5%[3#Z1#Z0"Z0"Y0!Y/ Y/ Y. Y. W. W. W. W. W-W-W-W-W-W-W-V,U,U,U+U+U+U+U+U+U+U+U+T+T+U+U,U,W,[*\)\)\)[*_)+ l���&���)���#������������ ��� ������������������������������ ��� ���������������"���'���-���3���9���4$ rY$['Y&Y&Y&Y&Y&Y+X0!V/ U-W)X'Y&Y&Y&Y&Y'Y'\)^(R ) �d���Z���_���]���Z���V���S���P���L���H���D���@���;���7���3���/���+���&���#��������������������� ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%��(Ig3"a0!a0!a0!a/ a8)bB1`<-_9*]8)]5'\3%Z2#Z0#Z0"Z0"Y/ Y/ Y/ Y. Y. X. W. W. W. W.W-W-W-W-W-W-V,U,U,U,U+U+U+U+U+U+U+U+T+T+U+U,V,Y,\*\)\)\)_+L��-���+���%��������������� ������������������������������ ��� ���������������"���)���.���4���9���4% tY%[(Y&Y&Y&Y&Y&X+W0 U. U,T+T+U*W'X&Y&Y&Y&Y'Y'Z(\(]'K ��^���^���_���\���Z���V���S���O���K���G���C���?���:���6���3���.���)���&���"��������������������� ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���"0 ^f0!a0!a0!a0!a/!a3$bA0a?/`<,^9)]7'\6&[4%Z2#Z1#Z1#Y0"Y0!Y0!Y0!Y0 Y/ X/ W/ W/ W/ W. W. W. W. W. W.W.W-V-U-U,U,U,U,U,U,U+U+U+U,U,U,U. W. [+\*\*\)]*^(& X���(���(���!������������ ��� ��������������������������� ��� ���������������#���(���.���5���6��80 X%[(Y'Y'Y&Y&Y&X,W0!U. T,T+T+T*S*R)T(W'Y&Y'Y'Y'Y'Y'Y'])Z%?s���Z���\���^���\���X���U���R���N���J���F���A���=���:���5���0���,���(���$��� ��������������������� ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���&-V&e3"a0!a0!a0!a/ b;+bB1a>.^;*]9)]6'\5&Z4$Z2#Z1#Y0"Y0!Y0!Y0!Y0!Y0 Y0 Y/ X/ W/ W/ W/ W. W. W. W. W. W.W-W-U-U-U,U,U,U,U,U,U+U+U-U,U-V/ Z,\*\*\*\*a+A��)���*���$��������������� ��������������������������� ��� ���������������#���)���.���4���7��77 ^'[(Y'Y'Y'Y'Y'X-W0!U. T,T+T+T*R)R(R(R(R(T(W'Y'Y'Y'Y'Y'Y'[(_)U#3 k���[���^���^���[���W���T���P���L���I���E���@���<���9���4���/���+���'���#��������������������� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���"<uh2"a1!a0!a0!a/ b6&bB2a@0`<.^:*]8)]7&[5%Z3$Z2#Y1"Y0"Y0!Y0!Y0!Y0!Y0 Y0 Y/ X/ W/ W/ W/ W. W. W. W. W. W.W-W-V-U-U-U,U,U,U,U,U,U,U-U-U.!Y.\*\*\*\*]+[$D���%���'��� ������������ ��� ������������������������ ������������������#���*���/���5���7��95 \'Z(Y'Y'Y'Y'Y'X-V0!U. T,T+T+S)R(R(R(R(Q(Q(Q(S'U'X'Y'Y'Y'Y'Y'Y'\(^(P) �`���\���`���]���[���W���S���P���L���G���C���@���<���7���3���.���*���&���"��������������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���"=^)d2"b0!a0!a0!a1"b=/aB3`>/_:,]8*\7(\4&Z3%Z2$Z2$Y1#Y0"Y0"Y0"Y0"Y0"Y0!Y0!Y0!Y/!W/!W/!W/!W/ W. W. W. W. W. W. W- W- U- U-U-U,U,U,U,U-V- U. U. W/!\,]+\*\*\*b,5 ���&���)���#��������������� ������������������������ ������������������$���)���/���5���5A= Z&Z(Y'Y'Y'Y'Y'X-W1"U. T,T+T*S*R)R(R(R(Q'P'P'Q'P'P'S(V'Y'Y'Y'Y'Y'Y'Y'\)^'C���[���[���^���\���Y���U���R���O���J���G���C���>���:���5���0���,���(���$��� ��������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������������ ���%��$Gh3#b0!b0!a0!a/ b8)bC4`?0_<-]9*]7(\6'[5&Z3%Z2$Z2$Y0"Y0"Y0"Y0"Y0"Y0"Y0!Y0!Y0!Y/!Y/!W/!W/!W/ W/ W. W. W. W. W. W- W- V- U- U-U,U,U,U-V-W- W.!V/!Z. ]+]+]+\*`,O  1���*���&��������������� ��� ������������������ ��� ������������������$���)���/���6���5@C_)Z(Y'Y'Y'Y'Y(X. W0"U/!U-U+T+S*R)R)R(Q'Q'P'P'P'P'P'P'P'Q(T(W'Y'Y'Y'Y'Y'Y'Z(_*Y%> s���Z���]���^���\���X���U���Q���N���J���F���A���=���9���3���/���*���%���!��������������� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���"" Od0 c2"b0!b0!a/!a2#dA0cB1a>.`<+^9*]7']6&\5&\3$[3#Z2#Z1"Z1"Z1"Z0"Z0"Z0"Z0"Z0!Z0!Y0!Y0!Y0!Y0!Y0!Y0 Y/ Y/ Y/ Y/ Y/ Y/ Y. X. W. W. W-W-W-X. Y. Y. Y0!Y0!\,]+]+]+]+])$ _���'���)���"��������������� ������������������ ��� ������������������%���*���0���7���7 BD_)Z(Y'Y'Y'Y'Y)Y/!X1"V/ U. U,U+U+T*T*T)R(R(R'R'R'R'R'R'R'R'R(S)T)V(X'Y'Y'Y'Y'Y'Y'[(`)W#/ h���[���_���^���[���W���T���Q���M���I���D���?���:���6���1���+���&���!��������������� ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���'���&R#h3$b1"b0!b0!a/ c;,dE5a?1`<._:,]7)]6(\5'\3&\3%Z2$Z1$Z1$Z1#Z1#Z0#Z0#Z0#Z0#Z0"Z0"Z0"Z0"Y0"Y0"Y0"Y0!Y/!Y/!Y/!Y/!Y/!Y/!Y/!X.!W.!W. W. W- W- X/!Y/!Y0!Y0#[.]+]+]+]+a-K��*���*���%��������������� ������������������ ��� ��������������� ���%���+���0���6���4DE^(Y'Y'Y'Y'Y'Y(Y/"Y1$W/!U. U,U+T+T*T)T)S(R'R'R'R'R'R'R(R(R(R(R(R(R)S)T)W(Y'Y'Y'Y'Y'Y'Z'\(_(N& ���]���\���^���]���Z���W���S���O���K���F���A���<���7���1���,���'���!��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���"/ de1"c2#b1"b1"b/ b5%cD4bB3a>0`;-^8+]7*]5'\4'\4&[2%Z2%Z1$Z1$Z1$Z1#Z0#Z0#Z0#Z0#Z0#Z0#Z0#Z0#Y0#Y0#Y0#Y0"Y0"Y/"Y/"Y/"Y/"Y/!Y/!Y.!W.!W.!W. W. X.!Y/!Y0"Y0$Z/"\,]+]+]+^,^(P���&���'���!������������ ��� ��������������� ��� ��������������� ���%���*���0���8���3 KL_)Y'Y'Y'Y'Y'Y)Y0#Y2$W0"U. U, U,T+T*T)T)R(R(R(R(R(R(R(R(R(R(R(R(R(R(R(R)R)S)U)W(X'Y'Y'Z'Z'Z'Z'^*](F~���]���[���_���\���Y���U���Q���L���G���B���=���7���1���+���%��������������� ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���$4Z'f4$b1"b1"b1"b0!d?0dE7b@0`=.^;,^8*]7(\6'\5&\4&Z3%Z2$Z2$Z2$Z2$Z2$Z2#Z1#Z1#Z1#Z1#Z1#Z1#Z0#Z0#Y0#Y0#Y0"Y0"Y0"Y0"Y0"Y0"Y0!Y0!Y/!Y/!X/!W/ W/!X/!Y0"Y0"Z1#\. ]+]+]+]+c-< ��&���)���#��������������� ��� ������������ ��� ��������������� ���&���,���1���7���5 LLa)[(Y'Y'Y'Y'Y*Y1#X3$W0"W/!U- U,U,T+T*T*R)R)R(R(R(R(R(R(R(R)R)R)R)R)R)R)R)R)R*S+S+U*W(Z'Z'Z'Z'Z'Z'[)_*[%: r���Z���`���^���Z���V���R���L���G���B���;���6���0���)���#��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���%��� ;|h4#b1"b1"b1"b0!c7(eE6cB2a>/`<-_9*^7)]7']5&\4&[4&Z2$Z2$Z2$Z2$Z2$Z2$Z2#Z2#Z1#Z1#Z1#Z1#Z1#Z1#Z0#Y0#Y0#Y0#Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y/!Y/!Y/!W0!W0!Y0"Y0#Y1$[0"]+]+]+]+a-W$>���)���'��� ������������ ��� ��� ��� ��� ��� ��� ���������������!���&���,���2���7���2 ON`)Z'Z'Z'Z'Z'Y)Y1#X1$W0"W/!V. U,T+T+T+T*S)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R*R+T+V)Y(Z'Z'Z(Z(Z(Z([)`+U"2 d���Z���\���[���V���Q���L���G���@���:���3���,���&��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���"Aa,c2#b1"b1"b0"b2#dA1dD5cA0`=-`;+_9*^7(]6&]5&]5&Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2#Z2#Z2#Z1#Z1#Z1#Z1#Z1#Z1#Z0#Y0#Y0#Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0!Y/!Y/!X0"Y0"Y0"Y1#Z2#]-]+]+]+^,a+1 p���&���*���#��������������� ��� ��� ��� ��� ��� ���������������!���&���,���2���8���3[R"`*Z'Z'Z'Z'Z'Z+Z1$Y2%X1#W/!V. U, T,T+T+T+S*R*R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)R)S*T+U+X)Z(Z(Z(Z(Z(Z(Z)\+`)N% ^���U���Z���V���P���J���C���=���6���.���(���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������������ ���&���$Ih4$b1"b1"b1"b0!d<,eI7eD3cA0a>.`<+`:*^8)]7']6&]5&\4&\4%\4%\4%\4%\4%\4%\4%\3$\3$\3$\3$\3$\3$\2#\2#\2#[2#Z2#Z2#Z2#Z2#Z1#Z1#Z1#Z1#Z0"Z0"Z0"Z2#Z2#Z2#[5&]0"^+]+]+]+`-O ��.���*���&������������������ ��� ��� ��� ��� ���������������!���&���,���3���8���5\U"`*Z(Z(Z(Z'Z'[,[5%Z4%Z2#Y1"W/ W. V-U,U,U,T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+U+U+U,U-W,Y*Z(Z(Z(Z)Z)[)])`+a)Cy���S���T���R���L���E���?���7���0���)���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#$ Wb0!d2#b1"b1"b0"c4%eF5eF6eB1b?/a<,`:*`8)^7'^7&]6&]5&]4%\4%\4%\4%\4%\4%\4%\4%\4%\3$\3$\3$\3$\3$\3$\3$\2#\2#[2#Z2#Z2#Z2#Z2#Z1#Z1#Z1#Z1#Z1#Z1#Z2#Z3#Z4%\3$^-^,^,]+]+`*# [���&���(���"��������������� ��� ��� ��� ������������������"���'���-���2���7���3^T#^*Z(Z(Z(Z(Z([-\5%[4%Y2#Y0"X/!W.V-U-U,U,T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+U+U+U+U+U+U+U+U,V-V-X+Z)Z(Z)Z)\)\)\)\*`,^(6_���K���J���G���@���9���1���*���"������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���% �)T$i4$b1"b1"b1"b0!e?.fH7eD3cA0b>.a;+`9*^8)^7'^7&]6&]5%]5%\4%\4%\4%\4%\4%\4%\4%\4%\4%\3$\3$\3$\3$\3$\3$\2#\2#[2#Z2#Z2#Z2#Z2#Z1#Z1#Z1#Z1#Z2#[2#[2#Z3$\4%]0!^,^,^,^,b.E��+���)���%������������������ ��� ��� ������������������"���'���-���3���8���5* nV$]+Z)Z(Z(Z(Z([. \5&Z5&Z2$Y0"W0 W/ V.U-U,U+U+U+U+U+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+T+U+U+U+U+U+U+U+U+U+U+U+U+U,V-W-Y,Y*\)\)\)\)\*\*]+b+V#/ �M���<���>���9���1���)���"������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���!4 qh2#b2#b1"b1"b0!c7'fG6eF5dB1b@/a=-a;+_9)^7(^7'^7&]5&]5&]5%]5%\4%\4%\4%\4%\4%\4%\4%\4%\4$\3$\3$\3$\3$\3$\3#\2#\2#\2#Z2#Z2#Z2#Z2#Z1#Z1#Z2#Z2#\3#\4$[6&\3$^-^,^,^,`.\'H���&���(���!������������������ ���������������������"���'���.���3���8���7- r]'_+[)Z)Z)Z)Z(\1!\7'Z4%Z3$Y2#W0!W/ W. V-V-U-U+U+U+U+U+U+U+U+U+U+U+U+T+T+T+U+U+U+U+U+U+U+U+U+U+U+U+U+U+U+U+U+U,U,U,U.V.W-[+\*\)\*\*\*\*\*`-b,Q% n7���2���0���)���!������������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���":_*d3#b1"b1"b0!b1"fB1fJ9dD4cA1a>/`<-`:+^8*^7)^7(]7(]6&]6&]6&]5&]5&\5&\5&\5&\5&\5&\5&\5&\5&\4%\4%\4%\4%\4%\4%\4$\3$\3$[3$Z3$Z3$Z2$Z2$Z2$Z3$[4%\4$\5&\6']1!^-^-^,^,e.=���'���*���$���������������������������������������#���(���-���4���7���5- s\'^*\)\)[)[)Z)\1!]8(Z6&Y3%Y2$Y0#W0!W0!W. W.V-U-U,U,U,U,U,U,U,U,U,U+U+U+U+U+U+U+U+U+U+U,U,U,U,U,U,U,U,U,U,U,U,U,U,U-U-U-U-U/ W/!X. Z+\*\*\*\*]+]+^+`-b+DK���*���'��������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���&���$Dg4$b1"b1"b1"b0!e;+fI9eF7dC3b?0a=.`;,_:+^8*^7)]7(]7(]6']6&]6&]5&]5&]5&\5&\5&\5&\5&\5&\5&\5&\5&\4&\4&\4&\4&\4&\4%\4%\3%\3%[3%Z3%Z3$Z2$Z2$[4&\4&\5&\6']3$^-^-^-^-a.T$ 8���*���&���!������������������������������������#���)���.���4���6��81 {]'^,\*\)\)\)\)\1"\8)\6'Y3%Y3$Y1#Y0!W/!W/ W. V- U- U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U-U-U-U-U- V- W- W/ W/!X0!Z. \,\*]+]+]+]+]+^,f.* W���$���%��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� ������������$���%% Kd0d3#b1"b1"b1"c3$fH6fJ9dE4bB1b?/`=-`;+^:*^8)^8)]7(]7']7']7']7&]7&]6&]6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\5&\5&\5&\5&\5&\5%\4%\4%\4%Z4%Z4$Z4$Z4%\5&\5&\7&]7(^0 ^-^-^-^-c-. e���&���)���#������������������������������������#���(���.���4���7�;; a*^+\*\*\*\)\*]3$\8)\7([5&Z3$Y2#X1#W0!W0!W/ V/ U. U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U.U.U.U.V. V. W. W. W/ Y0!Y0"Y2#Z3#[/ ]+]+]+]+]+b-B��,���'���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���'��&M!h4$b2"b2"b1"b0!e?/fK:eF6cC2a@/a=.`;,_:*^9*^8)]7(]7']7']7']7']7']7&]7&]6&]6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\5&\5&\5&\5&\5&\5%\4%\4%\4%Z4%Z5%[5%\6&\7&]8)^3#_-^-^-^-c/ I�0���)���'��� ���������������������������������$���)���/���5���6��8; a*]+\*\*\*\)]*]4$]:*\7'[5&Z3$Y2#X0"W0!W0 W/ V/ U/ U.U.U.U.U.U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U.U.U.U.U.U.U. U. V. W. W/ W/ W/ W0!Y0!Y0#Y2#Z4%[3$],]+]+]+_-]&H���%���'��� ������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������%���!+ ]f1!c3#b2"b2"b1!c6&gJ8fJ9eE4cC2b@/a=-`;+_:*`9*_8)^7(^7(^7(^7(^7(^7(^7'^7'^7']7']7']7']7']7']7']7']7']7']6&]6&]6&]6&]6&]6&]5&]5&]5&]5&[5&[6&]7&]7']8)^6'`/ `._.^-_.`+ W���&���*���#���������������������������������$���)���.���5���5��:: b+^,]+\*\*\)\+^4%^;+]8)]6&[4%Z4$Y2#X1"Y1"Y0"W0 V0 W/ W/ W/ W/ W/ W/ W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W/ W/ W/ W/ W/ W/ V/ W/ W/ Y0 Y0 Y0!X0"Z1#Z1#[3%[4%\.]+]+]+]+c-9���&���)���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���#4](d4%b2"b2"b2"b1!fB1hM;fG7eD3cA1b?/a=-`;+`:*`9*_8)^7(^7(^7(^7(^7(^7(^7(^7'^7']7']7']7']7']7']7']7']7']7']6&]6&]6&]6&]6&]6&]6&]5&]5&]5&\6&\6&]7&]7(^8)_3#`-`.`._.f/;���'���,���'���!��������������������������� ���$���*���0���6���6ABd,^,]+]+]+\*\,^6&_;,]8)]7'[5&Z4%Z3$Y1#Y1#Y0"X0"W0!W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W. W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ X/ X/ Y0 Y0 Y0!Y0"Y0"Z1#Z3#[4%\2#]+]+]+]+a-T" 2���*���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#5uh4$b2#b2#b2"b1!d9(hK9fI8eE4dC1b@/a>-a<,`;+`9*_8)^7(^7(^7(^7(^7(^7(^7(^7(^7(^7'^7'^7']7']7']7']7']7']7']7']7']7&]6&]6&]6&]6&]6&]6&]5&]5&]6&]7']7(^8)^6'`.`.`.`.c0Z(@���+���)���$��������������������������� ���%���*���0���6���4@Cc-]+]+]+]+]*]-^7'^;,]8)\7'[6&Z4$Z3#Y2#Z0#Y0"Y0!Y0 X/ X0 X0 W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ X0 Y0 Y0 Y0!Y0!Y0!Z0"Z1#Z1#Z3$\4%]-]+]+]+^+a+%d���'���)���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���$<a-d3$b2#b2#b2"b2"gG4hM:eG6dD2cA0a?/a=-`<,`:*`:*^8)^8(^8(^7(^7(^7(^7(^7(^7(^7(^7(^7'^7'^7']7']7']7']7']7']7']7']7']7']6&]6&]6&]6&]6&]6&]7&]7']7']8)^9*`2"`.`.`.`.e/6 w���(���,���&���!������������������������!���%���*���0���6���4�>Bd-]+]+]+]+]*]-^8'^<+]9*]7(\6&[5%Z3$Z2#Z1#Z1#Y0"Y0"Y0!Y0!Y0!Y0!Y0 Y0 Y0 Y0 W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ X0 Y0 Y0 Y0!Y0!Y0!Y0!Y0!Y0!Z0#Z1#Z2#[4%]0!]+]+]+]+`.N�.���)���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���'���$Gh4%b2#b2#b2#b0!e>-iN<gK9eG6cD2bA1a?/a>.`<,`;+^:*^9*^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^7(^7(]7(]7(]7(]7(]7(]7(]7(]7(]7']7']7']7']7']7']7'^7']7(]8)]:*_6&`/ `.`.`.b0!S"6���*���)���$��� ���������������������#���&���,���1���6���5MLc-],]+]+]+]*^._9)^=-^;*]8)\7'\6&[5&Z3$Z2$Z2#Y2#Y1#Y0"Y0"Y0"Y0"Y0"Y0!Y0!Y0!Y0!Y0!Y0!Y0!Y0!Y0!X0!X0!X0!X0!X0!X0!W0!W0!W0!W0!W0!W0!W0!X0!X0!X0!X0!X0!X0!Y0!Y0!Y0!Y0!Y0!Y0!Y0"Y0"Y0"Y0"Y0"Y1#Z2#Z3$[5&\3$],]+]+]+^,_) U���$���'���!������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���!# Qe/ d4$b2#b2#b1"c5%gI8fN;eH7dE4bB2a@/`>-`<-`;,_:+^9*^9*^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^8)^7)^7(^7(]7(]7(]7(]7(]7(]7(]7(]7']7']7']7']7']7(^7)^8)]9*^9)`1"`. `/ `/ `/ f.) f���'���,���&���"���������������������#���'���,���1���7���4KP d.]+]+]+]+]*^/!`;,_>.^;+]8)\7'\6&[5&Z4%Z2#Z3#Y2#Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0!Y0!Y0!Y0!Y0!Y0!X0!X0!X0!X0!X0!X0!X0!Y0!Y0!Y0!Y0!Y0!Y0!Y0!Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y0"Y1#Y2#Z2#Z5$\6&]/ ]*]+]+]+c-?��'���*���#��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���# )R#f5%b2#b2#b2#b0!fB1iP=gK8fG5dE3cB0b@/a>-a=,a<+_;*`:*`:*`:*`9)`9)`9)`9)`9)`9)`9)`9)`9)`9)`9)`9)_8(^8(^8(^8(^8(^8(^8(^8(^8(^8(^8'^8'^7'^8(_9)`:*_:*`;*_5%`/ `/ `/ `/ g1!C��.���.���)���%���!��������������� ���#���(���,���1���7���4MQ!d. ^,^,^,]+]*^1!a<,a>-_<*^:)]8(]7&\6&[5%\4%[4$Z3#Z2#Z2#Z2#Z2#Z2"Z2"Z2"Z2"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Y1!Y1!Y1!Y1!Y1!Y1!Y1!Y1!Y1!Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z2"Z1#Z3#\3#[5%\7&]4$]+]+]+]+`-Y&?���*���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���!( gi3#b2#b2#b2#b1"d9)iN<hM;gI8fF4dC1bA/b?.a=-a<,a<+`:*`:*`:*`:*`:*`9)`9)`9)`9)`9)`9)`9)`9)`9)`9)`9)`9)^8(^8(^8(^8(^8(^8(^8(^8(^8)^8(^8(^7'_8)`9)`9*`<+a:)`0!`/ `/ `/ b1!^* J���+���-���(���$���!��� ������ ���!���%���)���.���2���8���6ZU"d/^,^,^,^,^+_1"a>.a?/`<+^:*]8(]7']7&\5%\4%[4$Z3#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2"Z2"Z2"Z2"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z1"Z2"Z2"Z2"[3#\4#\4%\6&]6&].]+]+]+]+a,2 t���&���*���#������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���$4^*e4%b2#b2#b2#b1"hH7jO?gJ:eG8eD5cA2b?0b?/a=.a<-`;,`:+`:+`:+`:+`:+`:+`9+`9*`9*`9*`9*`9*`9*`9*`9*`9*`9*_8*^8*^8*^8*^8*^8*^8*^8*^8*^8*^8*^8*`9*`9+`;,a=.a4%a/ `/ `/ `/ f0!>���,���0���*���&���$���!���!���!���#���&���*���.���3���8���4"[[&b/ ^-^,^,^,^+`4$a>/a@0`<-^:*]8*]7)]7(\6'\5&\5&Z4%Z3$Z3$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2#Z2#Z2#Z2#Z2#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z1#Z2#Z2#Z2#Z2#Z2$Z2$[4&\4&\5&]7']2#]+]+]+]+_-U"3���(���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���"=g3$b2#b2#b2#b0"e;+jO?hL;fI8eE6dB3c@1b?/a=.a<-`;,`;,`:+`:+`:+`:+`:+`:+`:+`9*`9*`9*`9*`9*`9*`9*`9*`9*`9*`9*_9*^8*^8*^8*^8*^8*^8*^8*^8*^8*^9*`9*`:+a=.a9)a0 a0 a0 a0 b1!Z'?���-���-���)���&���$���#���#���%���'���+���/���4���8���3" ][&a/^-^-^-^,^,a5$b@0a@0`=-^;+^9*^8)]7(\6'\5&\4&\4&[3%[3%[3%Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2$Z2$Z2$Z2$Z3%\4&\5&\7(]6']-]*]+]+],b,&b���%���)���#������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������$���#" Fc-d4%b2#b2#b1"c3$iK9iQ>gK9eH7eE4cB2b@0a?/a>.a=-`<,`;+`;+`;+`;+`;+`;+`;+`;+`;+`:*`:*`:*`:*`:*`:*`:*`:*`:*`:*`:*_9*^9*^9*^9*^9*^9*^9*^9*^:*_:*`<+`<-a=.a3#a/a0 a0 a0 g1 1 r���*���1���,���)���&���%���%���&���(���,���0���5���8���8& dZ&b0 ^-^-^-^-^,a5%bB1a@0a>.`<,^:*^8*]7(]7']6&]6&\5&\4%\4%\4%\4%\4%\4%\4%Z4$Z4$Z4$Z4$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3#Z3$Z3$Z3$Z3$Z3$Z3$Z3$Z4%[4%\6&\6&]8)]2#]+]+]+]+c.I��+���)���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���%��(Fh5%b2#b2#b2#b1"f?/jQ@gM<gI9eG7dD4cA2a?0a>/a>/`<-`;,`;,`;,`;,`;,`;,`;,`;,`;,`;,`:+`:+`:+`:+`:+`:+`:+`:+`:+`:+`:+`:+_9*^9*^9*^9*^9*^9*^9*_:+`<-`<-`=.a7(a0 a0 a0 a0 e2"N" 7���0���/���,���)���'���'���(���)���-���1���5���9���60 tc*b0 _.^-^-^-^-a6'cA2bA2a>/`<-_;,^9*]7)]7(]6']6'\6&\5&\4&\4&\4&\4&\4&\4&\4&\4&\4&\4&\4&[4%[4%[4%[4%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z4%Z4&\5&\5&\8)]5'],]+]+]+`-^(! L���'���(���!������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���  Yh2"b2#b2#b2#b1"c7&iN=hO?fJ:eG7dE5cB2b@0a>/a=.a=.`;,`;,`;,`;,`;,`;,`;,`;,`;,`;,`;,`;,`;,`:+`:+`:+`:+`:+`:+`:+`:+`:+`:+`:+_:+^9*^9*^9*^:+^:+`;,`<-`>/a<-a2"a0 a0 a0 c1!d.)^���/���3���.���,���*���)���*���+���.���2���6���9���5/ sd,a0 `.`.`._-^,a8(cD4aA1a>.`<-_;,^9+]7)]7)]7(\6'\6'\5&\5&\5&\5&\5&\5&\5&\4&\4&\4&\4&\4&\4&\4&\4&\4&\4&[4&[4&[4&[4&Z4%Z4%Z4%Z4%Z4%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z4%Z4%Z4%Z5&[6&\6'\7(]7)]0"]*]+^,^,c.A���)���+���#��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���$ 0V'e4%b2#b2#b2#b1"gE5jR@hN;fJ8eF6dD3bB1b@0a?/a>.`=-`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+_:*^:*^:*^;+`<,`<,`>.a@0a6'a/a0 a0 a0 g2!G���2���6���2���.���,���+���,���-���0���3���7���8���80 wc,b0 `.`.`.`-`-a9)cE4aB2a@/`>.^<,^;+]9*]8)]7(]7(\7'\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&[5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%Z5%\5&\5&\7&\7&\7']9*]4%]+^+^,^,`.X% :���(���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#;ph3$b2#b2#b2#b1"d9)jR@hO=fJ9eG7dE4cB2aA0a?/a>.`=.`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+_:*^:*^:*_;+`<,`=-a?.a<,a0!a0 a0 a0 b2!`+M���2���4���1���.���.���.���/���1���4���9���9���:7e.b0!`.`.`.`-`.b:*cE4bC1a@/`>.`<,^:*]9*]9*]8)\7(\7'\7&\7&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&\5&[5%[5%[5%\5&\5&\5&\5&\5&\5&\5&\5&\6&\7&\7'\8)]9)^/ ^+^,^,^,d-0 l���%���)���"������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���$;]+e4$b2#b2#b1"c2#iK:jS@hN;fK9fH6eE4cC2bA0b@/b?/a>.a=-a=-a=-a=-a=-a=-a=-a=-a=-a=-a=-a=-a=,a=,a=,a=,a=,a<,a<,a<,a<,a<,a<,a<,a<,a<,`<+_<+_<+`=-a>-a@.bA0a5%a/ a0!a0!a0 h1!/ ���1���7���4���2���0���0���1���3���5���9���:���97f0 a0!`/ `/ `/`-`/ c=-dG5bD2bB0a@.a>-a=,_;*_:*_:*]9)]8)]8(]8(]8']8']8']7']7']7']7']7']7']7']7']7']7']7']7']7&]7&]7&]7&]6&]6&]6&]6&]6&]6&]6&]6&]6&]6&]6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&\6&]6']7']8(]9)^<+^5%^+^,^,^,d/ L�1���(���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���&���#=i5$b2#b2#b2#a0!e=-kTAjR?hM9fI7eG5eE3dC1bA/bA/a?.a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>,a>,a>,a>,a=,a=,a=,a=,a=,a=,a=,a=,a=,`=+`=+`=,a?.a?.bA0a;*a0!a0!a0!a0!d2#W%A���4���7���4���3���3���3���4���7���:���9���77g0 a0!`/ `/ `/ `.`/ c=,eH6cE3bB0b@/a>-a=,a<+`;*`:*_:*^9)]8(]8(]8(]8(]8(]8(]8']8']8']7']7']7']7']7']7']7']7']7']7']7']7']7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7'^8(^9)_;+^:)^. ^+^,^,`-_*" Y���&���)���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���!% Sf0!c3$b2#b2#b1"c5&jO<kS@iN;fK8fI7eF4dD2cB0bA/b@/a?.a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>,a>,a>,a=,a=,a=,a=,a=,a=,a=,a=,a=,`=,`=,a>-a>.aA0b@.a3$a/ a0!a0!b1"f/ * m���3���;���7���5���5���5���7���9���<���:�A:g0 a0!`/ `/ `/ `.a0!d?.fI6eF3cC0bA0b@/a>-a=,`<*`;*_:*^9)^9(^8(^8(]8(]8(]8(]8(]8(]8(]8(]8']8']8']7']7']7']7']7']7']7']7']7']7']7']7']7']7']7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7']7'^8(_:*`<+_3$^+^,^,^,e/ A���%���+���$��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%�*S"f4%b2#b2#b2#b1!fB1kUCiP=hL:eI7eF4dD2dB0bA/b@/a?.a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>-a>,a>,a>,a>,a=,a=,a=,a=,a=,a=,a=,a>-a>-a>-a@/bA0b9)a/ a0!a0!a0!g2#M ��<���=���;���9���7���7���8���:���=���;��AEi2"a1!a0 a0 `/ `.a2"eA0fH6dF4dC1aA/a?.a>-`<+`<+`;*_:*^9)^9)^9)^9)^9)^9(^9(^9(^8(^8(^8(]8(]8(]8(]8(]8(]8(]8(]8(]8']8']8']8']8']7']7']7']7']7']7']7']7']7']7']7']7']7']7']7']7']7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&^8(^9)^:)`;*`8'_-^-^-^-`/[)D���*���'��� ������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���"2 `i3#c2#b2#b2#b0"d7(kSBjTAgO=fK:fH7dF5dD3cC2bA0bA0a@/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>.a>.a>.a>.a>.a>.a>.a>.a>.a>.a?/b@/bA/bA1b@/a1"a0!a0!a0!b1"b.Z���:���>���;���:���:���:���<���?���;�CFh2!b1 a0 a0 a0 a.a2#eC2fJ9dF5dD2aA0a@/a?/`=.`=-`<,_<,^:*^:*^:*^:*^:*^:*^:*^:*^9*^9*^9*^9*^9*^9*^9*]9*]9*]9*]9*]9*]9*]9*]9*]9*]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]9)^:*^:*_;+`<,^1"^,^-^-^-d/8 }���&���*���#��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���&/Z)h4$c2#b2#b2#b2"iJ8kVDiP?gM;fJ9eG7dE5cC2bB2bA0a@/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>.a>.a>.a>.a>.a>.a>.a>.a>.b@/b@/cA/dC1a7(a/!a0!a0!a0!g2"<���;���@���>���=���=���=���>���A���=LHh2!b1 a0 a0 a0 a/a3#dC2eI8eG6cD3aB1aA/a@/`?/`=-_=,_=,^;+^:*^:*^:*^:*^:*^:*^:*^:*^:*^:*^:*^9*^9*^9*^9*^9*^9*^9*^9*]9*]9*]9*]9*]9*]9*]9*]9*]9*]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8*]9*^:*^;+`>._7(^-^-^-^-a/ T" 8���'���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&��� 6wl4$d3$c2#b2#a0!f=,lUCkTAiO=gL9fI7fG5eE3cC1cB0bA/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/b?.b?.b?.b?.b?.b?.b?.b@/cA/cB0cC1d>,b0!a/!a0!a0!c2#])R���=���B���@���?���?���@���B���>PU"g3"a0 a0 a0 a0 a.b6&eF5fJ9eG6dE4cC1bB0aA/a?.a>-a>-_=,_<+_<+_<+_;+_;+_;*_;*_;*_;*_;*_;*_;*_;*_;*_;*_;*_:*_:*_:*_:*_:*_:*_:*_:*_:*_:*_:*_:*_:*^:*^:*^:*^:*^:*^:*^9*^9*^9*^9*^9)^9)^9)^9)^9)^9)^9)^9)^9)^9)^9)^9)^9)^9)^:*_:*_<+`>-a>,_0!^,^-^-_.c-, k���%���)���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���! Ea-f4$d2#c2#a1"b4%jO=lWEjP?hM<fJ9fH7eF5dD3dC2cB1bA0b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/b?/b?/b?/b?/b@/c@/dB1dD2eB2c5%b/ b0!a0!b1"g1!/ ���<���E���C���B���B���B���D���?TU#g3"a0 a0 a0 a0 a/c7'fI8fL9eH7dE5dC2cB1b@/a@/a>.a>.`=-`<,`<,`<,`<,`<,`<+`<+`<+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;*`;*`;*`;*`;*`:*`:*`:*`:*`:*`:*`:*`:*`:*`:*_:*^:*^:*^:*^:*^:*^:*^:*^:*^:*^:*^:*^:*^9*^9*^9*^9*^9*^9*^:*`;+`;+`=-a?.`6&^,^-^-^-e/F���*���+���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%��&Ij5%d2#d2#d2#b0!gA1mYGkSAiO=gL:fI8fG6eE4dC2dB1cB1b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/b?/b?/b@/b@/dA0dC1dE3d=,b0!b0!b0!b0!e2"P!��F���G���F���E���E���E���F���@XU$f2#a0!a0 a0 a0 a.c7'gI8gL:eH6dF5dD3dB1bA/b@/b>/a>.a=-a=-`<,`<,`<,`<,`<,`<,`<,`<+`<+`<+`<+`<+`<+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;+`;*`;*`;*`;*`;*`;*`;*`;*`;*`:*`:*`:*`:*`:*`:*_:*_:*_:*_:*_:*_:*^:*^:*^:*^:*^:*^:*^:*^:*^:*_<*`<+`<,a>.a<,_.^-^-_-a._+ M���'���(���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���"$ Mg1"e3#d2#d2#d1"d6'kSAkVCiQ>iN;fK8fH6eF4dE3dC1dC1bB0bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/b@/b@/bA/cB0dB0dD2eB1c3$b1"b1"b0!c1"d/& m���E���J���H���G���G���H���A! e[&f2"a0!a0!a0!a0 a/d8(gJ7gM:fH6eF4eE3dC1dB0bB/b@/b?/a?-a>,a>,a>,a>,a=,a=,a=,`=,`=,`=,`=,`=,`=,`=,`=+`=+`=+`=+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<*`<*`<*`<*`<*`<*`;*`;*`;*`;*`;*`;*`;*_;*_;*_;*_;*_;*_;*_;*_;*_<+`=,`>,`>-aA0`4%_-_._.`.f0 B���)���)���$��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���'��*T%j5%d2#d2#d2#d1"iG5lYEkS@iO=gL:fI8eG5eE3dE3dC1bB1bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/b@/bA/dB0dC1eD2d:)b0!b1"b1"b1"g2#G��I���L���K���J���K���J���C" h`)e3#a0!a0!a0!a0!a. d:*hM;gL9fI7eG5dE3dD2cB1bA/b@/b@/a?.a>.a>-a>-a>,a>,a>,a>,a=,a=,a=,`=,`=,`=,`=,`=,`=,`=,`=,`=+`=+`=+`=+`=+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<+`<*`<*`<*`<*`<*`<*`;*`;*`;*`;*`;*`;*`;*`;*`;*`;*`;*`<+`=-`>-aA0`;*`.`.`.`.b0 \'?���&���&��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%��� 6 mk3#e3$d2#d2#d0!f;+kVFkVEiQ@gN=gK:eI9eF6dD5dC4cB2bA2bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1bA1cB2dB2dE4c?/b1#b0"b1"b1"c3#b+b���H���O���N���M���M���D" i_)d2#a0!a0!a0!a0!a/ d:*hL<gL<fI8eG7eE5dC4cB3bB2b@0b@0a?/a>/a>/a>/a>/a>/a>/a>/a>.a>.a>.a=.a=.a=.a=.`=.`=.`=.`=.`=.`=.`=.`=.`=-`=-`=-`=-`=-`=-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<-`<,`<,`<,`<,`<,`<,`<,`;,`;,`;,`;,`<-`<-a>/a@0a@0`3$`-`.`.`/f0 6 {���$���)���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���#9W)h4%d2#d2#d1"d2#iL<lYHjSBhP>fM;fJ8eG6dG5dE4dD4cC3cB2cB2bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB2cC2dC3dD3c7(b0!b1"b1"b2#h2#;���K���Q���P���P���O���F& |_*d2"a0!a0!a0!a0!a/ d;*gL;fM;fI8eG7dF5dD4cB2bB1bA0a@/a@/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>.a>.a>.a>.a>.a>.a>.a>.`>.`>.`>.`>.`>.`>.`>.`>.`>.`>.`>.`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-a>.a>.a@/bA1a9)`.`.`.`.e0 O" 3���)���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���%���"@k5%d2#d2#d2#c/ g@/mZIjUDiQ?hN<fK9fI8eH7dF4dE4dE4dC2dC2dC2dC2dC2dC2dC2dC2bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1cC2dD2dF4c>-b0!b1"b1"b1"f4$W&Z���Q���T���S���R���H) e.e3#a0!a0!a0!a/ a/ d>-gM;fM;fI8eH7dF5dE3cC3bB1bA0bA0aA/a@/a@/a@/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>.a>.a>.a>.a>.a>.a>.a>.a>.a>.`>.`>.`>.`>.`>.`>.`>.`>.`>.`>.`>.`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`=-`>.a?.a@/bA0b@/a0!`-`.`.a/ d-- ]���&���)���!������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������$���# Fe0!e3$d2#d2#d1"e6&lSBlYHjSBiP?gM<fJ9eH8dG7dF5dE4dC4dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3cC2cC2cB2cB2cB2cB2cB2cB2cB2cB2cB2cB2cB2cB2cB2cB2cB2cC2cC2dE4dD2b4%b0"b1"b1"c2#f0!/ ���P���W���V���T���K) e.d2"b0!a0!a0!a0!a/!e>-hN<gM;eJ8eH8eF6dE5dD4cC3cB3bA1aA0a@/a@/a@/a@/a@/a@/a@/a@/a@/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>/a>/a>/a>/a>/a>/a>/`>/`>/`>/`>/`>/`>/`>/`>/`>/`>/`>/`>/`>.`>.`>.`=.`=.`=.`=.`=.`=.`=.`=.`=.`=.`=.`=.`>.a?/a@/bA0cD3a7'`-`.`.`.d0!J���+���*���$��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���'���%O i4%d2#d2#d2#d0!hE3n[JkVDjQ@iN=gL:fI8fH7eG5eF4dE4dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dD3dD3eF5e<,b0!b1"b1"b1"g3$O��X���X���Y���V��Q. d/ d2#b1"b0!b0!a/ a/ e?.iO<hN<gK9fI7fG6eG5dE5dD3dD2cB1bB1bA0bA0bA0bA0bA0bA/bA/bA/bA/bA/bA/bA/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/b?/b?/b?/b?/b?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>.a>.a>.a>.a>.a>.a>.a?/b@/bA0dD3c@/`0!`.`/ `/ a0!`, K���&���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#- ]g1"e3$d2#d2#d0"f9)mVDlYGjSBjP?iM;fK9fH7eG6eG4dE4dE4dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dD3eF5dB2b3#b1!b1"b1"c2#c. u���V���]���W��S7g1!e3#b1"b1"b1"b0!b1"fB1hN<gM;fJ9fI8eG6eF5dE4dD3dC2cC2bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA/bA/bA/bA/bA/bA/bA/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/b?/b?/b?/b?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>.a?/a?/bA0bC2dD3a6&`.`/ `/ `/ h1!1���%���)���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���%2Y&h5%d2#d2#d2#d1"jJ:n[JlTCjQ@iO=gK9fI8fH7eG5eF4dE4dD3dD3eD3eD3eD3eD3eD3eD3eD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dD3dC2dC2dC2dC2dC2dC2dC2dC2dD3eE4c:*b1!b2"b1"b1"i3#@��X���^���]��V8g1"d3#b1"b1"b1"b0!b2#eD2iN<gL:fJ8fI8fH7eF5dE4dD3dC2cC2bB1bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA/bA/bA/bA/bA/bA/bA/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/b?/b?/b?/b?/b?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?/a@/bA/cB1dD3c=,`/ `/ `/ `/ c1"W&9���'���&��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���!:ui3$d2#d2#d2#c0!g>.n[IlXFkSAiQ>hN<gL:fJ8fH7fG6fG6eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4dE4dE4dE4dE4dE4dD3dD3dD3dD3dD3dD3dD3dF5d@/b2#b2#b2"b2"f4$Z(m���]���]��[;g2"c2#b1"b1"b1"b0!b3#fD3hM<fL:fJ9eI7eH7eH6dF5dD3dD3dC2cB1cA0bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB0bB0bB0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0b@0b@0b@0b@0b@/b@/b@/b@/b@/a@/a@/a@/a@/a@/a@/a@/aA0bB1bB1cE3dC2a2$`.`/ `/ a0!f/ 4 m���&���*���"������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���"?b,e3$d2#d2#d1"d3#kQ?m\KkUCjR?iP=gM;fK9fI8fH7fG6eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4dE4dE4dE4dE4dE4dE4dE4dD3dD3dE4dD3c8'b1"b2#b2#c3#h3"5 ���\���_ �aDh3"c2#b1"b1"b1"b0!c4$gE4iO<hM:gK9fJ8eH7eH6eG6eE4eE4dD3dC2dB1dB1dB1dB1dB1dB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bB1bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0b@/b@/b@/b@/b@/b@/b@/a@/bB1bB1cD3dF5b;*a/a0 a0 a0 d2"Q"��0���+���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���&���$Lj4%d2#d2#d2#d0!hA0o]KlWFjSAjQ?hN<fL:fJ9fH7fG6fG5eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4dE4dE4dE4dF4d>-b2#b2#b2#b2#e4%R$��e���` ��dGh4#c3#b1"b1"b1"b0!c5$gG5iO=hM:gK9fJ8fI8fH6eG6eF5eE4dD3dD3dC2dC2dC2dC1dC1dC1dC1dC1dB1dB1dB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cB0cB0cB0cB0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA0cA/cA/cB0cC1cC2dE4cD3a2"a/a0 a0 a0 e/" T���%���(���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���$ Ic/!f3$d2#d2#d1"e7'mUDn\KlVEkR@jP>hN<gK:fJ8fH7fG7fG6fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5eF5eF5eD3d5%c1"b2#b2#b3#e1!# ���] �fGh5#c3"b2"b2"b2"b/!c4%hG5iN=iL;hK8gJ9gI8fH8fG6fF6fF5eE4eD4eD3eD3eD3eD3eD3eD3eD3eD3eD3eD3eD3eD3eD3eD3dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dC2dC2dD3dE5eH6b9(a/a0 a0 a0 g2">��*���)���$������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���'��'R"j5%d2#d2#d2#c/ kK:p_OmXHkTCjQ@iO>hM<hK:gI8gH8fG7fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fF6fG7e;+d1"d2#d2#c2#h5%E���^pP g4#c3$b2#b2#b2"b1!c6%gG5iM=hK;gJ:gJ9gI8fH8fG7fF6fF6eE5eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4eD4dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dC3dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dB2dC3eD4eF5fH7dA1a0 a/a0 a0 c2"^*D���&���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%��� 0 hj3#e3$d2#d2#c/!g<,o]Kn^LlXEjUBkR?jP=iN;gL9gK8fI7fI7fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fI7fD2d3$d1"d2#d2#g4%^+vmU#h5%b3#b2#b2#b2#b1"c7(hJ8iO=gM;gL9gL9gK8fJ8fI7fI7fH6eG5eG5eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4eF4dE4dE4dE4dE4dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dE3dD2dD2dD2dD2dD2dD2dD2dD2dD2dD2dD2dD2dE3eF4eF5fI8fI7c6&a/a0 a0 b1 g1!5 |���%���*���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���"6\(f4%d2#d2#d2"d2"lP?obPlZJlVEkSBjP?iN=hN;gK9gJ8fI8fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7e:*d1"d3#d2#e3$j3#4 �lU"j5%c3#c2#b2#b2#b0"d7'gJ8gO=gM;gM;fL9fK8fJ8fI8fI8eI8eH7eG6eF6eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE3dE3dE3dE3dE3dE3dD3dD3dD3dD3eF5eF5eH7fK9eA0a/ a0!a0!a0!d2"V% �1���*���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���!>{i5%d3#d3#d3#d1!g@0o`Nn\KlXGlTBjR@iO=hM;gK9gJ8fI8fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fH7fH7fH7fH7fH7fH7fI8fE4d4$d3#d3#d3#h5%O$V$i4%d2#d2#d2#d2#c1"d8(fI7gN<gL:fK9fK9fJ8fJ8fI8fI8eH7eH7eG6eG6eG6eG6eG6eG6eG6eG6eG6eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5eF5dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE4dE3dE3dE3dE3dE3dE4eF5eG6fJ8fI8b4%a/ a0!a0!a0!f1!, `���'���(���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������$���%@a.g4$d3#d3#d2"e5%mXFoaOl[IlWEkSAjQ?iO<hM:gK8gK8fJ8fI7fI7fI7fI7fI7fI7fI7fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI7fI7fJ8fB1d2#d3#d3#d3#b1!HY(h5$d2#d2#d2#d2#d1"d:*gK8fM:fL9fL9fK8fK8fJ8fJ8fJ8eI7eI7eH6eH6eH6eH6eH6eH6eH6eH6eH6eH6eH6eH6eH6eH6eH6eG6eG6eG6eG6eG6eG6eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5dF5dF5dF5dF5dF4dF4dF4dF4dF4dF4dF4dF4dF4dF4dF4dF4dF4dF4dF4dF4eG5eH6eI7fK9d<,a/ a0!a0!a0!f2#J �-���)���%��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���'��#K k6%d3#d3#d3#c0 iH7pbPm^LlYGkUDjRAiP?iN<gL;gK9fJ8fI8fI8fI8fI8fI8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fK9e@/d2"d3#d3#e4$_+_+f5$d3#d3#d3#d2#d1"d:*fK9fM;fK:fK:fJ8fJ9fJ8eI8eI8eI8eH8eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eG7eG7eG7eG7eG7eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6dF6dF6dF6dF6dF5dF5dF5dF5dF5dF5dF5dF5dF5dF5dF5dG6eI8eI8eL:eF4a2"a/ a0!a0!b1"d- Q���$���(���"������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���!' Yf1!e4$d3#d3#d1!f9(o\IpbOm[ImXElTBkR?jP=iN;hM:hK8gK8gJ8gJ8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gL9fE3d2#d3#d3#d3#c1"d3#d3#d3#d3#d3#d2"f:)hJ7hM:hL9gL9gL9gK9gK9gK8fJ8fJ8fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fH7fH7fH7fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6eH6eH6eH6eG6eG6eG6eG6eG5eG5eG5eG5eG5eG5eG6eH7fI7fL9gM9c;*a/ a0!a0!a0!g2#@���)���*���$������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���$ .[&g6%d3#d3#d2"d1!kN<qdQp^KnYFlVCkS@kQ>jO<iM:iL9hL8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gL9gH5e5%d3#d3#d3#d4$d3#d3#d3#d3#d3#f=,iJ7iM9iL9iL9iL9hL9hK8fK8fK8fJ8fJ8fJ8fJ8fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6fH6eH6eH6eH6eH6eH6eH6eH6eG5eG5fH6fI7fK8gM:fE3b/!b/!b0!a0!d2"\):���)���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���"1 ki4$d3#d3#d3#d1!h?.qaOqaOo\JmWElUBkR?jP=iN;iM:iL9gK8gK8gK8gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9e>-d1"d3#d3#d3#d3#d3#d2"d3#f@/hK9hL:hK9hK9hK9hK9gK8gK8gJ8gJ8gJ8fJ8fK9fK9fK9fK9fK9fK9fK9fJ8fJ8fJ8fJ8fJ8fJ8fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI7fI7fI7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fH7fI7eI7eI7eI7eI7eI7fI7fJ8gM;gM;c7'b/ b0!b0!b1"g1"8 q���'���*���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���$:a,f4$d3#d3#d3#d2"nXGreTo_MnYJlVEkTBjQ?jO=iM;iM;hL:gL9gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gM;gJ9e;+d3#d2"d2"d2"d2"e5&fB1gL:gM:gL9gL9gL9gL9gL9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9fK9fK9fK9fK9fK9fK9fK9fK9fK9fK9fK9fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8gJ8gK9hM:iO=fB2b0!b1"b1"b1"d3$X& 5���(���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���$Ej5%d3#d3#d3#c0 iE4reTqbPo\JmXFlUCjSAjP>iO=iM;hL:gM;gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gM;gM;gM;gM;gM;gM;gM;gM;gM;gM;gM;gL:gL:gL:gL:gL:gL:gL:gK9eA0e;*d7&e8(e=,fG5gM;gL:gL:gL:gL:gL9gL9gL9gL9gL9gL9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9fK9fK9fK9fK9fK9fK9fK9fK9fK9fK9fK9fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fJ8gK9gL:iP<hK8c5&b0!b1"b1"b2#f0!! Y���%���)���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���#$ Jd/ f5$d3#d3#d2"e5%o\KrfTp^MnZHlWEkSAjQ?jP>iO=jN<hM;hL:hL:hL:hL:hL:hL:hL:hL:hL:hL:hL:hL:hL:hL:hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;gJ9hL:hM;hM;hM;hL:hL:hL:hL:hL:hL:hL:hL:hL:hL:hL:hL9hL9hL9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9hK9gK9gK9gK9gK9fK9fK9fK9fK9fK9fK9fK9fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI8fI8fI8fI8fI8fI8fJ8hK9hK9hN<jP>e?.b0!b1"b1"b1"h4$J��)���*���$��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���$ *Q#i6%d3#d3#d3#d1!kL;shUqaPp\KnYGlUDlSAkQ?jP>jO>iN<iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:hL:hL:hL:hL:gL:gL:gL:gL:gL:gL:gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK8gK8gK8gK8gK8gK8gK8gJ8gJ8gK9hK9iL:jN<kQ?iI7b2"b1"b1"b1"e3#_,E���)���(��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���!' ]j3"d4#d3#d3#c1!g:)q_NreSp^LnZHmWDlTBkSAjQ?jP>jO=iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<jN<jN<jN<jN<jN<jN<jN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:iL:gL:gL:gL:gL:gL:gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK9gK8gK8gK8gK8gK8gK8hL9iL:iL:jO<jO=e;+b/!b1"b1"b1"g3#;���&���+���$������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���$ 2]*g5%d3#d3#d2"c1"nUCsiWqaOp^KnYHlWElUBkS@jQ>jP>jO=iN<iN<iN<iN<iN<iN<iN<iN<iN<iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;iM;hM;hM;gM;gM;gM;gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL9gL9gL9iL:iM;iN<jR@hG5b2!b1"b1"b1"e3$[(6���(���&��� ������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���";rh4$e3#d3#d3#c1!hA0sgXreUp_Oo\KmXHkVEkSBjRAjP?jO?iO>iN=iN=iN=iN=iN=iN=iO>iO>iO>iO>iO>iO>iO>iO>iO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<hM<hM<gM<gM<gM<gM<gM<gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;hM<iN=iO>jQAjO>d8'b1!b2"b2"b3#g3"+ i���&���)���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���$<`,g5%d3#d3#d2"d4#o\KshXqaPo\LmYIlWFkUDkSBjQ@jP?iO>iN=iN=iN=iN=iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jP?jP?jP?jP?jP?jP?jP?jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<hM<gM<gM<gM<gM<gM<gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gM<iM<iO>iP?jSBgC2b0!b2#b2#b2#h5%O! �,���)���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���%��%Ej5$d3#d3#d3#c0 kI8siWqdSp_Nn\JmYGlUDkSBjRAjQ@jO>iN=iN=iN=iO=iO=iO>iO>iO>iO>iO>iO>iO>jO>jO>jO>jO>jO>jO>jO>jO>jP>jP>jP>jP>jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP>jP>jP>jP>jO>jO>jO>jO>jO>jO>jO>jO>jO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO=iO=iO=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN<iN<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<iM<hM<gM<gM<gM<gM<gM;gM;gL;gL;gL;gL;gL;gL;gL;gL;gL;gL;gM;hM<iN=iP=jSAiM;c4%b1"b2#b2#d4$a. " O���'���)���!������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$��� Ri1"e4$d3#d3#c1!f8(qaOrhWpbOo^LmZHlWEkUCjSAjQ?jQ?iO=iO=iO=iP>iP>iP>iP>iP>iP>iP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>iP>iP>iP>iP>iP>iP>iP>iP>iP>iP>iP>iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iO=iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<hN<gN<gN<gN<gN<gN<gM;gM;gM;gM;gM;gM;gM;gM;gM;hN<iO=iP>jR@jTBf@/b0!b2#b2#b2#h4$E���(���+���$��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���%,T&g5%d3#d3#d3#c1!mQ@tk[qcRp_Nn\KlYHkVEkTCjRAjQ@jP?iO>iP?iP?iP?iP?iP?iP?iP?iP?jP?jP?jP?jP?jP?jP?jP?jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jQ@jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?iP?iP?iP?iP?iP?iP?iP?iP?iP?iP?iP?iP?iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iO>iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN=gN=gN=gN=gN=gM<gM<gM<gM<gM<gM<gM<gM<iO>iO>iP?jTCiK:b2#b2#b2#b2#d4$_, <���(���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#4 fh4#e4#d3#d3#d1!g=,tjVshVqbPp_Mo[JmYFlVDlTBkR@kR@jQ?jQ?jQ?jQ?jQ?jQ?jQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=iO=iO=iO=iO=iO=iO=iN<iN<iN<iN<iN<iN<jP>jQ?jSAkSAe:*b1"b2#b2#b2#g4$8x���&���)���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���%5^*g5$d3#d3#d2"d2"oYGtkYrdRp`No]KnYGlWElTBkR@kSAjR@jQ?jQ?jQ?jQ?jQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?kQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jP>jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=jO=iO=iO=iO=iO=iO=iO=iN<iN<iO=jO=jQ?jR@kVDhF5c1"c1#c2#c2#g5%Y&7���'���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���"=l5%d3#d3#d3#c0 iE3tmZsjWqcPp`Mo]JlYFlWDkVCkUBkTAjS@jS@jS@jS@jS@kS@kS@kS@kS@kS@kS@kTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkS@kS@kS@kS@kS@kS@kS@kS@kS@kS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>iQ>iQ>iQ>iQ>iQ>iQ>iP=iQ>jQ?jS?kVCjR?e7(d0"d2#d2#e3$h1"- b���&���(���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���  Fg/ e4$d3#d3#c1!e7'rbOtn[rfSqaNo^Km[HmXFlWDkVCkUBjTAjS@jS@jS@kS@kS@kS@kS@kS@kS@kTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkS@kS@kS@kS@kS@kS@kS@kS@kS@kS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>iQ>iQ>iQ>iQ>iQ>iR>jQ>jR?kTAkWDgC2d0!d2#d2#d2#j5$O ��+���+���$��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%�'Di5$d3#d3#d3#d1!lN<tq^rhUqdQp_Ln\IlYFlWCkVCkUBkTAjS@kS@kS@kS@kS@kS@kS@kTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBlUBkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkS@kS@kS@kS@kS@kS@kS@kS@kS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jS@jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jR?jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>jQ>iQ>iQ>iQ>jR?jR?jS@kVCjN<d4$d1#d2#d2#f3$b-  J���)���'���!������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���"( Th3#e4#d3#d3#d1!f9(tmZtmZrfTqbPq_Lo[JmYGmXFlVDlVDkUBlTBlTBlTBlTBlTBlTBlUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUCmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDlUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClTBlTBlTBlTBlTBlTBlTBlTBlTBlTBkTBkTBkTBkTBkTBkTBkTBkTBkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAkR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@jR?jR?jR@kSAkTAkUClXFg>.d0!d2#d2#d2#j4%F���'���)���#��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���& 1X(h6$d3#d3#d3#c3#oWEup^thVrdRq`Np]KnZImYGlXFlVDmUClTBlTBlTBlTBlTBlUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUCmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDmVDlUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClUClTBlTBlTBlTBlTBlTBlTBlTBlTBlTBkTBkTBkTBkTBkTBkTBkTBkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAkR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@jR?jR?jR@kSAkTBmYGjK:d2#d1"d2#d2#f4%[(;���'���&��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���!7rl4$d5%d4$d4$c1!g@/tlXtmZrfSqbPp`Mo]KmZHmXEmXEmVClUBlUBlUBlUClUClVClVClVClVClVClVClVClVClVClVClVClVClVClVCmVDmVDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmWDmVDmVDlVClVClVClVClVClVClVClVClVClVClVClVClVClVClVClVClVClVClVClUClUBlUBlUBlUBlUBlUBlUBlUBlUBkUBkUBkUBkUBkUBkUBkTBkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkS@kS@kS@kS@kS@kS@kS@kS@kS@kS@kS@kS@kS@kSAkS@kUBlWElVCf;+d0!d2#d2#e3$k4$2 r���$���)���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���" ?_-f6%d4$d4$c2"e6&q^Muq^sjWrfSpbOo_Mp\JnYGmXFmWElVDlUClUClVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWElVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlUClUClUClUClUClUClUClUCkUCkUCkUCkUCkUCkUCkTBkTBkTBkTBkTBkTBkTBkTBkTBkTBkTBkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAkTBlTBkTBlVDlZGhF5d0"d2#d2#d2#k5%M#��,���+���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���%��%Hj6&d4$d4$d4$c1!jJ9vtbtmZrhTqcQq`Np]LoZImXFmWEmWElVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlWDlWDmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmXEmXEmXEmXEmXEmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXEmXEmXEmXEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWEmWElWDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVDlVClUClUClUClUClUClUCkUCkUCkUCkUCkUCkUCkUCkUBkTBkTBkTBkTBkTBkTBkTBkTBkTBkTAkSAkSAkSAkSAkSAkSAkSAkSAkSAkSAlUBlUClVDlYFkS@e6&d1"d2#d2#f3$f0!' V���'���(���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������$���"" If2"e5%e4$e4$d2"f8(siVuq^sjWseRqbOp_Lo]JnZGmYFmYFlXElWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXElWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlVClVClVClVClVClVCkVCkVCkVCkVCkVCkVCkUBkUBkUBkUBkUBkUBkUBkUBkUBkUBkTAkTAkTAkTAkTAkTAkTAkTAkTAkUBlVClWDlXEm[HhB1d0!d2#d2#d2#i4%H���*���*���$������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���'��'Q#k6&e4$e4$e4$d1!nR@wubulYsgTrdQp`Mo]Jn\Jm[HmYFmXElWDlWDlWDlWDlWDlWDlWDlWDlWDlWDmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFnYFoYFoYFoYFoYFoYFoYFoYFoYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmYFmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXEmXElWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlWDlVClVClVClVClVClVCkVCkVCkVCkVCkVCkVCkUBkUBkUBkUBkUBkUBkUBkUBkUBkTAkTAkTAkTAkTAkTAkTAkTAkTAkUBlVClWDl[HjQ?e4$d1"d2#d2#e3$b-C���&���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���"2 `k4#f5%e4$e4$d2"h@/ul[up]sjWrfTqbPp`Mo^Kn[ImYHmXFlXElWElWElWElWElWElWElWElWElWEmXFmXFmXFmXFmXFmXFmXEmXEmXEmXFmXFmYGmYGmYGmYGmYGmYGmYGnYGnYGnYGnYGnYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGoYGnYGnYGnYGmYGmYGmYGmYGmYGmYGmYGmYGmXGmXFmXEmXEmXEmXEmXFmXFmXFmXFmXFmXFmXFlWElWDlWElWElWElWElWElWElWElWElWElWElWElWElWElWElVDlVDlVDlVDlVDlVDkVDkVDkVDkVCkVCkVCkUCkUCkUCkUCkUCkUCkUCkUCkUCkTBkTBkTBkTBkTBkTBkTBkTBkUClUClWElYGmZGg?.c0!d2#d2#d2#k4$9���%���)���#������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���$5W)i7&e5%e5%e4$e5$r]Kxucum[siWreSraPp_Np]Ln[JnYImYGmXFmXFmXFmXFmXFnXGnXGnXGnYGnYGnYGnYGnYGnYGnYGnYGnYHnYHnYHnZInZInZInZInZIoZIoZIoZIoZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIoZIoZIoZInZInZInZInZInZInZInZInYHnZInYHoYHoZIoZIoZIoZIoZIoZInZInYHnYHnXGmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmWFmWEmWEmWEmWElWElWElWElWElWDlWDlVDlVDlVDlVDlVDlVDlVDlVDlVDlUClUClUClUClUClUClUClUDlVElWEmXGm\KjK;d3#d2#d2#d2#i5%T& 6���)���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���&���!>~m7&e5%e5%e5%d1!jG6xvdvp^tjXtgUrdQq`Np_Mo\JoZIoZHmXFmXFmXFmXFmXFoYGoYGoYGoYGoYGoYGoYGoYGoYGoYHoZIoZIoZIoZIoZIoZIoZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIpZIoZIoZIo[Jo[Jo[Jp[Jp[Jp\Kp[Jp[Jp[Jp[Jp[Jp[JpZIpZIoZIoZHnXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmWEmWEmWEmWEmWElWElWElWDlWDlWDlVDlVDlVDlVDlVDlVDlVDlVDlUClUClUClUClUClUClUCmVDlWElXFm[JlWFf9(d2"d3#d2#e3$h2#/ c���&���)���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���"Ad0!f6&e5%e5%e3$f8(teTxucumZthWreSqbPq`No]Ko[JoZImYGnYGnYGnYGoYHoYHoYHoYHoYHoYHoYHoZHoZHoZIoZIoZIoZIoZIoZIoZIpZIpZIpZIpZIpZIpZIpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZIpZIpZIpZIp[Jp[Jp[Jp[Kp\Kp]Kp]Kp^Lp^Lp^Lp]Kp\Kp\Kp\Kp\Jp[Jp\Jp[JpZIoZIoZHnYGmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmWEmWEmWEmWEmWElWElWElWElWDlWDlVDlVDlVDlVDlVDlVDlVDlVDlVDlUClUClUClUClUClVDmWEmXFlZHn^LiG6d1!d3#d3#d3#i4%P"���*���*���$��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������ ���&���%K j7'e5%e5%e5%d2"nP>xxfuo]tkYsgUqcQpaOp_Mo^Lo[Jo[JnZHoZIoZIoZIoZIoZIoZIoZIoZIo[Jo[Jo[Jo[Jo[Jo[Jp[Jp[Jp[Jp[Jp[Jp[Jp[Jp[Jp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Jp[Jq]Kq]Kq^Lq^Lq^Lq_Mq_Mp`Np`NpaOp`NpaOp`Np_Mp^Lp^Lp]Kp]Kp\Kp[Jo[JoZImYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmXFmXFmXFmXFmXFlXFlXFlWElWElWElWElWElWElWElWElWElWElWElVDlVDlVDlVDlVDlWEmXFmYGm]LmUCe6&d2"d3#d3#e4$f0 ! P���&���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���")Pg4#g6&e5%e5%d3#g<,vlZwtbtm[shVrdSqbPp`Nq_Mp^Kp\Jo[JoZIoZIoZIoZIoZIoZIoZIo[Jo[Jo[Jo[Jo[Jo[Jp[Jp[Jp[Jp[Jp[Jp[Jp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp\Kq^Lq^Lq^Lq_Mq_Mq`NrbOq]LmR@kH6hA/jE3lN<o[IpaOp^Lp^Lp]Kp]Kp[Jo[Jo[JnZHmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmYGmXFmXFmXFmXFmXFlXFlXFlXFlWElWElWElWElWElWElWElWElWElWElVDlVDlVDlVDlWEmWEmYGn[In_LhA0d1!d3#d3#d3#k6%E��'���)���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���'�+O&l9(e6&e6&e6&e4$qYGyyfvp^tkYsgUsdRqbPq`Np^Lp]Ko\JoZIoZIo[Jo[Jo[Jo[Jo[Jo[Jp\Jp\Jp\Jp\Jp\Jp\Jp\Kp\Kp\Kp\Kp\Kp[Kp[Kp[Kp[Kp\Kp\Kp\Kp\Kp]Kp]Kp]Kp]Kp]Kp]Kp]Kp]Kq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lp]Kp]Kp]Kp]Kp]Kp]Kp]Kp]Kp]Kp\Kp\Kp[Kq]Lq]Mq^Mq_Nq_Mr_Nr_OraOsbPoTCj?.g6&f5%f5$f5$f5$i@/p[IqaOp^Lp^Lp^Lp]Kp\Jo[JoZIoZInZHmYGnYHnYHnYHnYHnYHnYHnYHnYHnYHnYHnYGnXFnXFnXFnXFmXFlXFlXFlWElWElWElWElWElWElWElWElWElWElVDlVDlVDlWEmXFnXFnYHo^LjO=d4$d2"d3#d3#g5%\+A���&���&��� ������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������&���4 nl8'e7'e6&f6&d2"jD4yubyubwo\ukYtgUscQraPr_Mq^Lq^Lp\Jp\Kp\Kp]Kp]Kp]Kq]Kq]Kq]Kq]Kq]Kq]Kq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Lq^Lq^Lq^Lq^Lq^Lq^Lq^Lq^Lr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mq^Lq^Lq^Lq^Lq^Lq^Mr_Nr_Nr_Nr`Os`PsaQsdStdSnN=g8(f5%g7'g7'g7'f7&f7&f5$iB0q_LraOq_Mq_Mq^Lq^Lq]Kp[Jp[Jp[JoZIoZIoZIoZIoZIoZIoZIoZIoZIoZIoZIoZIoZHoYGoYGoYGnYGmYGmYGmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmWEmWEnXFoYGoYIq]Lp\Jg<+d1!d3#d3#d4$k4#9 q���%���*���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���"8_,g7'f6&f6&f5%f5&ubO{ziwp^vlZtiVseRsbPr`Nq_Mq_Mq]Kp\Kp]Kp]Kp]Kq]Kq]Kq]Kq]Kq]Kq]Kq]Lq]Lq]Lq]Lq]Lq]Lq]Lq^Lq^Lq^Lq^Lq^Lq^Lq^Lr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mq^Lq_Mr_Mr`NraOraOsbPsbQseTscRnM<g6'g6'g7'g7'g7'g7'g7'g7'f6%f8'nTBsdRraNq_Mq^Lq^Lq]Kp\Jp[Jp[Jp[JoZIoZIoZIoZIoZIoZIoZIoZIoZIoZIoZIoZHoYGoYGoYGoYGmYGmYGmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmXFmWEmWEnYGoYGo[Jq`MkL:d2"d3#d3#d3#g5%W' 3���+���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���'���#Hl9(f6&f6&f6&e3#mL:{|hyt`vp\ulXtiUseRrdPqbNqaMqaMp_Kp_Kq_Kq_Kq_Kq_Kq_Kq_Kq_Lq_Lq_Lq_Lq_Lq_Lq`Lq`Lq`Lq`Lq`Lq`Lr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`MraMraMraMraMraMraMraMr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`MraNsaNrbNrcOrcOsdPsfRtiUteRmI8g5&g6'g7(g7(g7(h8(g8(g7'g7'g7'f5$lK:sfSrcOqbNq`Lq`Lq_Kp^Jp^Jp^Jp^Jp^Jo]Io]Io]Io]Io]Io]Io]Io]Io]Io]Io\Ho[Go[Go[Go[Gm[Gm[GmZFmZFmZFmZFmZFmZFmZFmZFmZFmZFmZFmYEnZFo[Go]Ip`Kp\He8(d1"d3#d3#d3#g3",Y���%���(���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���$ Id2"h8'f7&f7&f6%h;*vjW{yfwr_um\tiXsgVrdSrcPqbOraNq`Mq_Lq_Lq_Lq_Mq_Mq_Mq_Mq_Mq_Mq_Mq_Mq_Mq`Mr`Nr`Nr`Nr`Nr`Nr`Nr`Nr`Nr`NraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNraNr`Nr`Nr`NsaOsbPsbQtdStdStfStjWscPlH5g6'g7(g7(g7(g7(i9*b0"c1#h8(g7'g7'f4$lK9rfUrcQrcPqaNq`Mq_Lp^Kp^Kp^Kp^Kp^Kp^Ko]Jo]Jo]Jo]Jo]Jo]Jo]Jo]Jo]Jo]Jo\Io[Io[Io[In[Im[ImZHmZHmZHmZHmZHmZHmZHmZHmZFmZGmZFmYGoZHo\Ip^KqbOjF5d1!d3#d3#d3#i5%P!�,���)���$��������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���&��'T$m:(f7&f7&f7&e2!pWF|~kyudvp_ulZuiWsgUseSrcPrbOqaNq`Mq`Nq`Nq`Nq`Nq`Nq`Nq`Nq`Nq`NqaNraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOrbPsbQscRscRteTtgUtgUtkYscQmE5g6&g8(g8(g8(g7(l:+^- Of4%h8)g7'g6&g8'pTBshWrdRrcQqcPqaOqaNp_Lp_Lp_Lp_Lp_Lp_Lo^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko]Jo\Jo\Jo\Jo\Jm\Jm]Jm[Im[Im[Im[Im[Im[Im[Im[Hm[Gm[Gn[Io\Jp^LpbOnWEf7&d1!d3#d3#f5$d.! N���'���'���!������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���"1 bk6%g8'f7&f7&e4#jC1zucz{hws`vo\ukXthUsfSsdQrcPraOqaOq`Nq`Nq`Nq`NqaNqaNqaNqaNraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPraOraOraOraOraOraOraOraOsbPscQscQsdRteTtgUtiUumYs_Lk?/i5'i8)h8)g8(g8(m;*W)6 t]*i9*g7(g7(g5%i=,saNtiUrdRrcQqcPqbOqaNp_Lp_Lp_Lp_Lp_Lp_Lo^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko]Jo]Jo]Jo]Jm]Jm]Jm[Im[Im[Im[Im[Im[Im[Im[Hm[Gm[Io]Jo]JpaNp`Mh?.c1!d3#d3#d4#l5$<���&���+���"��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���# 0^*i9)f7&g7'g6&g5$t_N{nytcwp]ulZtiWsgVseTrcRrbPqbOq`Nq`NqaNqaNqaOqaOqaOraPraPraPraPraOraOraOraOraOraOraOraPraPraPraPrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQrbQraPrbPrbQscQscRsdStfUtgVtiXvm\r^Lk>.i5'i8)i8)i8)h8)n;*X*U8j9)g7(g7(g7(g5&lJ9ukYsgUseSrcRqcPqbOqaNp_Mp_Lp_Lp_Lp_Lp_Lp_Mo^Lo^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Ko^Lo]Jo]Jo]Jo]Jm]Jm]Jm[Im[Im[Im[Im[Im[Im[Im[Im[Io\Jo]Jo_LqbPmQ?d2#d3#d3#d3#g5$]+9���*���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���"=rl9(g7'g7'g7'f4$lH7|{h{yfzs`wo\vlYuiWtgUseSsdQscQrbOrbOrbPrbPrbPsbPsbPsbPsbPsbPsbPsbPsbPsbPsbPsbPsbQsbQsbQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscRsdRtdRtfStgUthUuiWvjYwo^s]Lk<-i7(i9*i8)i8)j9*r;+Q&OWd2"i9)g7(g7(g6'h9*saOvlYthUtfTsdRrcQrcPrbOqaNq`Mq`Mq`Mq`Mq`Mq`Mp_Lp_Lp_Lp_Lp_Lp_Lp_Lp_Lp_Lp_Lp^Kp^Kp^Kp^Ko^Kn^Kn\Jn\Jn\Jn\Jn\Jn\Jn\Jn\Jn\Jp]Kp^LqaOq`Nf;*d2"d3#d3#d3#i4$0 j���'���*���"������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���$9c1"i9)g7(g7(g6(h7'vfT}~lzucyq^wm\ujYtiVtgTseRsdQtcQsbPsbPsbPsbPsbPsbPsbPsbPsbPsbPsbPsbPscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscQscRtdSteRtfSuhUuiVujWvkZwm\qWFj<,i7(i9*i9*i9*i9*o9)N$H���<L"n;*g8(g8(g7(f5&oP>wn[vkXthUtgTteRrcPrcPrbOqaNq`Mq`Mq`Mq`Mq`Mq`Mq`Mp_Lp_Lp_Lp_Lp_Lp_Lp_Lp_Lp_Lp^Kp^Kp^Kp^Kp^Ko^Ko]Jo]Jo]Jo]Jo]Jo]Jo]Jo]Jp]Kp_Lp_LrdPlL;d1"d3#d3#d3#i5%P#/���)���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���'��$K n:*g7(g7(g7(e2$qSB}m|xhyscwp^vl[ujYthVtfUtdTtcRscRsbQsbQsbQsbQsbQsbQscQscQscQscRscRscRscRscRscRscRscRscRscRscRsdRsdRsdRsdRsdRsdRsdRtdRtdRtdRtdRtdRtdRtdRtdRtdRtdRtdRtdRtdRtdRsdRsdRsdRsdRsdRsdRscRscRscRscRscRscRscRscRsdSsdTtfUtfUthVujWukXvm[wo^pVEi8)i8)i9*i9*i9*j;,o:+K��?���8+ pn6&i:*g8(g8(f5&i=,ufTwp^vkXthVtgUteSrcQrcQrbPqaOqaNq`Nq`Nq`Nq`Nq`Nq`Np_Mp_Mp_Mp_Mp_Mp_Mp_Mp_Mp_Mp_Lp^Lp^Lp^Lp^Lo^Lo^Lo]Ko]Ko]Ko]Ko]Ko]Ko^Lp^Mp`MqdPoZHf7'c1!d3#d3#e4$h0! U���$���(���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���"%Mh4$i:*g8(g8(f5&j=.zsa}~mzvcxs`vo\ulXtiVthUtfUtdSsdRscQscQscQscQsdRsdRsdRsdRsdRsdRsdRsdRsdRsdRsdRsdRsdRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRteRsdRsdRsdRseSteTtgVthVuiWujXvlZwq^wm[pR@j9*j8)j9*j9*i9*k;,m7(D>���= HX)n;+i8)g8)f7'g7'qXFwq^vn[ukYtiWthUtfSsdQrcPqcPqbOqbOqaNqaNqaNqaNqaNqaNp`Mp`Mp`Mp`Mp`Mp`Mp`Mp`Mp`Mp`Mp_Lp_Lp_Lp_Lo_Lo_Lo^Ko^Ko^Ko^Ko^Ko_Lp`Mp_LqcPqcPiE3c1!d3#d3#d3#l6%B���(���+���$��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������ ���# +V'l;+i8)i8)i7(g4%t\K~r{xfztaxq^wn\vjXuiWtgUtfUsdSsdRscRsdRsdSsdSsdSsdSsdSsdSsdSsdSseSseSseSseSteSteSteSteSteSteSteSteSteSteSteSteSteTteTteTteTteTteTteTteTteTteTteSteSteSteSteSteSteSteSteSteSteSteSteSteSteSteSteSteStfStgUtgVthVuiXujYulZxq_wl[oN>i8(j8)j9*j9*j9*l;+n7(>w��3���=���:9p:*i8)i8)h8(f5%lF5xtawq^vmZukYtiWthUsfSsdRscPqcPqbOqbOqaOqaNqaNqaNqaNqaNqaNp`Mp`Mp`Mp`Mp`Mp`Mp`Mp`Mp`Mp_Lp_Lp_Lp_Lo_Lo_Lo^Ko^Ko^Ko^Ko_Lp_Mp`MqaNreRnVDd3"d2"d3#d3#g5$`->���)���'��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���!2 fo9*i8)i8)i8)h5&mE5}{j~}l|we{sbyp_wm]wkYviXuhWuhVtfTteTteUteUteUteUteUteUteUtfUtfUtfUugUugUugUugUugUugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugUugUugUugUugUugVvhWviXviXvjYwk[wm\yraxkXoI7j7'j:*j:*j9*j9*m<,g4&6 h��5���;���;Le2#l:+i8)i8)i7(i9)vcSzueyq_wn\vkZuiXuhVtfUteSsdRrcQrcQrcQrcQrbPrbPrbPrbPrbPrbPqaOqaOqaOqaOqaOqaOqaOqaOqaOq`Nq`Nq`Nq`Np`Np`Np_Mp_Mp_Mp_Mp`NqaOqaOrcQrdRh@.d1!d3#d3#d3#h4$:v���'���*���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���$/b/!l;+i9*i9*i8)i7(xfVq|yg{udzp`xn]wl[vjXuhVuhWugVteUteUteUteUteUufUufUufUufUugUugUugUugUugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugUvgVvhWvhWviXwjYwk[yn^{scyiXnF5i7'j:*j:*j:*j:*p=,h4%* \���.���:���=���5F o;+i9*i8)i8)h5&oM={udzsbxo_wn[vjYuiWtgVtfUtdSsdRscQrcQrcQrcQrbPrbPrbPrbPrbPrbPqaOqaOqaOqaOqaOqaOqaOqaOqaOq`Nq`Nq`Nq`Np`Np`Np_Mp_Mp_Mp_MqaOqbPqbPseSmR@c2"d2"d3#d3#g5%Z( 7���(���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���"?~n;+j9*i9*i9*h5&pO?o~}l|wfzsbyo^xm\vk[vjYuhWugVtfVteUteUugUugUugUugUugUugUugUugUugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVvhWviXvhWviXwjYwl[ym]{sbxgWmD3i8'j:*j:*j:*j:*q=,b1# L���*���7���:���4$ Wj5&k;,i9*i8)h6'k?.xm[{xgyq`xo^vl[vjYuhWtgVteTsdSscQscQrcQrcQrcQrbPrbPrbPrbPrbPrbPqaOqaOqaOqaOqaOqaOqaOqaOqaOq`Nq`Nq`Nq`Np`Np`Np_Mp_Mp_Mp`NqaOqbPseSp]Jf9(c1!d3#d3#d4#j3"% b���$���)���#������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���$ Bf2$l;,i9*j9*j8)k:*{p_r}xh{vcyr`xo^wm\vkYuiXuhWugVugUugUugUugUugUugUugUugVugVugVugVugVugVugVugVugVugVugVugVugVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVuhVugVugVugVugVugVugVugVugVugVugVugVugVugVugVuhVuhWviWvjXvkZxl[xm\yo]{savcRmB1i8'j;*j;*j:*j:*p=+_.! G���'���4���6���7��8L%p<,i9*i9*i8*i6'sZH||jzucyp_wn]vk[vjXuhWtgVteSsdRscQscQrcQrcQrcQrbPrbPrbPrbPrbPrbPqaOqaOqaOqaOqaOqaOqaOqaOqaOq`Nq`Nq`Nq`Np`Np`Np_Mp_Mp`MqaNqaPrdQsgSkJ8c1!d3#d3#d3#j6%N" �-���)���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%��&R$p<,j9*j:*j:*i5%tZIs}|l|wf{uczq_xn]wl[vjYviXuhWugVuhVuhVuhVuhVuhWuhWuhWuhWuhWuhWuhWuhWuhWuhWuhWviWviWviWviWviWviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviWviWviWviWuhWuhWuhWuhWuhWuhWuhWuhWuhWuiWviXvjXvjYwl[wl\xn\zq_{tcubQk?.j9(j;*j;*j;*j<*q>,\*6���)���2���4���6���.0 kq9)i9*i9*i9*h6'lC3{ve{yfyubxq_vm]vkZuiXthWtgVtfTsdRsdRsdRrcRrcRrcRrcQrcQrcQrcQrcQrcQqbPqbPqbPqbPqbPqbPqbPqbPqbPqaOqaOqaOqaOpaOp`Np`NpaNqaOqbPrcPshVp]Ke5$d2"d3#d3#f5$c/ # M���(���(���!������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���!% Wo9)j;+j:*j:*i7'mA1}wfp|zi{vezrayp^xm\wk[vjYvjYuhWuhWuhWuhWuhWuhWuhWuhWuhWuhWuhWuhWuhWviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXviXuhWuhWuhWuhWuhWuhWviXvjYvjZvk\wm\yo]zq`zsbsZJk=-j9)j;*j;*j;*j<+o=+Z(/���&���/���0���3���2 ?`.!m;,j9*i9*i8)i7(ubQ}|lzveysawp^vl\ujYuiXtgVtgVteTsdRsdRsdRrcRrcRrcRrcQrcQrcQrcQrcQrcQqbPqbPqbPqbPqbPqbPqbPqbPqbPqaOqaOqaOqaOpaOp`Np`NpaOqbPqbPrdRsfTjE4c1!d3#d3#d3#j5$C���'���+���$��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���%+_-m<,j:*j:*j:)i8&xfSs}|j|xg{uczq_yp]wn\vlZvkYvjXuiWuiWuiWuiWuiWuiWuiWuiWuiWvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXuiXviXujXvkZvl[vm[wn\yp^ztbzr`sWFk;+j9)k;+k;+j;+k<+q>,S! &���&���,���.���0���3���-Fp;+j9*j9*i9*h6'nJ9}}j|zhzvcxs`vp]vm[ujXujWthVtgUsfSseRseRseRrdRrdRrdRrdQrdQrdQrdQrdQrdQqcPqcPqcPqcPqcPqcPqcPqcPqcOqbOqbOqbOqbOpbOpaNpaNqbOqcPqdQsiVoXFd2"d2"d3#d3#f5$b,<���(���&��� ������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���#3mm;+k<+j;*j;*i8'oJ9pp}{j{vfzscypaxo^wm]vk[vjZujYuiXvjXvjXvjXvjXvjYvjYvjYvjYvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjZvjYvjYvjYvjYvjYvjYwjZwk[wk[wl\wm]wo^xp`zuezrarRAk:*k:*k;+k;+k;+n<,p;*Bv #���&���*���*���,���0���/ Ge3%m<,j9*j9*i8)k=-yl[~m{wgytcwq`vo^uk[ujYtiXtgWsgVseUseTseTseTrdTrdTrdTrdSrdSrdSrdSrdSrdSqcQqcQqcQqcQqcQqcQqcQqcQqbPqbPqbPqbPpbPpbPpaOpbPqcQqdSrgVqcPg>-c1!d3#d3#d3#j4#1 s���'���)���#������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���%9h2#m=,j;*k;+k:*j8(zn^y~|m|xi{ufzrcypawn^vl\wk\vj[vjYvjYvjYvjYvjYvjYvjZvj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vjZvjYvjZvj[wk\wk\wl]xm]yo_ypazvgzraqO>k:*k;+k<,k<,k;+n=-m:*<n�����#���'���(���*���-���/��.Q#p=,j:*j9*j9*i4%sWGq|zjzvewsbwp_vn\uj[tiYtiXsgWseVseVseUseUseUrdUrdUrdUrdTrdTrdTrdTrdTrdTqcRqcRqcRqcRqcRqcRqcRqcRqbQqbQqbQqbQpbQpbQpbQqbQqdSreTsjYmP?d1!d2"d3#d3#i6%W% 1���(���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������ ���&��%J"r>,k;+k;+k;+j8(tVEtq}yj{wfztdyqayo_yn]xl]wj[vjZvjZvjZvjZvj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vk[wk\xl\xm]xm]yn]yo_zqb{vfzp`pK9k9)k;+k<,k<,k<,n>.l9)$ T������$���%���&���'���)���-���(, ]o9)k;+j:*j:*i5'mB2|xg~n|yg{tdxqavp^vm\ujZtiYtiXsgWsfVsfVsfUsfUsfUreUrdUrdUrdTrdTrdTrdTrdTrdTqcRqcRqcRqcRqcRqcRqcRqcRqbQqbQqbQqbQpbQpcQqbQqcRrdSsiXqaOf9(d1!d3#d3#f5$d0!$Y���&���(���!������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������$���  Ip8(l=-k<,k<,j:*l?.}tbu~}l}yi{vezrbzp`yn]yo^ym]xk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[wk[xl\xl\xl\xl\xl\xl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\xl\xl\xl\wk[wk[wk[wk[wk[wk[wk[wk[wk[xl\ym]yn]zp^zp^{p`zsb|wgym\oI8k:*k<,k<,k<,k<,p?.k7'& P������#���#���#���$���'���+���+2]*m<,j:*j:*j9)i6'u_Nt}zi|we{tcyp_wo\vm[ukYuiXuiWtgVtgVtgVtgUtgUtgUsfUseUseUseTseTseTseTseTseTrdRrdRrdRrdRrdRrdRrdRrdRrcQrcQrcQrcQqcQrdRrdRrdSsgUtjXkK9c0 d3#d3#d3#k6%J ���)���,���$��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%)V(o?.k<,k<,k<,j8(w^Nxp~{l|xh|te{rbzqayo_yn^ym]wk\wk\wk\wk\wk\wk\wk\yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]yl]xl]wk\wk\wk\wk\wk\xl]ym^yn^yo_zp_{qb{sd|wgyl[nG5j9(k<,k<,k<,k<,q?.i5%" ?������!���!���!���"���$���(���,���&>oo<*j;*j:*j:*i7'nI8n~~o|yi{uezrcxp_vn]vl[ujYuiYtiXtgWtgWtgWtgVtgVtgVsfVseVseVseUseUseUseUseUseUrdTrdTrdTrdTrdTrdTrdTrdTrcRrcRrcRrcRqcRrcRrdSrfUtkYq^Kd5%d2"d3#d3#f4$b.D���(���'��� ������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���#5 ^p<,l=,k<,k<,j:*nD3qu~nzj~wg|td|rb{q`zo^zo^ym]yl\yl\yl\zl]zl]zl]zl]zl]zm]zm]zm]zm]zm]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zn]zm]zm]zm]zl]zl]zl]zl]zl]yl\ym]yn]zn]zo^zp`{qa|rc|ue~xgyeSoC2k:*k<,k<,k<,k<,p>.c.  2������ ��������� ���"���$���)���+:a2#n=,j:*j:*j:*j9(zhWr|l}xg|ud{rbzp_wn\vkZvkYviXuiWuhWuhWuhWuhVuhVuhVtgVtgVtgVtgUtgUtgUtgUtgUtgUseTseTseTseTseTseTseTseSsdRsdRsdRsdRreSseTsfUthVuhViC2c1!d3#d3#d3#j5$=���'���)���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���&.`/!o?.k<,k<,l=-k9){iYyq|k~xh}vf{sc{qazp_zo_zn^zm]zl]zl]zl]zm]zm]zm]zm]zm]zn]zn]zn]zn]zn]zn]zn]zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn^zn]zn]zn]zn]zn]zn]zn]zm]zm]zl]zl]zl]zm^zn_zo_zp_{q`{rb|sc}vf~xhwaPnA1l;+l=-l=-l=-l=,p?.b-'���������������������"���&���*��%Iq>+j;*j;*j:*h4$rSBso}yi|vf{sczp`yo]vm[vkZujXuiWuiWuhWuhWuhWuhVuhVuhVtgVtgVtgVtgUtgUtgUtgUtgUtgUseTseTseTseTseTseTseTsdSsdRsdRsdRreSsfTsfUtgUvm[oVDd3#d2"d3#d3#g6%Z(8���'���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���!>{s>-l=-l=-l=-k9)tQ@vv~nzjwh~ve|sc|qa{q`{o_zn^zn^{n^{n^{n^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{n^{o^zo_{o_{p_{qa{qa|sc}ud~xhzju\Ll=.k;,l=.l=-l=-m?.rA/P#"������������������������#���)���%$ Ml6&k=+j;*j;*i8'l?.~udu|l}xh}ue|sc{q`zo]wm\wlZvkYviXviXviXviXviXviWviWviWuhWuhWuhWuhVuhVuhVuhVuhVuhVtgUtgUtgUtgUtgUtgUtgUtfTteTteTteTsgUtgVthVvkYteSg>-c1"d3#d3#e4$i3#, e���%���)���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#���! Al6'n@/l=-l=-k;,m?/~sc{p}lyj~wg|tc|rb{qa{p`{p_{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{p_|p`|qa|qa|qb|sc}ud~yi~yguZIl=.l;,l=.l=.l=.m>.t@.Q" ������������������������� ���%���(�+Z&p>-j;*j;*j:)i7&u]Lxnzj~wf|tc{razp^yn]xl[wlZvjXviXviXviXviXviXviWviWviWuhWuhWuhWuhVuhVuhVuhVuhVtgUtgUtgUtgUtgUtgUtgUtgUtfTteTteTtfUtgUthVujXwmZnQ@c1!d4$d3#d3#j6%R#��*���+���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���%��%I$r@0l=.l=.l=.k9+wZJys~n{j~wg}uf}sc|rb|qa|p`{o^{o^{o^{o^{o^{o^{o^{o^{o^{o_{o_{o_{o_{o_{o_{o_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_{o_{o_{o_{o_{o_{o_{o_{o_{o_{o^{o^{o^{o^{o^{p^|q`|qa|qa}rb}rd~ueyiyiuWEl<,l=-l>.l=.l=.m?0s@/;q �����������������������������"���'���$1 ]o;*l;+k;+j;*i8'nE4~ms|l~xh}ud{razp`yo]ym\xk[vkXvjXvjXviXviXviXviXviWviWviWuhWuhWuhWuhVuhVuhVuhVuhVtgUtgUtgUtgUtgUtgUtgUtgUtfTteTteTugUthVtiWukYsbPf9(d3#d4$d3#f4$c0! R���(���(���!������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���"* Nr=,m>/l=.l=.k;+nB2pxp}m~zi~vg~tf}rb|qa|qa{p_{o^{o^{o_{o_{o_{o_{o_{o_{o_{o_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_|p_{o_{o_{o_{o_{o_{o_{o_{o_{p_|p`|qa|rb}rb~te~ugzk~uesPAl;,l=-l>.l>.l>.o@0r<,7e������������������������������ ���%���(�-`0"o?-k;+k;+k;+i8'zhWxo~zj}wf{tc{qazo_yn\yl\xk[vjXvjXvjXviXviXviXviXviWviWviWuhWuhWuhWuhVuhVuhVuhVuhVtgUtgUtgUtgUtgUtgUtgUtgUtfTteTtfTthVtiWuiWvkYjG6c1"d4$d4$d3#i5%G���'���)���$������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���(��'[+r@0l=.l>.l>.l;*{gW}uq|mxiwgufsc~rc~qb}qc}pb}pb}pb}pb}pb}pb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb~qc~qc~qc~qc~qc~qc~qc~qc~qc}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}pb}pb}pb}qc~qc~rdsdtevfxizmrcsP@l;,m=.m>/l>.l>.n@0q=,4R���������������������������������"���(���#?zr?-l=-k;+k;+i7&rP?uu|nyjvf}tc|qb|p`|o^{m]zl]yk[yk[yk[yj[yj[yjYyjYxjYwjYwjYwiYwiYwiYwiXwiXwiXwiXwiXvhWvhWvhWvhWvhWvhWvhWvhWvgVvgVvhWviXwjYyo_rZId4$d3#d4$d4$f5$a,B���&���&��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������&���"5eq=-m?/l>.l>.l;,sL<vzr}nzlxivfte~sd~qd}pc}pb}qb}qb}qb}qb}qb}qb~qc~qc~qc~qc}qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc~qc}qc}qc}qc~qc}qb}qb}qb}qb}qb}qc~rd~sdsdufvgyi}oparK:l<,m>/m>/m>/m>/pB1l6'3I������������������������������������%���$>f2$n?.k<,k<,j9*k;+rb{o{kxg~ue|tc|p`{o^{n^zl]zk\zk\yk[yk[yj[yj[yjYyjYxjYwjYwjYwiYwiYwiYwiXwiXwiXwiXwiXvhWvhWvhWvhWvhWvhWvhWvhWvgVvhWwiXviYxl\xjYiB2c1!d4$d4$d4$j5#9 z���&���)���"��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������#���"7e4%pA0l>.m>/l<.m=-p`}tp|mzjwgvftdsdrcrbrbrbrbrbrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcscududufvgxgzj~n~l\pH7l=,m?/m?/m?/m?/rA1i4&- 8������������������������������������#���&��%S#q?.k<,k<,k<,i6'vXHzr}l{jwguc~ra}p_}o^}n^|l\|l\|l\{l[{l[{k[{k[{kY{kYzkYykYykYyjYyjYyjYyjXyjXyjXyjXyjXxiWxiWxiWxiWxiWxiWxiWxiWxiWxiWyjXyk[|q_qWEc2!d4$d4$d4$j6%W%1���)���%��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������$���#;tA1m>/m>/m>/l9+wUE{xs~o|kyiwfueudtdrbrbrbrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrcrctcududvevgwizkphYpD4l=,m@/m@/m?/m?/sC2a/!'���������������������������������������%���## Rn;+l=-k<,k<,j:*nB1yiwo|kyhvetb~q`}q^}o^}m\|l\|l\|l\{l[{l[{k[{k[{kY{kY{kYzkYzkYzjYzjYzjYzjXzjXzjXzjXzjXyiWyiWyiWyiWyiWyiWyiWyhVziWyjXykX{n\xfSg<+c2"d4$d4$e5%e2"* \���'���(���"������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���  Co<,n?0m>/m?/l=-oA1|o|tq|mzjxiuhufudrcrcrcrdrdrdrdrdrdrdrcrcrcrcscscscscscscscscscscscscscscscscscscscscscrcrcrdrdrdrdrcrcreseufvfvfwhwj{mr|eTqC3n>.n@0m@/m@/mA/sB1`- (���������������������������������������"���(���']- q?.k<,k<,k<,j7'|dS{s~oziwgue~rb~p`}p_|n]|l]|l]|l]|l]{l\{l\{l\{k\{kZ{kZ{kZzkZzkZzjZzjZzjZzjYzjYzjYzjYzjYyiXyiXyiXyiXyiXyiXyiXyjXzjYyjXzl[|r`oL;c1"d4$d4$d4$i6&S"���*���*���%��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������������$��%S's@1m?/m?/m?/l;+{aPwso}lzkwhvfvfuescsdsdsdsdsdtdtdtdtdudududududueufufufufufufufufufufufufufufufueudududududududtdtdtdteufugvhwhxizj|ns|dTo@/n>.o@0o@0o@0oA0vD1K( ������������������ ��� ���������������������&���#4io=-m>.k<,k<,j8(rK;tyq|lyhwfudra~r_~p^}n^}m]}n]}n]}n]|n\|n\|m\|l\|l\|l[|l[{l[{l[{k[{k[{k[{kZ{kY{kY{kY{kYzjXzjXzjXzjXzjXzjXzjXzkY{kY{l[|q`wbPf8'c2"d4$d4$e5%e1! O���'���'���!������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������!���8Zt>/n@0m?/m?/l>-rH7u}vq~n{kxixhwfvgtesdtdtdtdududududududueufufufufufufufufufufufufufufufufufufufufufufufufufufududududvevfvgvgwhxizj~n~oz^MoA/n?.oA0o@0o@0oA1vC1I$������������������� ��� ��� ��� ���������������#���$7i2$p@/l=-k<,j;+j:)p`r~n{jxhvescra~q_}p^}o^}n]}n]}n]}n]|n\|n\|m\|l\|l\|l[|l[{l[{l[{k[{k[{k[{kZ{kY{kY{kY{kYzjXzjXzjXzjXzjXzjXzkY{kY|lZ|o]}r_mG6c0 d4$d4$d4$k6%E��'���(���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������������(]0#sB1m?/m@/m?.m>,jYytq|oykxhwiuhugteteueueueufufufugugugugugugugugugugugugugugugugugugugugugugugugugugugugugugugugugufvgvgviwjxj{mp}myZKn>.o@0oA1oA1oA0qC2vC0H"q�������������������� ��� ��� ��� ��������������� ���%��$Fr?0l=.l=-l=-j7&vVEyvp|myjvhtdrb~qb}q`}o^}o^}n^}n^}n^}n^|n]|n]|n]|l]|l]|l\|l\{l\{l\{k\{k\{k\{k[{kZ{kZ{kZ{kZzjYzjYzjYzjYzjXzjY{k[{k\|l\}rat[If4#d3#d4$d4$i7&V) ;���(���&��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������EluC0m@/m@/m@/l:)vTD~ws~p|mzkwiwhvhufufufugugugugugugugugugugugugugugugvgvgvgvgvgvgvgvgvgvgugugugugugugugugugugugugugvhwhxhxiykyk{nsykxVGo?-oA0oA1oA1oA1rD3t@.A\��������������� ��� ��� ��� ��� ��� ���������������#���" Co9)l>/l=.l=.k;+m>.ue~s}ozkwhufsd~rb~qb~o_}o^}o^}o^}n^}n^}n^}n^|n]|n]|m]|l]|l\|l\{l\{l\{l\{k\{k\{k[{k[{k[{k[{k[zjYzjYzjYzjYzjY{kZ{m\{m\}o^{kZj?/d1!e4$d4$e5%i4$5 l���%���*���#������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ��������� -l8)nB0m@/m@/n>.p@0zj{vsp|mzjxiwhvhvfvgvgvgvgvgvgvgvgvgvgvgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgvgvgvgvgvgwhxixixjzk{k|nsvgwSBo@.pA0pB1pB1oA1qD3r=-?S������������������ ��� ��� ��� ��� ��� ���������������"���'��(V)qA0l=.l=.l=.k8){`P}vq}lzjwgvdscrbqap_p^p^p^o^o^~o^~o^~o]~o]~n]~n]~n\~n\~n\}n\}n\}l\}l\}l[}l[}l[}l[}l[|kY|kY|kY|kY|kZ}m\}n\~o]rarSAd2"e4$e4$d4$i6&U&��.���+���%��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ���������V'sC1n@0n@0o@0m;+z[L{vr~o|nzkxiwhwhvgvgvgvgvgvgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwgwhxixiyj{l|l|ntuduL<o?/pB1pB1pB2pB1vF4l9*3 ?����������������� ��� ��������� ��� ��� ���������������%���$3 Wp<,m?/l>.l>.k;,pD4tzr~n{kxhweudrbqaq`p_p^p^p^o^o^~o^~o^~o]~o]~n]~n]~n]~n\~n\}n\}n\}l\}l\}l[}l[}l[}l[}l[|kY|kY|kY|kY|l[}n\}n\q_|hUh;*d2"e4$e4$d4$g2"' U���'���(���!������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������.8n>.oA1o@0o@0n>.qD3x{xtq~o{mykyjykwjwiwhwhwhwhwhwiwiwixjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjxjwixjxjxkykzl|n~o~qyqbtJ:p@0qC3pB2pB2pB2wF4i7(- <������������������ ��� ������������ ��� ��� ������������"���$+`."qA1l>.l>.l=.k9)k[ws~o{lyiwfvetcrbqbqaqaq`q`p`p`p`p`p`p^o^o^o^o]o]~o]~o]~m]~m]~m]~m\~m\~m\~m\}l[}l[}l[}m\}o]~o^q`udpL;d0 e4$e4$e4$j7&E��*���*���%��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� ��� V/"vD3o@0o@0oA0n=,hX}zus}q|n{lzlylxlxkwiwiwiwjwjxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkylzlzm{m|o}prwoasH6p@0qC3qC3qC3qC3xF5_3& *������������������ ��� ��������������� ��� ���������������$���"=wu@0m?0m>/l>.j:*uP>x}vq}ozkwhvgudtdqbqbqbqbqaqapapapapapap_p_o_o_o^o^~o^~o^~n^~n^~n^~n]~n]~n]~n]~n]}l\}m\~n^~o^p_tezbQg7'd2"e4$e4$h6&_. D���&���'��� ������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���DOvD2oA1o@0oA0m;)wSB~zwtr}o|n{mylylxkxkxjwjxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkyk{l{l{m{o}pswiZqD4pA1qC3qC3qC3rD4yG5]0#(��������������� ��� ��������������������� ���������������"���!<m7'n@1m>/m>/k<,m>-uews~o|lxivgugtdsdqbqbqbqbqbqaqapapapapap_p_o_o_o^o^o^~o^~o^~n^~n^~n]~n]~n]~n]~n]}l\}n]~o^~o^sbo_mD4c2"e4$e4$e5%l5%;���$���+���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������o9*pB1oA0oA0n?/o>.}o}yvtr~q}p{mzmzlzlykykxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkylylzl|n|o|o~puugWrF4pB1qC3qC3qC3rD4yF5[- ��������������� ��� ��������������������� ��� ������������ ���%���$I$rB1m?/m?/m>/k9*zZJzup|nzkxhvguftdrcrcqbqbqbqbqaqapapapapap`p_p_o_o^o^o^~o^~o^~n^~n^~n]~n]~n]~n]~m]}l\~n^~o^q`uevZId3"e5%e5%e4$i7&[* 6���+���&��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������W*`uC3oA0oA0oA0n=-}_Q}yxvsr~p}p|o}o{m|n{mzlylylylylylylylylylylylylylylylylylylylylylylylylylylzl{m|o|o~p~pqwu~aQqB2pC3qD4qD4qD4rF5wF3R$w���������������� ��� ������������������������ ���������������$���$' Ip=-oB0m?/m?/l=.oA1uxtp|mykxivgvgtesdsdrcrcrcrcrcrbrbqbqbqbqbqaqapap`p_p_p_p_o_o_o^o^o^o^o^o^p_p_scq`i=-d2#e5%e5%e4$j4$0 b���&���)���"������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������" n>/qB1oA0oA0oA1n?-p}zxvusr}p~p|o|o|o|n{mzlylylzlzlzlzlzl{m{m{m{m{m{m{m{m{mzlzlzlzlzlzlylzl|n|o|o}prrx~r}^NrD4qC4qD4qD4qD4sF6vE3O!m ��������������� ��� ������������������������ ��� ������������!���(��%Y+tD2m@/m@/m?/l;*gX{ur}n{lxjwhvguftesdsdsdrcrcrcrcrbrbqbqbqbqbqaqapap`p_p_p_p_o_o_o^o^o^o^o^o^p`rbxgtTBd3#e4$e5%e5%j7&Q!�-���)���%��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������U/#yF3oA0oA1oA1oA1pA0uN=pb{xxwtrs~q~p}o|o|n|n{nzm{m{m{m{m{m{m{m{m{m{m{m{m{m{m{m{m{m{m{m{m|n|n|o}o~qrtyr{XHq@0rC4rD5rD5rD5vG7uC2@Z����������������� ��� ��������������������������� ���������������$��� /au?.pA1m@0m@/k<,tK9wwrp|nyjwiwhuftdsdsdsdsdsdrcrcrcrbrbrbqbqbqbqaqaqapap_p_p_p_p_o_o_o^o^o^p_p_q`uehWi;+d3#e5%e5%f6&g1!# T���%���'���!������������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� pA0zH5oA1oA1pB1pB1pB1n>.n;,wN>vg~{zxvvut~r}q|p|p|o|p|o{n{n{n{n{n{n{n{n{n{n{n{n{n{n{n{n{n{n|o|o|p|p~rtuz{nyRDqB2rD4rE5rE5qD5xG8n>06N����������������� ��� ��������������������������� ��� ������������"���"0f3$rC3o@0n@0l>-m>-p`{vr}p|mykwiwhufsdsesesdsdsdrdrdrdrcrbrbqbqbqbqaqaqapap`p`p`p`p`o`o`o_o_p_qaqatdrbpH7c2"e5%e5%e5%n8'?���&���*���$��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������'M-"vF4qC2pB1pB1pB2pB2qB2p@0p>.}\My}|zxxvu~s~s}r}s}r|q|p{p{p{p{p{p{p{p{p{p{p{p{p{p{p|p|q}q}r}s~sux{{owQBqC2rE5rE5rE5rE5xI8o?0 A������������������ ��� ������������������������������ ���������������%���#:}uE2o@0o@0o@0m<,xUEzvs}pznxlwiwiuhtgtgtgtftftfrfrfrfrerdrdqdqcqcqcqcqcpcpbpbpbpbpboboboaoaqcqcrdvg|aQf4#e4$e5%e5%h7&`.=���)���'��� ������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>0p<,vF4tD3pB3qC3qC3qC3qC3o=-rB2dV~{yxwuus~s~s~r~r}q|p|p|p|p|p|p|p|p|p|p|p|p|p}q~r~s~stux~tgvM>qD4rF6rF6rF6rF6yI8k9* ;������������������ ��� ������������������������������ ��� ������������#���$>o;+qC2oA0oA0o>/o?/}o|xu~q|ozmxkxjwivhugugugugufuftfsfsfsfsdsdrdrdrcrcrcrcqcqcqbqbqbqbpbpbpaqbrcrcuftelA1c3#e5%e5%e5%j6%< r���'���*���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� L)Ij=.}I7rD4qC3qC3qC3qD4pB2o>-uI9qb~||zxwvuts~s~r}q}q|p|p|p|p|p|p|p|p|p|p}q~r~stsux|revL:qC3rF6rF6rF6rG6xI8i7) +������������������ ��������������������������������� ��� ������������ ���'��$Q(uD3oA1oA0oA0m<,~_Qyur}p{mylxkwivhvhugugugugufuftfsfsfsfsdsdrdrdrcrcrcrcqcqcqbqbqbqbpbpbpbrbrcsdxjxXId4$e5%e5%e5%h7'[) 5���(���&��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� G)iqB2uF5qD3qD4qD4qD4rD5qB3p?0zSD~r~}{zxwvuts~s}r}r|p|p|p|p|p|p|p|p}r}rstuwy}m_tG7rC3sF6sF6sF6sG7xJ8c0##��������������� ��� ������������������������������������ ���������������%���". Ru?/qC3oA1oA1n>-sH7w{vs~q|nzlxkxjvhvhvhvhugugugugufufsfsfsfsesdrdrdrcrcrcrcqcqcqbqbqbqbpbpbqcqbsdwho_j?.c3#e6&e5%f6&k3$. _���%���(���"������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(#m<,uF5vG6qD4rD5rD5rE5rE5pA1qA1_Px~|zxwwvttts~r~r~r}r}r}rsttuvw{{eVtI9rF6sG7sG7sF6tG7yI7c.!��������������� ��� ������������������������������������ ��� ������������!���# .]0#uE3pB1pB1n@0n>.k\~zvs}r|nzmylxjwiwiwiwivhvhvhvhvgvgtgtgtgtgtetesesesdsdsdrdrdrcrcrcrcqcqcresdvgxiuP?d4#e6&e6&e5%l8'F ��+���*���$��������������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������; 5g<.xH7sE6rE5rE5rE5rE5qE5p@/tF6hZ~|zyxwuuttts~stttuvxx|{bSrD3rE5sG7sG7sG7uI9{J8Q#z����������������� ��� ��������������������������������������� ���������������%���"?lwE3pB1pB1oB1m=,xQB{yv~s}q|nymylwjwiwiwiwiwivhvhvhvhvgugugugugufufsfsesdsdsdrdrdrcrcrcrcrcrdsetezkjYi9'd5%e6&e6&g7&e0 " F���(���(��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������R,!YzD3uH7rF5rE5rF6rF6rF6qD4q@0wL<wk}zzxxwvutttttuvwx}z^OrC4rF6sG7sG7sG7uI8vG6>l ����������������� ��� ��������������������������������������� ��� ������������"���$7k8)rD4pB1pB1pA0pA0yk~zwt~q}p{nymxkwiwiwiwiwiwivhvhvhvhvgvgugugugufufsfsesdsdsdsdrdrdrcrcrcrcsftfwhxhqI7d3#e6&e6&e6&j6&=���'���*���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$ _4'nxG6xI8sG6rF6sF6sF6sF6rE5p@/|XHr~}{zyxwvvvvvwxy}v~[LrC4sF7sG8sG8sG8vI:yH61[������������������ ��� ��������������������������������������� ��� ���������������&���#M#xE4qC3pB2pB2n=,|YI|xus~p}o|mylxjxixixixiwiwiwhwhwhwhwgwgvgvgvgvgvftftetdtdtdtdsdsdscscscuetfvfzj~aQe4$f5%e6&e6&g8(a. 9���(���&��� ������������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������'!d;-uG7uH8sF6sF6sF6sG7sG7qC3sE4aQ~|{yxxxxxz{u{UErD5sG8sG8sG8sG8xJ;uB2.K������������������ ��� ������������������������������������������ ���������������$���#* Hr?.sE5qC3pB2o@0rD2r~zwtr~o}n|mzlxkxjxixixiwiwiwhwhwhwhwgwgvgvgvgvgvfvfufueudududsdsdscscudvevfyhuemC2d3#f6&f6&e6&l6&) l���%���)���"������������ ��� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������7 :xB1zJ8sG6sG7sG7sG7sG7sF6qB3tG8m`~}}||||}p{UErD4sG7sG8sG8sG8wJ:r=/*?������������������ ��� ������������������������������������������� ��� ������������!���&��(R, yG6qC3qC3pB2o?.dU~yvs~q}p|o{mylxlxkxkxkxkxkwiwiwiwiwiwhwhwhvhvhvhvgvgugufufufufsfsfsdteufvgwhzlyWGe3$e5%f6&f6&k9(Q% �,���*���%��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ N*TyE5zK:vI8sG7sG7sG8sG8sF7qA2xO@vi{mxP?sE5tH8tH8tH8tH8xK;n9+) 0������������������ ��������������������������������������������������� ���������������%��� 6`xD2qD4qC3qC3o?/wO>~|xus~p}o}n{lzlzlylykykykykxixixixixixiwhwhwhwhwhvgvgvgvfvfvfvfuftfuevfwgxg|lm]i<*e5$f7&f6&h8'f3#& T���'���(���!������������ ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� \7*ysF6xK:sG7sG8sG8sG8sG8rC5sC3~YH}tfwL<sF5tH8tH8tH8tI9yL;k5(' /��������������� ��� ��������������������������������������������������� ��� ������������"���$ ,f4&tF6qC3qC3qB2p?/rd|yvtr~p}o|m{l{lzlylykykykykyjxixixixixiwhwhwhwhwhvgvgvgvgvfvfvfvfufvfwgwgzj|muP?d3"f7&f7&f6&l8(H ���(���+���$��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$m<-}K:tH9sG8sG8sG8sG8tG7sE5sD4cTpbuH8rE5tH8tH8tH8tI9}M;a/#���������������� ��� ������������������������������������������������������� ���������������%���#EtxG5qD4qC3qC3o@0zVF|yvts}q}p|n{m{m{mzlylylylylykxjxjxjxjxjwiwiwiwiwhvhvhvhvhvgvgvgvgugvgwhxi|nj[f6%e6%f7&f7&h7'd/ @���(���'��� ������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>#DxG8xJ:tH9sG8tH8tH8tH8tG7rB1vJ:pcnauG7sG7tH8tH8tH8uJ:|L:H) ��������������� ��� �������������������������������������������������������� ��� ������������#���% 7k;,tG6qD4qD4pB2qB1|p|ywu~s}q|p{p{o{o{oznymymymymymxlxlxlxlxlxlwkwkwkwiwivivivivhvhvhvhviwjwjzmtfqH7e5$f7&f7&f7&l8'4v���&���)���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� K-#^}I9yJ;sH8tH8tH8tH8tH8sF6rC2{SCsm`tE5sF6tH8tH8tH8vI9}K9D'}������������������ ��� ������������������������������������������������������������ ������������ ���&���"F%{I8rD5qD4qD4o>-_P}zxvt}r}q{p{p{o{o{o{oymymymymymxlxlxlxlxlxlwkwkwkwiwivivivivhvhvhvhwiwjxk~q}^Nf4$e6%f7&f7&i:(]*6���&���&��������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������8`7*zM:vJ:tH8tH8tH8tH8sG7rD4rC3~YI~eWsC3tF6tH8tH8tH8uJ9{I8@!`������������������ ��� ������������������������������������������������������������� ���������������$��� * RuC2sF7rD5qD4o@0vK;||ywus~r}q|p|p|o|o|o|o{n{n{n{n{nzmylylylylylxkxkxkxjxjwjwjwjwjwhwiwjxjxk~psdmA0e5%g7'f7&g8'j5%+ a���%���)���"������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������21nA2|M;vI:tH8tH8tH8tH8tH8rC3vG7gYeWsD3tH7tH8tH8tH8vJ:{H7C!X������������������ ��� ������������������������������������������������������������� ��� ������������"���%+_/#wH8rD5rD5qC4o@/k]{xwus~s}r}q|p|p|p|p|p|p{o{o{o{o{oymymymymymymxlxlxlxkwkwkwkwkwiwjxkxlzmqzXJe3#g7'g7'f7&l:(R$��,���+���$��������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 3?xG6}L;uI9tH8tH8tH8tH8sF6rB2yQB|VGsD4sG7tH8tH8tH8xL;yE4:?������������������ ��� ���������������������������������������������������������������� ���������������&���!AgxH6rE5rE5rD5p@1yP@}yxvts~r}q}q}q|p|p|p|p|p{o{o{o{o{oymymymymymymxlxlxlxkxkwkwkwkwjxkxkyl~pqci9)f6&g7'g7'g8'i3#$ L���'���'��� ������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������' W0$b|K9yK;tH8tH8tH8tH8tH8rD4rD4tH8tH8tH8tH8xK;q>/84��� ������������ ��� ������������������������������������������������������������������� ��� ������������"���'0h9+vH8rE5rE5rD5sC3wj}|yxvss~q}q}q}q}p}p}p}p}p|o|o|o|o|o{m{m{m{m{mzmylylylykykxkxkxkyj{kzl|n|nuM=e4%g7(g7'g7'k9(C���(���)���$��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+[7*~N;wJ:tH8tH8tH8tH8tH8tH8tH8tH8zM<r=//!��������������� ��� ����������������������������������������������������������������������� ���������������&��#K"yJ8rF6rE5rE5o>/_P|{ywuss~q~q}q}q}p}p}p}p}p}p|o|o|o|o|n{m{m{m{mzmylylylykykxkxkykzl{k{mteUh7&f6'g7(g7'i9)f/ ?���&���&��� ������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ,l@1~L;vJ:tH8tH8tH8tH8tH8N<Z6*�������� ��� ��� ��� ������������������������������������������������������������������������� ��� ������������$���"$ Gr?/tH8rF6rF6pB2uH8y||zwwu~t~s~s}s}s}r}r}r}r}r}q|p|q|q|p|p{o{o{o{o{oznynynymymymxmyn{n{nsznrG7e5%g7(g7(h8(l8'5r���'���)���"������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������O)KtF6}M<tH8tH8sH8}M;[5* ������ ��� ��� ��������������������������������������������������������������������������������� ������������!���%��'^+wI9rF6rF6rE5p@/gZ}|zxw~u~t~t~t~t}t}s}s}s}s}s}s|q|q|q|p|p{p{p{p{o{ozoyoyoynymymynzo{o|qw`Rg2#g8(g8(g7(m:*V'��+���+���%��������������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$ M.#j|L:xJ:}N<[5)r ������ ���������������������������������������������������������������������������������������� ���������������%���"1 WvE4sG7rF6rF6qC3yM=~{zxwv~u~t~t~t~t}t}t}s}s}s}s}s|r|q|q|p|p|p{p{p{p{ozoyoyoynymymyo{o{ptugn>/h6'g8(g8(i8)h6'! W���(���(���!������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������A"!sD2W5(h�������������������������������������������������������������������������������������������������� ��� ������������!���'��)d4'zI9rF6rF6rF6qB1m`|{xwv~u~u~t~t~t~t~t}t}s}s}s}s}s|r|q|q|p|p|p{p{p{p{o{oyoyoyoymynzo{p}qtzUEg5%i8)g8(g8(m:*R#���)���*���$��������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������%���"CryH6tG7rF6rF6pB2}XI~{yxwvu~v~u~t~t~t~t}t}t}s}s}s}s}s|r|r|r|p|p{p{p{p{p{oyoyoyoyn|o{p|qvj^k:+h7(i8)h8(h:)g1"H���&���'��� ������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������"���" 7n;,vI9sF6sF6qE4sE4}s~|yxxwwwwuuu~u}u~u~t~t~t~t}s}s}s}s}q}q|q|q|q|q|pzpzpzp|q}p|q~u~swN?g5&i9*i8)i9)n;)=���%���*���#��������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������#�� P%yL9sF6sF6sF6o@/bT~|zyxwwwwwvuuuu~u~t~t~t~t}s}s}s}s}q}q}q|q|q|q|p{p{p|q|q}p~szfYj6&i8)i9*i8)m;+],7���)���&��������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������������! EvD3tH8sF6sF6rD4vJ:}|yxwwwwwwvuuuu~u~u~t~t~t}s}s}s}s}s}q}q|q|q|q|p|p{p|q}q}qu{oqF6h6'i9*i9*j9*l8)1h���&���)���"������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������!���`1%yJ9sG7sG7sF6pA0m`~~zyxwwwwwwwuuuu~u~u~t~t~t}s}s}s}s}s}q}q|q|q|q|p|p{p}q}s~sx\Mh6&j9*j9*i9*n;+W'��+���*���%��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ������������3WuH6uH8sG7sG7qC2{QB~}{yyxxxxxxxwvvvuuuuut~t~t~t~t}s}r}r}r}r}q}q|r~ssxuhn?/i8(j9*j9*j:+l6'! Q���'���(���"������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ���������"j7*yK:sG7sG7rF6qB1zn~}|yyyxxxxxxwwwwuuuuut~t~t~t~t~t}s}s}s}s}r}s}stuy{TFh7$j:*j9*j9*o<,I��'���)���$��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ��������H#mzK9sG7sG7sG7qA1`R~}|yyyxxxxxxxwwwwuuuuu~t~t~t~t~t}s}s}s}s}t}s~st|l_k:)i:)j:*j9*m<,d0"?���'���'��� ������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� ������ ,uA1tI9sG7sG7rE5uH7{~}|zyyxxxxxxxwwwwuuuuu~t~t~t~t~t~t}s}s}s~t~stxtvM=h8'j;*j:*k;+p:+?|���'���*���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� ������ ]3%yK:sG7sG7sG7pA1j]}|{yyxxxxxxxxwwwvuuuut~t~t~t~t~t}s}s~sttu{_Qj7'j;*j;*j;*n=,]+��/���*���&��������������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� ���#6sG6uH8sG7sG7rE5yN=~|{zyyxxxxxxxwwwvuuuuuttttt~s~sttuy{orE5j9)k;+j;*j;*n:)1 _���&���)���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������k9+yK:sG7sG7sG7n?.}r~}{{{{zzzyyyxxxwwwwwvvvvv~uvvww~ZLi7'k;+k;+j;*p>,Y%�-���)���%��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������B$P{K:sH8sG7sG7pA1]M~~}||{{zyyyyyxxxxxxwwwwvwxx|thn?/i9)k<,k;+m=,f5% K���&���(���!������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� xH7tH9sG7sG7rE5uH6~~}||{{yyyyyxxxxxxwwwwwwwzz{VFh8&k<,k<,k<,t=,?���$���+���#������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������O/%kxI:sG8sG8sG8rF6tI9fY}}||zyyyyyxxxxxwwwwwxy~k^m<,k<,k<,k<,o>.c2#9���+���&��� ������������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������8N=uI:sG8sG8sG8sG7rF6pA0sF4na~}||{zyyyxxxxxwwwxwx{xuJ:j:)k<,k<,k<,q?-<m���&���*���"������������ ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������I) U|H7vJ;sH9sG8sG7sG7sG7qE5pA0xL=zm}}|{zzzzxxxxxwxxxybTk9*l<,k<,k<,o?.[* 2���(���%��������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  X3(exI9wJ:sH8sG7sG7sG7sG7rF6p@/}UFw~}||{{yyyyyxyzy~{qrF5j:+l=.l=-l>-n9)( Y���$���(���"������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� l?/}J9uI9sG7sG7sG7sG7sF6pA1rD4aQ~~|{{zyyyyyz{}~}YJj9)l=.l=.l=-t?.E!���'���+���$��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������G%7tC3{K:tH7sG7sG7sG7sG7rE5qB2sE5ob~}{{{zzyyz|sgn=.k=-l=.l=.o?/g6'@���)���'��� ������������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� F)UyH6xJ9tH8sG7sG7sG7sG7sF6p@0yL<zm}|{{{zz{~~yPAk;,l>.l>.l=.r?.C|���'���*���#������������ ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������W3'q~I9wJ9sG7sG7sG7sG7sF6qB2rB2WH}~}||||||k_m<,l=.l>.l>.n?0b/! 4���(���&��������������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������>!e:,~L;tH8sG7sG7sF6sF6rE5qA1oA1bS~~~{vL=j;+m>/l>.l?/s=-3 d���$���(���"������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������6?sE4xJ9uH7sF6sF6sF6sF6rF6oA0uH7j^`Qm<,m>/m>/l>.sA0P(��,���*���$��������������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� @&V|H6{J9sF6sF6rF6rF6rF6pD4p@0yN>|p}pqB1k=-m?/m>/o@1n;*+ P���'���(��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.[4(~~K9uH8rF6rF6rF6rF6rE5pA1p@0~VH{~WIk<*m@/m?/m>/s@0K"���'���*���#��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,+e;-{J:tH8rF6rF6rE5rE5rE5o@0rA2dVxlo@/l?.m@/m?/oA1k6(=���&���%��������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� W+FqC3|K9sG7rE5rE5rE5rE5qC4o>-uI9qd{RAl<*m@/m@/m?/t@0Br���#���&���!������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! R/#^xF5yI8rE5rE5rE5rD5rD5o@0n>.zN=znpcn=,n?/n@0m@/rB1\/"0���$���#��������������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&R0${|J8uG7rD5rD5qD4qD4qC3o@0p>.XJwJ:m=-o@0n@/nB0s>-0 S��� ���%��������������� ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������G"0n>.{H8uF7qD4qD4qD4qC3pB2m<,sC3fXi[m<+o@0o@0n@0sC1U*�����$��������������� ��� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������J'Fl?0{I8qD4qD4qC3qC3qC3oA0n=,vH7m_o@/m?.o@0o@0pA1p>,! 5��������������������� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� B'jwF6tF5qD4qC3qC3qC3pB2n@/m<+|TCy]Nm<,oA0o@0o@0tC2>!z��������������������� ��� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0g7(xE5wF5pC3qC3qC3pB2pB1m=+qA0oA1o@0oA0o@0pB2h7'  ������������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������=+b9+}J8rD4pB2pB2pB1pB1n@/n@0oA0o@0oA1wA07S��� ��������������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 8 Nl?/wF5qC2pB1pB1oA1oA1oA0oA0uC3`2% ������������ ��� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ^/"esC1wF4oA1oA1oA0oA0pA1wB0:.��������� ��� ��� ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/"[4&tD4pB2oA0oA0sB2P, {���� ��� ��� ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������F%>v@/qB1oA0wC2 ������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [2%UyE3C&^��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������8���������������p���������������`�����������������������?���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���������������������?���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���������������������?�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���������������������?���������������������?���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���������������������?���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���������������������?�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���������������������?���������������������?����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?������������������������������������������?���������������������?���������������������������������������������������������������������������������� �������������������@�������������������@��������������������������������������@�������������������0���������������������������������������������������������������������������`������������������8�������������������������������������������������������������������� ������p���������������������������������������������������@�������������0������������� ����?������������������������������������?`������������������������������������������������?���������8�?������������������������������������������������������������������������?���������?�������������������������������������������������������������������������`����������������������������?������?0����������������������@����0���� ����������� ���������?���?@��?0�� �(���������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������?\(X'- ��I���H���N���K���D���<���4���,���$������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������Ac.^,c-M �]���W���V���O���G���>���5���-���%������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������D])^,]+]+`-a+F m���W���W���T���L���D���<���3���*���"������������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������*N"b-]+Z/!Z,\*]+a,Y&8 l���T���V���R���K���B���:���1���)���!������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������# gb-^+\-V/!T+U)Z)\*^*c,Q"# c���U���V���P���H���@���7���/���&������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������,U%`,]+Y/!U,S(Q&O%S%[([)]+`*L ���Z���U���U���N���F���>���5���,���#��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������8d/]+\/ W0!U*S(Q%N$N#O$U'Z([(_*](B ���S���U���T���L���C���:���/���&��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������<Z(`-],Z0"V.T)R&Q%O$N#N#M#Q%W'Z([(`*Z%/ o���V���V���O���F���;���0���#��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������� Jc/ ^,\1"X0"U+T)Q&P%O$N#N#N#N#N#Q%X'['\(`)W$) ���\���R���P���E���8���+��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� [c._-^. [2$V. T*S(Q&P$O$N$N#N#N#N#M#N#R%Y&['[(](J ���L���C���<���.��� ������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������%E c/^,]2#Z1#W,T)T'S'R%R%P%P$P$P$P#P#P#N#P#U&X'Z'])]'=j���9���-��� ������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������:g1 ^-^1"\4&X/!U+T(S'R&R&R%Q%P%P$P$P$P#P#P#O#M#Q$V&Y'Y'^)^(6���%��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������ Ia-a/^-]4&Z2#V- T*S(R'R'R'R&R&P&P%P%P%P$P$P$P$N#O#P%U'Y'Y'a), ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������>f1!`._2$\4&Y/!V,U*T(R'R'R'R'R&Q&P%P%P%P%P$P$P#O#O$P&U'Y'\)C���&��������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������( ef0!`.`/!^7([2#X. U+U)T(R(R(R'R'R'R'Q&P%P&P%P$P%P%P$P%R'Y'Z']( X������������������������������������������ ��� ��� ������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������#Q%e1!`-_4%\6'Y0!V-U+T)T(S(R(R'R'R'R'Q'P'P&P&P%P%P%P&P'W(Z'_)<���%��������� ������������������������������ ������������������������������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/ oe0 `/ `3#]8)Z3$X/!V,U*U*U*T)S)S(S(S(S(R(Q'Q'Q'Q&Q&Q&P'T(Z'](K 2��������� ����������������������������� ������������%���(���)���'���#������������ ��� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������:`-b0!`0!_:*]7(Z1#X/!W-U+U+U*U*T*T)T)T)T(S(R'R'R'R'R'R'S)X(['_)" k������������ �������������������������� ���������$���+���3���8���9���7���2���,���%������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������2f2!a/ `5%^:+[5&Y1"X/ V,U+U+U+U+U+T*T*T)T(S(R(R'R'R'R'R)U*Z'^)Q 4��������� ������������������������ ���������$���,��6��:���C���F���E���@���:���2���*���!������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������B`-c1 a2"`;,]7([3$Y0!W.U,U+U+U+U+U+T+T*T*T*T)S)R(R(R(S)S*Y)[(['R��������������������������������� ���������&���+;E1 ���P���L���R���O���I���A���9���0���'������������ ��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������E g3"a/b8(`;+]5%\2"Y/ W-V-V-V,V,V,U+U+U+U+U+U*T*S)S)T)T+W*Z(a+A���!��������� ��������������������� ���������'���* HG_)](K) ��X���S���U���O���G���?���6���-���&������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Ma/ b0!b3#b<-_8)\4%Z1"Z/ X-W-W-W,W,W,V+U+U+U+U+U*U)T)U)U*V+Z*\)Y% P���������������������������������������'���, KB`)Y%Y&](_(A���[���V���T���M���E���<���4���,���$������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������)X(d2"a0!b<-`<-]6'[2$Z0#Y/!Y- W- W. W.W,W,V,U+U+U+U+U+U+T*U*U,Z+\)a+, ������������ ������������������ ���������)���,GN`)Y&Y)Y*Y&Y&^(](>i���W���W���S���K���C���;���2���*���"������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������' mf2"a/ b5&b?/^9)\5%Z1#Z0"Y0 Y/ X. W. W. W- W- W-V,U,U+U+U+U+U+U,W-\*_+X% <��������� ��������������� ������������*���*UO ^(Y&Y)W. U-U*W'Y&Z'^)T#2 f���T���V���Q���J���A���9���0���(��� ������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������ 7Z)d1"a1"b>-`=,]7([4%Z1#Y0"Y0!Y0 Y/ W/ W/ W. W. W. W-V-U,U,U+U+U,V. [+]*_*( o������������ ��������������������� ���*���.cO ^*Y&Y)V/!U-S*S)R)T(X'Y'[(a)K �`���U���V���O���G���?���6���-���%������������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������!M!h3#a. b:+aA1^9*\6'Z3%Y1#Y0"Y0"Y0"Y0!X/!W/!W/ W. W. W. V- U-U,U,U-U.!Z-]*a,K ���&��������� ��������������������� ���+���+ l\'])Y'Y*V0"T- S*R)R(Q'P'Q'U'Y'Y'\)^(Dz���Y���U���U���N���E���=���4���+���#��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������! [g2"b0!b2#c@0`<-^7)\5&[2$Z1#Z1"Z0"Z0"Z0!Y0!X0!X0!X/ X/ X/ X. V. V-W- X/!X0!\,^,^* ]������������ ��������� ���������!���+���*' Y%[(Y'Y+W0!U- U*T)R)R'Q'Q'Q'Q(T(W(Y'Z'^)Z&?z���S���W���S���K���B���9���/���&��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������&H#h3#b0!c=.b@2^9,]6(\5&[2%Z1$Z2#Z1#Z0#Z1#Z1#Z0#Y0"Y0"Y0"Y0!Y0!X/!W. X/!Z1#\. ]+b-;������������ ��������� ���������!���+���3" |Y&[(Y'Y+X1$V.!U+T*S)R(R(R(R(R(R)R)R)U)X(Y'[(b*W$* k���U���V���O���F���;���0���$��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������5k5$b0!c5'cD4`=.^8*]6'[3%Z2$Z2$Z2$Z2#Z1#Z1#Z1#Z0#Y0#Y0"Y0"Y0"Y/"Y/!W0!Y0#Z0"]+_,]*G������������ ������ ���������"���,���." `)\(Z'Y-X2$V/!U,T+S*R)R)R)R)R)R)R)R)R)R*R*V*Y'Z'\)a+U## ���Z���R���Q���F���9���,��������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������,X*e2#b1"dA0dC2a<,_8)^6&\4%[3%[3%[3%[3$[3$[2$[2$\2#Z1#Z1#Z1#Z1#Z0"Z0"Y1#[3$]. ]+d-1 ���!��������� ��� ��� ���������#���+���-1 \(\(Z([/!Z3%X0"U.U,T+S+S*S*S*S*S*S*S*S*S*S*T+U+U+W+Y)Z(]+^)I ���J���E���>���0���!������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������<h3$b0!e<,fG6c@/`;+^8(]6&]5%]4%\4%\4%\4%\4$\3$\3$\3$\2#Z2#Z2#Z1#Z1#[1#[4$]1"^,c.G��'������������ ��� ���������$���+9/ ])[)Z([1"[4&X1"W. U-U,U+U+U+U+U+T+T+U+U+U+U+U+U+U+U+U,W,Y+[)])b,]):f���8���/���!������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������ Le1"c1"c5%fH7dC3a=._9*^7(]7&]5&]5&\5&\5&\5&\5&\4%\4%\4%\4$[3$Z3$Z2#Z3$[4%\5&^/ _,b- X������������������������&���,���15 d,]*[)\3$[6'Y2#W/ W. V-U,U,U,U,U,U+U+U+U+U,U,U,U,U,U,U,U,U.U. X. [+\*]+c.a+:���)��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������G!g5#b0!e?0eF6b@0`;,^8*]7(]7']7&]6&]6&\6&\6&\6&\6&\5&\5&\5&\4%[4%Z3$[4%]7'^3$^-d/?���%��� ������������������&���+��3?a+\*\+]4%\7'Y3$W0!W/ V. U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U-U. U. W. W0 Y1"Z1"],]+f/?��� ��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������+ hh3#b1!d9)gJ9cC2a>.`:+^8)]7(^7(^7(^7']7&\7&]7&]7&]7&]6&]6&]6&]5%\4%[5&\6'^6'`.a/W( 7��� ������������������'���, J>c,]+]+^6&]9)Z4$X1#X0!W/ V. V.V. V. V. V. V.V.V.V.V.V.V. V. V. V. V/V/V/ X/ X0 Y0!Z2#[3#],`,U$;��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �������%S(f4$b2"gE4fH7b@0a>-`:+_8)^7(^7(^7(^7(^7'^7']7']7']7']7']6&]6&]6&]6&]7']8)_3$`-h1!7������ ���������������(���,?M!e.]*],^8)]9)[5&Z2$Z1"Y0!X0 W0 W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ W/ X0 Y0!Y0!Y0"Z1#[3$]-]+c-$ q������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������5~g3$b1"d<,gK9dD3a?.`<,_:+^8)^8)^8)^8)^8)^8)^7(]7(]7(]7(]7(]7']7']7']7']8)^6'`/ d0 M"1���"������������ ���)���,SN"b-]*^/ _9)^:*\6'[3$Z2#Y1"Y0"Y0"Y0"Y0!Y0!X0!X0!X0!X0!X0!X0!W0!W0!X0!X0!X0!X0!X0!Y0!Y0!Y0"Y0"Y1"Z4%\1"]+b-P"/��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������8a/!d3$c4$hK9fI7cB1a>-`<+_;*_9*_9)_9)_9)_9)_9)_9)^7(^8(^8(^8(^8(^8']8'_9*_;+`2#`. f1!$ n��� ���#��������� ���)���-?M e/ ]*],`;*_<,]7(\6&[4$Z2#Z1#Z1#Z1"Z1"Z1"Z1"Z1"Z1"Z0"Z0"Z1"Y1!Y1!Y1!Y1!Z1"Z1"Z0"Z0"Z1"Z1"Z1"Z2"[4$\6&]-^+`+ X��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������5h5%b0!e>.hM<eE5b@0a=-a;,`:+`:+`:+`9*`9*`9*`9*`9*_9*^8)^8)^8)^8)^8)_8)a<,a9)a0 e1"S$3���&���"���!���$���+���+IQ$d/ ^+_2#a<,`=-]8)\7'\5&[3%Z3$Z3$Z2$Z2$Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z2#Z1#Z1#Z1#Z1#Z1#Z1#Z2#Z2#Z2#Z2#Z2#[3$\6']1"]+c-6��� ��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������ Ja/ c2#d7'hK;fI9dC3a?/a<-`;,`:,`:,`;,`:+`9+`9+`9+`9+`:+_9+^8*^8*^8*_:*`;+a;,a2"b0 a. W���%���'���$���'���,���,QT$d0 ^,`1"b>/`?/^9*]7(\6&\5&\3&\3%[4%[3%[3%Z3%Z3%Z3%Z3$Z2$Z2$Z3$Z3$Z2$Z2$Z2$Z2$Z2$Z2$Z3$Z3$Z2$Z2$[3%\5']6']-_,\)E��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������G"i5%b1"gF5hN>dE5bA1a>/`<-`;,`;,`;,`;,`;,`;,`:+`:+`:+`:+`:+_:+^9*^9*_;+`>.a5&a/h3"D���(���,���)���)���0���,U[(e1 _,`2#cB2a?/_:+]8)]7(\6&\5&\5&\5&\4&\4&\4&\4&\4&[4&[4&Z4%Z4%Z4%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z4%Z4%[6&]7(]0!^+d.2 }������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! Wd1"b1"d8)iN=fI8cC2a@0a>.`<,`<,`<,`<,`<,`<,`<,`<,`;+`;+`;+`;+`;+^:*^;+`>.a=-a1"c1!]+P���-���.���.���2���- aZ)e0 `,b6&bB1a@0_<,]9*\8)\7'\7&\6&\6&\6&\6&\6&\6&\6&\6&\6&\5&\5&\5&\5&\5&[5&[5&[5&[5&[5&[5&[5&[5&\5&\6&\8)]6'^,b.O#��+��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������7\*e4$b3$iK:iP=eG5cC1b@/a>.a>-a>-a>-a>-a>-a>-a>,a>,a=,a=,a=,a=,a=,`=+`>-b@/a5%a/ h2#, ���)���4���2���5���1  s`,d1"`.b6&dE3bC1a=,a;*_:*]9)]8(]8(]8']7']7']7']7']7']7']7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]7&]8(_;*^1"^,d-' s������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������@j6&a0!e=-jR@gJ8dF4cB0a?.a>-a>-a>-a>-a>-a>-a>-a>-a>-a>,a>,a=,a=,a=,a>-a@/b<+a/ d2"]+ G���5���7���7���3' xe0 c1!`.b8(eG5cC1a?.a=+_;*^9*^9)^9(^9(^8(]8(]8(]8(]8(]8(]8']8']7']7']7']7']7']7']7']7']7']7&]7&]7&]7&]7&^9(_;*_4%^,d/ @��� ��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������?b/ c2#c5%jO=hO=eG6cD3bA/a@/a?/a?/a?/a?/a?/a?/a?/a?/a?/a?.a>.a>.a>.a>.bA/cA0b4%b0!c/  m���7���<���;���6/ d1 c1!a/c;+eG6cD3a@/`>._<,^;*^:*^:*^:*^:*^:*^9*^9*^9*^9*^9*^9*]9*]9*]9*]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]8)]9)^;+`=-_/!_-a-P��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������$I"k5%b0!hF5kVCgK9eG5dC1cA/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/b?/c@/dC1c9)b/ g3#=���7���A���?���;3 g1!b1!a0 d<+fJ8eF4cA0a?.a>-`<,`<,`<+`<+`<+`<*`;+`;+`;+`;+`;+`;+`;*`;*`;*_:*_:*_:*_:*^:*^:*^:*^:*^:*^:*^:*^:)_;+a>-`6&^-c/=���#��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������" ^j4$d1"d5&kS@iP=fI7eE4cC1bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/bA/b@/b@/cC1eC2c3$d1"a. ^���B���D���A:f2"b1"a/ eB0gM:fG5dC1bA0a>.a>,a=,a=,a=,`=,`=,`=,`=,`<+`<+`<+`<+`<+`<+`<+`<*`<*`;*`;*`;*`;*`;*`;*_;*_;*_;*_;*_<+`?.a:*_-b/W'0��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������(R&g4$d2#iL;jTBgK:eG6dD4cB1bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0cB1dC3c8(b1"h2#2 ���G���I��E9g2#b0!a0!fC3gK:fF6dD3bA0a@/a>/a>.a>.a>.a>.a>.a=-`=-`=-`=-`=-`=-`=-`=,`=,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<,`<+`<+`<+a>.b@/a2#`-g1!2������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������6l6&d0"hC2lYHhP>fJ8eF6dD3dC3dC3cC2cC2cC2cC2bB2bB2bB2bB2bB2bB2bB2bB2bB2dE4d@/b0"e3#T'�Q���L���J>j2#a0!a2#eB1gM<eG7dD4bB2bA0a@/a?/a?/a?/a?/a?/a?/a?/a?/a?/a>/a>/a>/a>/`>/`>/`>.`>.`>.`>.`=.`=.`=.`=.`=.`=.`=.`=.a>.bA1b:*`.d0 L"��)��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������/],f3#d3#lSAkUChL:fH7eF5dD3dD3dD3dD3dD3dD3dD3dD3dC3dC2dC2dC2dC2dC2dC2dD3eE4c7(b0!i2#, ���K�WCh3$b0!a0!gE4hN<fH7eE5dD3cB2bA0bA/bA/bA/bA/b@/b@/b@/b@/b@/b@/b@/b?/b?/b?/a?/a?/a?/a?/a?/a?/a?/a?/a>/a>/a>.a>.a>.a>.bA0cA0`0!a/ e1! ^��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��������� Fi4$d1"iH7lWEiP>gK9fG6eE4dD3eD3eD3eD3eD3eD3eD3eD3dD3dD3dD3dD3dC2dC2dC2dD3d=,b1!h4$A���Q]O$i4$b0!b4$fG6gL:fI7dF5dC2cB1bA0bA0bA0bA0bA0bA0bA0bA0bA/bA/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/a?/a?/a?/a?/a?/a?/a?/a@/dE3c:)`.g2"D���'��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������ Ph2#e1"f9)mZIjTAhN<fJ9fG6eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4eE4dE4dD3dD3dE3c5&c2#d1!w gP$h4$b0!c5%fF4gM<fI8eG6dE4dC2dB1dB1cB1cB1bB1bB1bB1bB1bB1bB1bB1bB0bB0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0bA0b@/b@/b@/bA/b@/bA/bD2c@0a0!d1!X(;��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������E j5%d0!jJ:lYGjP?gL:fJ8fG6fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5fF5eF5eG6d<,c1"i5%7 ��gT&i6%b1!c3$hH7iM<gJ8gH6fF5eE4eD3eD3eD3eD3eD3eC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dC2dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dB1dC2eG6c7&a/g2"% p������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0 wk4$d0!hC2o^LlUCjO>gL:fI8fG7fG7fG7fG7fG7fG7fG7fG7fG7fG7fG7fG7fG7fG7fG7fH8fD3d2$g3$R'~V)h5%b0!d:*gJ8hM;gK9fJ8fH7fF5eE4eE5eE5eE5eE5eE5eE5eE5eE5eE5eE5eE5eE4eE4dD4dD4dD4dD4dD4dD4dD4dD4dD4dD3dD3dD3dC3dC3dC3dC3dD4fH7eB1a0 f2"T&/��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ �������)U)h5$d2#mUDm\KjR@hN<gK9fI7fH7fI7fI7fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fJ8e>-d2"e2#FW(i5%c2#d8)fJ9gN<fK9fJ8fI8eH7eG6eG6eG6eG6eG6eG6eG6eG5eG5eG5eG5eG5eG5eG5eG5eG5eG5dF5dF5dF5dF5dF4dF4dF4dF4dF4dF4dF4dF4dE4dE4eG6fH7b5%b0!b/  S������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������7~k6%c0 iD4n_NlVDjQ>hM:fJ8fI8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fK9e;+d2"`-`.f5$d0!e9)fL:fL:fK9fJ8eI8eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH7eH6eH6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG6eG5dG5dF5dF5dF5dF5dF5dG6fL:dA0a. h3#E ��� ��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������ Kd0!e3#e7&o\Jo]JkTAjO=iL9hK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gM9f@.d1!d3#e3#d2"e9)hI6iM:hK9gK8gK8fJ8fI8fJ8fJ8fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fH6fH6fH6fH6fH6fH6fH6eH6eH6eH6eG6eH6fJ8gJ8c6&c0!c. X��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������M#j6%c/jH8qcQmXFjR@iN<hL:gL9gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gL:gM;fH6d6%d2#d3#e;+gL9gM;gL9gL9gK9gK9gK9gK9fK9fK9fK9fK9fK9fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI8fI8fI8fI8fI8fI8fI8fI8fI8fI8fJ8hN<e>-b/ i4$9������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������" Xf3#d2"g<,q`Oo]KlTCjP?iN<hM:hL:hL;hL:hL:hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hM;hN<hJ8gE4gF5hL:hM;hL:hL:hL:hL:hL:hL:hK9hK9hK9hK9hK9hK9hL:hL:hL:hK9hK9hK9gK9gK9fK9fK9fK9gJ8gJ8gJ8gJ9gK9gK9gJ9gJ8gJ8gJ8gJ8gI8fI8gJ8iN;iL:c3$d2#`.@��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������/\+g5%c1!nUCrdRmXFlSAjP>iN<iN;iN;iN;iN<iN<iO<iO<iO<iO<jO<jO<jO<jO<jO<jO<jO<jO<jO<jO<iO<iO=iP=iO=iN<iN<iN;iN;iN;iN;iN;iM;iM;iM;iM;iM;iM:iM:iM:iM:iM:iM:iM:iM:iM:iM:hM:hM:gM:gL:gL:gL9gL9gL9gL9gL9gL9gL9gL9gL9gK8hK8iM;jO=e<+b0!f2#- x������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������+ rk6%d1!g<,rcRo]LlVEkRAjP?iN=iN=iN=iO=iO>iO>iO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>iO>iO>iO>iO>iO=iO=iN=iN=iN=iN=iN=iN=iN=iN=iN=iN<iN<iM<iM<iM<iM<iM<iM<iM<iM<hM<gM<gM<gM;gL;gL;gL;gL;gL;gL;gL;gL;hM;jQ@iH8b1!f4#Q'���(��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������;^,f3#e6&p^MqbQmYGkTCjQ?iO=iN=iO>iO>iP>iO>jO>jO>jO>jO>jP>jP>jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP>jP>jO>jO>jO>jO>iO>iP>iP>iO>iO>iO>iO=iO=iO=iN=iN=iN=iN=iN=iN=iN=iN=iN<iM<iM<iM<iM<iM<iM<hM<gM<gM<gM;gL;gL;gL;gL;gL;hM;iP>jR@e:*b1"g3#& f������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Al7&c0 mQ?sjXo]KlWEkSAjP>iP>iP>jQ?jQ?jQ>kQ>kQ?kQ?kQ?kQ?kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kQ?kQ?kQ?kQ?kQ?kP?jP>jQ>jQ>jQ?jQ?iP>iP>iP>jP>jP>jP>jO=jP=jP>jP>jP>jO=jO=jO=jO<jO<jO<jO<jO=iO=hO=hN<hN<hN<hN;hN;iO>kSAfB1a0!h5%B��� ��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������>d1!e2"e7'rdRqcQnZIlVDkSAjR@jQ?jQ?kQ?kQ?kQ?kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kQ?kQ?kQ?kQ?kQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jP>jP>jP>jP>jP>jP>jP>jO=jO=jO=jO=jO=jO=iO=iO=iO=iN<iO=jSAjN<c2#d2#e2# L��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������'R&i5$d3#o[HrhUo_LmYFkUBjS@jS@kS@kS@kS@kTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkTAkS@kS@kS@kS@kS@jS@jS@jS@jS@jS@jR?jR?jR?jR?jR?jR?jR?jQ>jQ>jQ>jQ>jQ>jQ>iQ>iQ>iQ>jR?kVCg@/d1"j4$;���"��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������# cj5%d1!hC2tn\qcPn\JlWElUCkSAkSAlTBlTBlUBlUBlTBkTBkTBkTBlUBlUClUClUClUClUCmVCmVCmVCmVCmVCmVCmVClUClUClUClUClUClUClUBkTBkTBkTBlTBlTBlUBlUBlTBlTBlTBlTAlSAkSAkSAjSAjTAkTAkSAkS@kR@kR@kR@kR@kR@kQ?kQ?kQ?kQ?kQ?kQ?iQ>jR?lVCiJ8d1"g3$X*.��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������)Y)g5%d3#p]KsjXp_Mm[ImWEmUClTBlUClVClVClVClUClVClVClVCmVDmVDmWDmWDmWDmVDmVDmWDmWDmWDmWDmWDmWDmWDmWDmVDmWDmWDmWDmWDmVDmVDlVClVClUClUClVClVClVClVClVClUClUClUBlTBlUBlUBkUBkUBkTBkTAkTAkTAkTAkTAkS@kS@kS@kS@kS@kR@kR@kTBlWEf;*d1"m5%2������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������:k7&d1!mP?uq^rdQp^KnYGmXDlVDlWDlWDlWDlWDlWDmWDmXEmXEmWEmXEmXEmXEmXEmXEmXEmYFmYFmYFmYFmYFmYFmYFmYFmYFmXEmXEmXEmXEmXEmXEmXEmXEmXEmWElWDlVDlWDlWDlWDlWDlWDlWDlWDlVClVClVClVCkVCkVCkVCkUBkUBkUBkUBkUBkTAkTAkTAkTAkTAlUBlYFiH6d1"j4$F ���(��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������6_.g4$f7'sgUtkXq`Nn\ImYFlXDlWDlWDlWDlXElXEmWEmXEmXEmXEmYFmYFmYFmYFmYFmYFmYFmYFmYFnYFnYFnYFnYFnYFmYFmYFmYFmYFmYFmYFmYFmYFmXEmXEmXEmXEmXElXElXElWDlWDlWDlWDlWDlWDlWDlWDlVClVCkVCkVCkVCkVCkUBkUBkUBkUBkTAkTAkTAkTAlTBlXElUBe4$f2#`- <��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ���������F!k8'd2"pWEvr_reSp_Mo[JmXGlWFlWFnXFnXGnXGnYGnYGnYGnYHnYInZInZIoZIoZIoYIoYIpYIpYIpYIpYIpYIpYIpYIpYIpYIpYIpYIoYIoYIoZInZInYInYInYHoYHoYIoYIoYInYInXFlWFlWFmWFmWFmWFmWFmXFmVEmVEmVEkVElVElVDlUDlUDlUDlUDlUDlTClTClUClVDm\KiF6d0!l6&<������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Vj6%e3#iB1wsatkYqbPp^LnZImYHnYHoYHoZHoZHoZIoZIoZIoZIp[Jp[Jp[Jp[JpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJpZJp[Jp[JpZIp[Iq[Jq\Kp^Mq_Nq`Nq`Np]Lp\Kp[JoZImYFmXFmYGmYGmYGmYGmYGmWEmXFmXFlXElWDlWElWElWElWElWElVDlUCmVDm[IkP?d4$g5$W( 5��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������#H#k8'e3#q\Jvq_reTq`Np]Lo[IoZIoZIo[Jo[Jp[Jp[Jp[Jp[Jp[Kp[Kp[Kp[Kp[Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp\Kp[Kp[Kp\Kq]Lq_MraOq^LnSAkL:lN<oZIq_Mp]Kp\KoZInZHmYGmYGmYGmYGmYGmYGmXFmXFmXFlXFlWElWElWElWElWElVDlVDmYGn\Jg>.d1!j5$& k������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������9n9(d3#mN=zwevkYsdRq`Np]Kp\Jp\Jp]Kp]Kq]Kq]Kq]Kq]Lq]Lq]Lq]Lq^Lq^Lq^Lq^Lq^Lq^Lr^Mr^Mr^Mr^Mr^Mr^Mr^Mr^Mq^Lq^Lq^Lq^Lq^Lq^Lr_Nr`OscRsbQkB2g5%f4$f4#h>-q^Lq`Nq^Lp\Jp[JoZIoZIoZIoZIoZIoZIoYHoYGnYGmYGmXFmXFmXFmXFmWEmWEnXFp]LlQ?d2"i5%Y(+��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������ Ge4$g7&f5$wiWxs`thVscPq`Mp^Kq^Kq_Kq^Lq^Lq^Lq_Lq_Lq_Lr_Mr_Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr`Mr_Mr`MsaOsbPthUr`Mj@0g5&h7(g7'g7'g6&oWFrdQq`Lp^Kp]Jp]Jp]Jo\Io\Io\Io\Io[HoZGnZGmZGmYFmYFmYFmYFmYFnYFo\Io[Hf9(e3"c1! M������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������;m:)e4#nN=zyfvm[sgUrcQqaNq`Mq`Mq`Nq`NqaNqaNqaNraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOraOsbPsdSuiWtdRkB2f5%k:*c2$_.!h8(g7'p[JreSqbOp`Mp_Lp_Lp_Lo^Ko^Ko^Ko^Ko]Ko\Jo\Jm\Jm[Im[Im[Im[HmZGn\IqbOlO=c1!k6%L#���"��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������" Th5%g6&i>-yubxtaujXtfTrcPqaOqaOraOraOraOraOraOraPraPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPsbPscRseTukZsaOk?/i6'p<,M'2 e3%g6'i?/sfTseSqcPp`Mp_Lp_Lp_Lo^Ko^Ko^Ko^Ko^Ko]Jo]Jm]Jm[Im[Im[Im[Im[Ip`Mo[If9(e3#c0! N���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������N'm:*f2"qVE|{iwo\tiWtfTrcQrbPsbPsbQsbQsbQsbQsbQscQscQscRscRscRscRsdRtdRtdRtdRtdRtdRtdRscRscRscRscRscRscRscRscQsdRteTuhVwn[taPk=.i7(n:+R*[Q'k:*g7'r\JvlZsfTrcQqaOq`Nq`Nq`Np_Mp_Mp_Mp_Mp_Mp^Lp^Lp^Lo]Ko]Ko]Ko]Jp^LqaNhA1c0 j6%5������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������$ Vk8(g6&kD4|zhyubulZthVteSsdQscQscRsdRsdRsdRsdRsdRsdRtdRtdRteRteRteRteRteRteRteRteRteRteRteRteRteRteRtdRtdRsdRteTtgUuiWwp^s^Lj;+i8)q=-Q(���3;r<,g5%lG6wq]ukXtgTsdQqbPqaNqaNqaNqaNp`Mp`Mp`Mp`Mp`Mp_Lp_Lo_Lo^Ko^Ko_LqdPnUCc2"f4$b0! <��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ������ 2`1#k9*j;,zra}{iyp^vkYuhVteTtdTteTteTtfTtfTtfTtfTufTufUufUufUufUufUufUufUufUufUufUufUufUufUufUufUufUufUufTufTugVuhWwkZxq^rXHj;+k9)s=-C ��9Fc2%k8)i:*vfUxq^vjXugUsdRrcPrbPrbOrbOrbOqaNqaNqaNqaNqaNq`Mq`Mp`Mp_Lp`LqbOrcQh>.d2"g3#) j������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0yo<,i5&qQA~o{udxm\vjXuhVufUufUugUugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVugVvgVvhWviYxm[zq`tZIi8(k:)l:+?!�1���0H$o<,h5&rVF{xgwn\uiXtfUtdRrcQrcQrbPrbPrbPqaOqaOqaOqaOqaOq`Nq`Np_Mp_MqaOseRlM<c0 h6%Q'���&��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������=h5'l9*k>-|ve|ziyr`wl[viYuhVuhVuhVuhVuhWuhWuhWuhWviWviWviWviWviWviWviWviWviWviWviWviWviWviWuhWuhWuhWuhVuhWuiXvkZyp_zq`pM<i9(k<+q>,;���!���(  bn:+i8)j:+yr_yubvl[tiWtfUsdQrcQrcQrcQrcPrcPqbOqbOqbOqbOqbOqaNqaNp`Np`NrdQseSf9(d2"j4$' a��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������G%q>,i8'vcRq{tcyp^vl[ujXuiWuiWuiXuiXviXviXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXvjXviXviXvjYvl[yraxlZpK:j9)n=,o;*/j���!���+��/S)o;+i6'uaO|{hwq_ukZthWsfUseRseRrdRrdRrdQrdQqcPqcPqcPqcPqbPqbOpbOpaNqcQrfTkH7d1!j5%;���#��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ Jj9)l:*mD3~|l}{kzrbxn_wl\ujZvjYvjYvjZvjZvj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vj[vjZvjYvj[wl\xm]zscyn]nH7j8(p>-h8)+ ^������'���&6r=,i6'qRB~ozvevo^ujYthWseVseUseUrdUrdUrdTrdTqcRqcRqcRqcRqbQqbQpbQpbPrgVo\Kd2"g4$X+.���!������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������&`/"p>.k9){m]r|vezq`zn^xl\wk[wk[wk[wk[wk[xk\xk\xk\yl\yl\yl\yl\yl\yl\yl\yl\yl\yl\xk\xk\wk[wk[wk[wk[xk\yn]zo^|ueyl\mB2j:*p?.h7' J������#���%1`1$m;+j8){ra~|kyravn\ujYthWtgVtgUtfUseUseUseTseTrdRrdRrdRrdRrcQrcQrcQrdStjYjG6c0 m6%2������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������*hq>-j8(rO?u~{j|td{p_zn^yl]yl\yl]yl]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zm]zl]yl]yl]ym]zo^{qa}wixeTnD3k;*sA0[.",������ ���#���#D!p=+i7'uZJp|vfzq`wm[ujXuhWuhWuhVugVtfVtfVtfUtfUseTseTseTseSsdRrdRreStiXnTBd1!j5$G"���'��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������ /`0"o>.l=-}qbr~wg|sc{p_{n^{m^{n^{n]{n^{o^{o^zo^zo^zo^zo^zo^zo^zo^zo^zo^zo^zo^{o^{o^{o]{o]{n]{n^{o_{p`|sb~wgyfVmA1l;+s@0U*(������������ Nn<*j:(nE4p{k|scyp_wl[vjWuiWuiXviWviWuhWuhWuhVuhVtfUtfUtfUteTtdSseSthVujXg?.d1!i4$ Z������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������K'sA1j9*yaRy{k~ue|rb|p_{o^{o^{o_{o_{o_{o_{o_{o_|p_|p_|p_|p_|p_|p_|p_|p_{o_{o_{o_{o_{o_{o^{o^{q`|ra~te|kxdTl;,l=-s@1K(����������������I#q>-i5$waPt}vf{qazn^xkZvjXviXviXviWviWuhWuhWuhVuhVtgUtgUtgUtfTteTtgVvlZnR@c2"j6%D ���$��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������" Vq@.l=-nA1~osxi~te}rc|pa|pa|pa|pa|pb|pb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qb}qa}qa|pa|pa|pa}pb~rcxhxjuWGm?0m>.uB0D#������������������-oq=-j8(sQAs{l}td{o`{m]yk[xkZxjZxjYwjXvjXviXviXviWviWuhVuhVuhVugVugVwkZtcRe6&f4$^- :��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���������#N)sA1l;+}gXy|mwhtercqbqcrcrcqcqcrcrcrcrcrcrcrcrcrcrcrcqcqcrcrc~rbscufyjvhvWHl;+o@/rA0>w�������������������'Z/"o?.j8)scuxg~sb|o_{m]{k\zk[zk[zkYykYykYyjYyjYyjXyjXxiWxiWxhWxhWxjXzp^nN=c1!k6%:������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������8|wB2l<,sN?{rzkvgtdrcrdrdrcscscscsdsdsdsdsdsdsdsdsdscscscscrcrdtevfzk{muO?l<+pB1qA/4^��� ������������������3ts?.j8(uTDw|lve~r`}o^|l]|l\{l[{l[{kZ{kZzkZzjZzjYzjXzjXyiWyiWyiWzjX{n]t]Kd3#g5%Y)-��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������$a4'pA0l<*p`{}oyjvgtetdtdudueueufufufufufufufufufufufufufufueuevgwiouftM>n?.uD2m;,$T��� ������ ��� ��������� Ah9*m<,nC3qsyitd~q`}o^}n]}n]|n\|m\|l\|l[{l[{k[{kZ{kY{kYzjXzjXzjX{l[|q_jC2c1!j6&$ f������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������C!sC1l=,{`Pt|mxivgufvfvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvgvhwixj}psdtN>n>.uE3g8) 5��� ������ ��� ��� ���������?!s@0j9(z_Oz|mvgrc~p`~o^~o^~o^}o]}n]}m]}m\|m\|k\|k\|k[|kZ{kY{kY|l[~q`u\Ke4$i6%R'+��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���  8p@/o?.sI8~r|mxjwhvgvgvgwgwgwgwgwhwhwhwhwhwhwhwhwhwgwgwgwhxi{krufrF5o?.xG4b2%$��� ������ ������ ���������% Rp=-l<,qI:vryivdrbq`p^p^o^~o^~o]~n]~n\}n\}l\}l\}l[}lZ|kY|lZ~q_|hUg6%e3#d2"B����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���B$uC2m:*hY~u}pzlylwjwiwixixjxkxkxkxkxkxkxkxkxkxkxkxkxkxkyl{nusesI8qA1vE4_5( 3��� ������ ��������� ������*`0#qA1k9)n_~~oxiufrcqbqbqapapap_o_o^~o^~n^~n^~n]~m\}l\~o^vepM<c0 l7&G"���$��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������.;uC1n?.wTDyt~p{nzlykxkxkxkxkxkxkxkxkxkxkxkxkxkxkxkyl{m}oxparF4pA1zH7\3&��������� ������������������*lsB1l<-rH8xu{lwhtercqbqbqapapap`p_o^o^~o^~n^~n]~n]}n]ra}kYg8(f4$e1!F������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������]4'tD3m<,}ozus~p}o|nzmylzlzlzlzmzmzmzmzlzlzlzl{m|oswhYsG7qC3zJ8N) ��������������������� ������4e5'oA0n?/|m|pyjvhtesdrcrcrcrbqbqbqap`p_p_o_o^o^q`sbmE4d3#k7&4������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ oA1uE3pA0rE5oa}yvt~r}p|o|n|n{n{n{n{n{n{n{n{o|p}ptybSq@0sD5tE6J( ��� ��� ������������������������D$xE3k<+yXGt|nxjvgseseserdrdrcqcqcqbpap`p`o`o_p`ue{`Pd1!h7&_/ :��������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������!&i;*wE5tC3o=-rD3oa~zwus~s}r|p|p|p|p|p|p|p}r~swz|[MqC3sF6zJ7A$��� ������ ������������������ ������7l<,pA0p@0s~s{nxjvhugugufsfsfsdrdrcrcqcqbqbpbpbsdtelA1e3#k5%( b����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� *If;-|I7rC3q@0yRCzn~|xvus}q|q|q|p|q}rsyt|ZKrC3vH7zI75g��������� ������������������ �������%X.!tC3n>.pav}pylwivhugugugtfsfserdrcrcqcqbqbpbqcwiuUEc2"k8'@���!��� ������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� H* |rC3yH7qB2p@0`Q|xwut~sssv{tzUFrC2wJ9k>/7]�� ������ ��������������������� ������&\vE3o?-xRC|t|oylwjwiwivhvhvgugugtfsesdrdrcrcrdvhrci=,f5%d2# I�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  S0$wI7vH8qB2rD4j\~{ywvvwrwN>rD5xJ:nB2 6��� ������ ��������������������� ��������%`2%sD4o>-ugwq|nxlwixiwiwhwhwgvgvguftetdsdrcsctfyjsN>d2#l9(7������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� -\5(}K:wH8qB1xO@qd}||xjxP@rE5|L<c8++��������� �������������������������������>!yG6o?/bS{t~p|nylxkxjxjwiwiwhwhvhvgufuetesdtezkhYf5$i8'V,.��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������:!Ob</zJ:sE7p?0zRCwylwM>sE5{L;_4( '��� ������ �������������������������� ������ 2h:,sD3rD4xws}p|mzlylykyjxixixiwhwhvgvgvfvfufwh{krL<d4#m8'0z������������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������L,"kxH9zK;tF6rC2~YJnbsD4tG7|M<R0%���� ��� ����������������������������� ���������K'xH7o>.i[}ws|p{o{oymymymxlxlxlwkwjwivivhvhwi|n|\Md3!l:(G#���"��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ R0$|yI9yK:rD4vI9gZk^tF6uH7~M;I* ��������� ���������������������������������������� PvE4qB2xQBzvs|q|p|p{oznznymymymxlxkxjwjwiwjzm{mnB2f5%j8& S��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-`9,zJ:vI9qA0{TEhZrC3uH8zK:G&z�� ������ ���������������������������������� ��������� P,!xH8p?0qd~yur}q}p}p}p|o|o{nzmzmymylykxkxjyl~q{ZLf4%k9(G!���$��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,<oC4}M<tG7rC3vJ9wI8?T������ ��� �������������������������������������������>wzJ8pA1_P{wtr}q}q}p}p}p|o|n{m{mzmylykxkylphXf5%i8(`0"3��������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  9![sF6O=tG6 4����������������������������������������������� ������ @qB2sG6qB2y{wu~t~t}t}s}s}r|q|p{p{pzoyoymym|ptsI:e5%o;*8������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  V4(!)������������������������������������������������ ���������D%zJ8pC3aS}yw~u~t~t}t}t}s}r|q|p|p{pzoyoynzoufXi8'j:)U*,��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������( RwG5sE5wM=|ywwvu~u~u~t~t}s}r|q|q|qzp{p~synl=.h7(n:*# a���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������\4(yJ:p@/m`~zxwwvuu~u~t~s}s}r}q|q|p|p|qy^Qg5'p;,M&��(��������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� GvG6rE4zP@}zxwxxvvuu~u~t~t}r}r}q}quxlm>.k9)h5& D������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ j<.vI8tG7}zxxxxxwvuu~t~t~t}s}s~tzzUFh6%q<,6������������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���0`zK:pA0cT~{yxxxxwvuutt~t}s~swujm>-l<*c3$:�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� pC3tG7vJ:}|zzyxxxwwwvu~uwxtJ:j9(o;++ b������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������P/${K;qB2}r~}|zyyyxxwwwm`i8(q?.S+���%��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� sG8yJ;rF6zPA{o~{yyyxxwwzvuJ;j:*m:*% _�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������-4nC3|L;sF6pA1vK;|p~}|{yyyy]Pi7's@/?������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  8 UrD5|L;rE5qC3~ZKz~|zzytpD3m=-l:*D��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ O/$vG7zJ:qC3rA1j\}|}}XIj;*q?/:������������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� U3({K9wH8pA0rD4qei\k9)p@0[1$��)��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������)6a9+{K:tG6p@0zQBwkuL;l=-t@01l������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������B'_mA2xH8qB2n<,YIj\l<+rB1P)������������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������N-#|yH7xH8pA1o?.]Op@0n?/p@. J��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  'P/$vF5vF5n=,tG7j\j]l<*uD2D$������������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#9`7*wF5rC2l;*xN<qC3sB1[2%��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������,Km?0zG5o@/n@0wD2)I��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� :!gp@1xG4T/#�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������++n?. $��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���������������������������������������?�����������������������������������������������?��������������������������������������������?����������?��������������������������������������������������������������������������������?����������?������������������������������������������������������������������������������������������������������������������������������������?����������?����������?�������������������������������������������������������������������������������������������������������������������������?����������?��������������������������������������������������������������������������������������������������������������������������?����������?����������?������������������������������������������������������������������������������������������������������������������������������������?����������?��������������������������������������������������������������������������������������@�����������������?�����?������������<�����|����������������������������������������?���?`��������������b(���`������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���:[(K! w���M���G���G���?���3���*��� ��������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������@c-a-a+G ���V���S���M���C���8���,���"��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������E^*^,[-\+b-`+6 n���Q���P���J���?���3���(������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���(K b-[.U. T)V(\*b,U%/ j���Q���P���H���<���1���'��������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( ud.],X0"T+Q&N$Q%X(\*b+O! ���[���O���N���C���8���+��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���/U&a-\/!W. T)Q%O$M#N#R%X(_*_(D ���W���Q���J���=���-��������� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������/ d.^. Y1"V,S(P%N$N#N#M#N#T%Z(`*^(5 j���L���E���7���&������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Q_,`.]2#X/!T*R'Q%P%O$O$O$O#N#P#U%Z(^)U$1 _���5���)������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������H d0 ]1"Z2$W,U)S&R&R&Q%P%P$P$P#P#N#P#V&[(`*X% p��������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������( qd/`/]4%Y/!U+T(R'R'R&Q&P%P$P$P#P#O#N#P%V'Z']( _����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� 2[+c0 _3$[4%X.U*T(R'R'R'R&Q&P%P%P$P$P$O#Q&W'_*<��������� ����������������������������� ��� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������2f1!`1!]6'Y1"V,U*T(S(R'R'R'Q'P'P&P&P%P%P&U'[(Y&R�������������������������������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?_,b0 _6'\5&X/ V+U*U)T)S(S(S(S(Q'Q'Q'Q&Q&R(Z'_)' ~��������� �������������������� ���������!���)���+���)���#��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������H!g2!`4$^9)Z3$X/ V,U+U*U*T*T)T)T(R(R'R'R'R)W)_)Q"2����������������������������������&���+���8���?���<���6���-���"��������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! \f1 b2!_:+\6'Y0"W. U,U+U+U+T+T*T*T)S)R(R'S(U*[)\' ^������������������������ ���������%:4 k���I���N���J���B���6���+��� ��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ������ I"g1!b6'_:*\3$Z0!X.V-V-V,V+U+U+U+U*T)S)T)T+X*`*=��������� ��������������� ���������$BAa)W$1 i���Q���O���H���>���2���'��������� ��� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������5g2"b4#a<-^7([2$Z0!X. W. W.W-W,U,U,U+U+U*T*U,X+]*X&E��������������������� ������!���%]M _(Y([(_(U#1 n���R���Q���H���=���2���(��������� ��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������ ��� 9_-c1"b<,`;,\4%Z1#Y0!Y/ X. W. W- W-V-U,U+U+U+U+V-[+b,7��������� ������������ ������ ���& NP!_)Y(W.U+X'['`)T# ���\���O���N���E���:���.���$��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������� ����� Df1"b6'`?/]8)[4%Y1#Y0"Y0!X/ W/!W. W. W. V-U,U,U,U. Y-`,M �-������������������ ������"���& jS#^(Y*V. T,R)R(S'X'\(](H ���V���Q���M���B���7���+���!��������� ������������������������������������������������������������������������������������������������������������������������������������������������������$ ci2#b0!b=-_;-\6'[2$Y0"Z0"Z0"Z0!X0!X/ X. X. W. V-V,V. X/!],b,$ o��������� ��������� ������#���&[W%](Y*W0"U,S*R(Q'P'Q'U(Z(^)^(9 o���T���P���J���>���2���'��������� ������������������������������������������������������������������������������������������������������������������������������������������� ���%R'f2"c:+b@1^8*\5&Z2%Z1#Z1#Z0#Z0"Z0"Y0"Y0!Y0!Y/!W. W. Z1#\-c-L!��&������ ���������������$���'! yX%[(Y+W0#U- U+S)R(R(R(R(R)T)V)Z(`)X$5 j���R���O���E���8���'����������������������������������������������������������������������������������������������������������������������������������������������������% ig2"c3$dB2`=.^7(\4&Z2%Z2$Z2$Z2#Z1#Z1#Y0#Y0"Y0"Y0!Y0!Y0#[1"^,_* U��������� ��� ���������"���* y[&](Z,X2$V.!T+S*R)R)R)R)R)R)R)R*U*Y(\)a+T#! ���X���I���C���2���!������������������������������������������������������������������������������������������������������������������������������������������ ���@],d2#e?/dC2a;+^7']5%\4%\4%\4%\3$\3$\3$[2#Z2#Z1#Z1#Z2#[4%^. d-6������������ ���������$���+* ^)\)[.Z3$W0!U-U+U+T+T+T+T+T+T+T+U+U+U,W+Y*^*`*G ���C���3���$������ ��������������������������������������������������������������������������������������������������������������������������������� ������;i3#d8)fG6b?/_9*]7']6%]5&\5&\5&\5&\4%\4%\3$[3$Z2$Z2$\4%]2#a-[(?��������� ���������"���,/ a*\)[0![6&X1"V. U-U+U,U,U+U+U+U,U,U,U,U+U,U-V.Y-\+b,b,E B�������������������������������������������������������������������������������������������������������������������������������������������� Ga0 c3#eD3cC4`<,^9*]7(]7']7&]6&\6&\6&\6&\5&\5&\4%[4%[6&]6&_/ a-! i������������������%���23a+^+]3$[6'Y2$W0"V/ U.U.U-U-U-U-U-U-U-U-U.U.U. U. V/ X0!Z1#\-a,^+F������������������������������������������������������������������������������������������������������������������������������������� ���#P&g3"d;+fH7a?/`:+_8)^7(^7(^7(^7']7']7']7']6&]6&]5&]5&]8(_3$e0 H!���#���������������%5Bc-^+^5&]9)Z4$Y1"X/!W/ W/ W/ W/ W/ W. W. W. W. W. W/ W/ W/ W/ X0 Y0 Y1#[3$],d-2 ��������� �����������������������������������������������������������������������������������������������������������������������������������+ si4$d6&gI7dE3a>._:+^8)^8(^8)^8)^8(^7(]7']7']7']7&]7'^7(_7(a0!c. b������������ ���% D>c-^-^7'^:*[5&Z2$Z1"Y0!Y0!Y0!X0!X0!X0!X0!X0!W0 W0 X0!X0!X0!Y0!Y0!Y0!Y1"[4%\0!b-I!�)������������������������������������������������������������������������������������������������������������������������������������� ���-X+e2#fC2gK8cA0a=,`;*_9*_9)_9)_9)_9)_8)^8(^8(^8(^8'_9)`:+`2#g1!8��� ���������"���&;N"f.^,`:)_;+]7'[4$Z2#Z2#Z1#Z1"Z1"Z1"Z1"Z1"Z1"Z1"Y1"Y1"Z1"Z1"Z1"Z1"Z1"Z1"[4$\5$^,c, a�������������������������������������������������������������������������������������������������������������������������������������� ������>h4$d7'hK;eE5b?/a<,`;+`;+`:+`:+`:*`:*`:*_9*^9*^9*^9*`;,a9)c1!Y* A���#���#���&���'MJ d._/ a<,`=-]8)\6&[3%[3%[3%Z3$Z3$Z3$Z3#Z3$Z2$Z2$Z2#Z2#Z2#Z2#Z2#Z2$Z2$Z2#Z4%]6']/ b,>���"������ ���������������������������������������������������������������������������������������������������������������������������������� Lc1"c3$hH7fJ:bA2a>.`;,`;,`;,`;,`;,`:+`:+`:+`:+_9*^9*_;,a<,a1!j4"3���"���)���+���) HV'f/`2#b?/`>.]9*\6'\5&\5&\5&\4&\4&[4&[4&[4&[4&Z4%Z4%Z3%Z3%Z3%Z3%Z3%Z3%Z3%Z5%\7']4%_,[( 9������������������������������������������������������������������������������������������������������������������������������������� ������C h3$f>.iP>eF5b@0a>.a<,a<-a<-a<-a=-a<,a;,a;,a<,`;+_;+`?/a8'f1!S' A���+���.���*ZR&e/ a2#cA/a@0_;+^8)\7'\7'\7']7&]6&]6']6']6&]6&]6&\6&\6&\5&\5&\5&[5&\5&\5&\5&\7']9*^/!d.5��������� �����������������������������������������������������������������������������������������������������������������������������������) mg3#c4$iM;gL9dD1b@/a>.a>-a>-a>-a>-a>-a>,a>,a=,a=,a=,a?.b>-b2"e1! b���.���4���. l[+f0 a6&eE3cB0a<,`;*^9)^8(^8(]8(]8(]8(]8']7']7']7']7']7&]7&]7&]7&]7&]7&]7&]7&]7'_:*^4%a.V&3������������������������������������������������������������������������������������������������������������������������������������� ���+Y+f3$hF5iQ?eG6cB1a?/a?.a?.a?.a?.a?.a?.a?.a?.a>.a>.a?/cA0b6&h2#F!���8���9���0 k_-e1!b6&eE4cD3a?._<,^:+^:*^:*^:*^9*^9*^9*^9)^9)]9)]8)]8)]8)]8)]8(]8(]8(]8(]8(]8(^:+_;*_/ b. d��������� ����������������������������������������������������������������������������������������������������������������������������� ������3j3$f;*kSAgK:eE4cB0b@/b@/b@/b@/b@/b@/b@/b@/b@/b@/b?/dC1d<+e1"W( O���=���=- a.c0 d<*fJ8dE3b@/a>-`=,`<,`<+`<+`<+`<+`<+`;+`;+`;+`;+`;*_;*_;*_:*_:*_:*^:*^:*^:*_;+a>-`3#e/C��� ������ ����������������������������������������������������������������������������������������������������������������������������������9^-f2"iI9iQ?eH6dD3cB0bA/bA/bA/bA/bA/bA/bA/bA/bA0bA0cB1dC2c5%i3#.���<���=( g2"c0!d=,hK9eF5bB0b?/a>.a>-a>-a>-a=-`=,`=,`=,`=,`=,`<,`<+`<+`<+`<+`<+`<+`<+`<+`;+`;*a?.a:*a.c. X�������������������������������������������������������������������������������������������������������������������������������������� ������I#j3$gA1kUDgK:dG6dD3cC2cC2cC2cC2bB2bB1bB1bB1bB2bB2bB2cE4c;+f3#V(T���G4c/ c0!d>.gK:eG6cB3b@0a@/a@/a?/a?/a?/a>/a?/a>/a>/a>.`>.`>.`>.`>.`>.`=.`=.`=.`=-`=-`=-a?/b@0a2#f0 3��������� ���������������������������������������������������������������������������������������������������������������������������������� Vh2#f4&lSCjR@fI8eF4dD3dD3dD3dD3dD3dD3dC3dC3dC2dC2dC2dD3eB1c3#f1"# ���J3g2#d0!e?/hM;fG6dD4cB1bA/bA/bA/bA/b@/b@/b@/b@/b@/b?/b?/b?/a?/a?/a?/a?/a?/a?/a>/a>/a>/a>.cB2b<+c0!Y* 3������������������������������������������������������������������������������������������������������������������������������������� ��� 4Y)g1"iD4lVDhM<fH7eE4eD3eD3eD3eD3eD3eD3dE3dE3dD3dD3dC2dD3c9)h4#>���S?h3#d2"fA1gM;fI8eE4cC2cA0cA0bA0bA0bA0bA0bA0bA0bA0bA/bA/b@/b@/b@/b@/b@/b@/b@/a@/a@/a?/a?/bB1c@/a1"d/ % m��������������������������������������������������������������������������������������������������������������������������������������������6l4$e6&mUDjR@gL:fH7fF5fF5fF5fF5fF5fF5fF5fF5fF5eF5eF5eG6dB1e3$_/!{9i3#c1"gC2iM<gI8eF5dD4eC2eC2eC2dC2dC2cC2cC2cC2cB2cB2cB1cB1cB1cB1cB1cB1cB1cB1cB1cB1cA0cA0cB1dE4b9(g2!E ���������������������������������������������������������������������������������������������������������������������������������������� ��� :^-f3#lQ@nZHiO=gK9fH7fG6fG7fG7fH7fH7fH7fH7fH7fH7fG7fG7fF5e8'e2#?F i4$d5&fE4hN<gK9fH7eG6eF4eF5eF5eF5eF4eF4eE4eE4eE4eE4eE4eE4dE4dE4dE4dD3dD3dD3dD3dD3dD3dD3dD2fG6eE3c2"b/ R������������������������������������������������������������������������������������������������������������������������������������� ������E j3"g@/n^LlTBhM;fJ8fI8fI8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fK9eC1e3#],T'g3#e4%fG6gN<fK9eI8eH7eH7eH6eH6eH6eH6eH6eH6eH6eG6eG6eG6eG6eG6eG5eG5eG5eG5eG5dF5dF5dF5dF4dF4eG6eI7b5&i2#=��������� �����������������������������������������������������������������������������������������������������������������������������������# ai3#f7&nXFnZHkQ?hL9gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gK8gL9fE3d3#d2"f3#e7'hF4hM;gK9fK8fJ8fI8fJ8fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fI7fH6fH6fH6fH6eH6eH6eH6eG6eG6gL:eA0d0!X) 7������������������������������������������������������������������������������������������������������������������������������������� �����"U(g2"kM<qaPlTCjO=hL:gL:gL:gL:gL:gL:gL:gL:gM:gM:gM:gM:gM:gL:gL:fA0e5%e:*gJ8gN;gL9gK9gK9gK9gK9gK9gK9gK9gK9fK9fK9fK8fK8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI8fI8fI8fI8fI8gL:hK9c4%h2#) r��������� �����������������������������������������������������������������������������������������������������������������������������������% gh3"f8(p]LnZHkR@jO=iM;hM;hM;hN;hN;hN;iN<iN<iN<iN<iN<iN<iN<iN<iN<iN<hL;iM;iN<hM;hM;hM:hM:hM:hM:hM:iL:iL:iL:hL:hL:hL:hL:hL:hL9gL9gL9gK9gK9gK9gK9gK9gK9gK9gK8gK8hK9jN<e<,f2#V).������ ������������������������������������������������������������������������������������������������������������������������������� ���;`/ f3"mQ@qaPlVDkQ@iN=iN<iN=iO=iO=iO=jO=jO=jO>jO>jO>jO>jO>jO>jO>jO>jO>jO>iO>iO=iO=iO=iN=iN=iN=iN<iN<iN<iN<iN<iM<iM<iM<iM<iM;iM;iM<hM<gM;gM;gL;gL;gL:gL:gL:gL:gL:iO>hI8c2#c1! W�������������������������������������������������������������������������������������������������������������������������������������� ������;j3#hB1rfUn[IkTBjP?iO>iO>iP>jP>jP>jP>jP?jP?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jQ?jP?jP?jP?jP>jP>jP>jP>iO>iP>iO>iO>iO>iO=iO=iO=iN=iO=iO=iN=iN=iM<iN<iN<iN<hN<gN<gM<gL;gM;gL;iN=jRAf=-h3$@��������� ���������������������������������������������������������������������������������������������������������������������������������� Sf2"e3#p[JqcQmWEkR@jQ?jQ?jQ?kQ?kQ?kQ@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kR@kQ@kQ?kQ?kQ?jQ?jQ?jQ?jQ?jQ?jP?jP>jP>jP>jP>jP>jP>jO>jO=jO=jO=jO=iO=hO=hN=hN<hN<kRAhF5d2#`/ A�������������������������������������������������������������������������������������������������������������������������������������� ���"S(h3"iE4siTo]JlVCkTAkS@kR@kS@kS@kSAkTAkTAkSAkSAkTAkTAlTAlTAlTAlTAlTAlTAkTAkSAkSAkTAkTAkTAkSAkS@kS@kS@kS@kR@kR@kS@jS@jR@jR?jR?jQ?jR?jR?jQ?jQ>jQ>jQ>jQ>jQ>iQ>iP=jR?kS@e7'i3#. y��������� �����������������������������������������������������������������������������������������������������������������������������������,vj4#g<+shVrcPmZGlVClTBlUBlUBlUBlUBlUBlUBlVClVClVCmVCmVCmVCmVCmVCmVCmVCmVCmVClVClVClVClVClUBlUBlUBlUBlUBlUBlUBlUBlTAlTAkTAkTAkTAkTAkS@kS@kS@kS@kS@kR?kR?kR?jR?jR@lWDhD3h2#O&��$������������������������������������������������������������������������������������������������������������������������������������� ���,V+f2"nUCtnYp^LmXFlVClVClVDlVDlVDlVDmWDmWDmWEmWEmWEmWEmXEmXEmXEmXEmXEmXEmXEmXEmWEmWEmWEmWEmWDmWDmWDlVClVClVDlVDlVDlVClVClUClUCkUCkUCkUBkTBkTBkTBkTBkSAkSAkSAlSAlVDkR@e6&j3#$ i��������� ����������������������������������������������������������������������������������������������������������������������������� ������C k5$iD3tmZrdRn\JmXFlWElWDlWDlWEmXEmXEmXEmXFmXFmYFmYFmYFmYFmYFnYFnYFnYFnYFmYFmYFmYFmYFmXFmXFmXEmXEmXEmXElWElWDlWDlWDlWDlWDlWDlVClVCkVCkVCkUCkUBkUBkUBkTAkTAkTAlVClYFf<,i2#@���"������ ���������������������������������������������������������������������������������������������������������������������������������� Ee3#g6%sfSulZp`NnZJmXFmXFnXFnXGnYGnYHnYHnZIoZIoZIoZIoZIpZIpZIpYIpYIpYIpYIpYIpZIpZIoZIoZIoZInZInZIo[Io\Ko\KoZInXGmWFlXFmXFmXFmXFmXFmWEmWElWElWDlVDlVDlVDlUClUClTCmZHjO>f3#a. ?������������������������������������������������������������������������������������������������������������������������������������� ������F#k5$lL;vq_reSp^LoZInYHoZIoZIoZIp[Jp[Jp[Jp[Jp[Jp[Jp[Jp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Jp[Jp\Jq]Kq`Nq^LnTBoXFq^Lp\KoZImYGmYGmYGmYGmYGmXFmXFmXFlWElWElWElWElVDlVDmXFmWEe9(j5$8��������� ���������������������������������������������������������������������������������������������������������������������������������� \g5%g8(ugUulZrbPp^Kp\Jp\Jp\Kp\Kp\Kq\Kq\Kq\Kq\Lq\Lq]Lq]Lq]Lq^Lq^Lq^Lq^Lq]Lq]Lq]Lq]Lq]Lp\Kp]Lr_NscRq[Ij@0g6&h;*pZHq`Nq\KpZInZHnZHnZHnZHnYHnXFnXFmXFmWEmWEmWEmWEmWEo]KjG6f2"^- 9������������������������������������������������������������������������������������������������������������������������������������� ���/\. i6&r\JyuctgTrbOq_Lq^Lq_Lq_Lq_Mr_Mr`Mr`Mr`Nr`Mr`Mr`Nr`Nr`Nr`Nr`Nr`Nr`Nr`Nr`Nr`Nr`Nr`Mr`NsbQthUq[Hi<,h6&e5%g4$mN=seSq_Mp^Kp^Kp\Jo\Jo\Jo]Jo\Io[Hm[HmZGmZGmZGmZFo\JoYGf7&h3#" c��������� ����������������������������������������������������������������������������������������������������������������������������� ������4~l7&jA0yvdvn\seSrbOqaNqaNraOraOraOraOraOraOrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbPrbOraOrbOseSukYqZHi:*j9)U*^.!h6'pVDsgUqbOp`Mp_Lp_Lo^Ko^Ko^Ko^Ko]Jm]Jm[Im[Im[Hn\IqaNiE4j4#Q&��#������ ���������������������������������������������������������������������������������������������������������������������������������� Pg5&h5%u`OzvdujXsdSrbPrbPsbPsbQsbQsbQscQscQscQscQsdQsdQsdQsdQsdQsdQscQscQscQscQscQscRsdSuhUvkYqWFk;,o:*95h7'j@0uhVtgTrcPqaNq`Mq`Mp_Lp_Lp_Lp_Lp^Ko^Kn]Jn]Jn]Jq`NmQ@f3#].H�������������������������������������������������������������������������������������������������������������������������������������� ������N'm7'pP@||iwo\thVsdSscRscRsdRsdRsdRteRteRteRteRteSteSteSteSteSteRteRteRteRteRteRteRtfTvkYwq^pP?k8)o:*6Zf5'i6&r[Iwo\tgTrcPqbOqaNqaNqaNp`Mp`Mp`Mp_Lp_Lo^Ko^Kp`MqbOg<+k4$2��������� ���������������������������������������������������������������������������������������������������������������������������������� Uk7(k<-{tc|wfwl[uhWtfUtfUtfUufUugUugUugVugVugVugVugVugVugVugVugVugVugVugVugVugUugUvhWxl\xn]qQAm:*n:*0���'G$o9*nI9ys`vmZtgVscRrcQrbPrbPrbPqaOqaOqaOq`Nq`Np_Mp_MrdRmQ?g4#[- 8������������������������������������������������������������������������������������������������������������������������������������� ��� .^/#l7(tZJ~~myp_vjYuhWugVuhVuhVuhWuhWviWviWviWviWviWviWviWviWviWviWviWuhWuhWuhVuhWviXxo^zo_oI8m:(j8(" d��� Zk8)j:+yp^ytbujXtgUsdQrcQrcQrcPrcPqbOqbOqbOqaNpaNp`NrdQp]Ke5&i3#, m��������� �����������������������������������������������������������������������������������������������������������������������������������5q;*nI8~}l|wfxn^vjYuiXuiXuiXviYvjYvjYvjYvjYvjYvjYvjYvjYvjYvjYvjYvjYvjYvjYviYviYvkZyrayjYnF6n;*h7'& ^���4U+m8)rTD{{iwp^tiXsfUseSrdSrdSrdRrdRqcQqcQqcQqbPpbPqbPsgUjH7h3"I#���!������ ����������������������������������������������������������������������������������������������������������������������������������3f5'm:*yk[pzqbxm^vjZvjZvkZvk[wk[wj[wj\wk\wk\wk\wk\wk\wk\wk\wj\wk[vk[vj[vj[wk\xm]{tcxiXnC2r=,^2% ?������7r<+nF7}|kzudvm\thXsfVsfUseUrdUrdTrdSqcRqcRqcRqbQpbQrgVnWFe3#f2# P�������������������������������������������������������������������������������������������������������������������������������������� ������@ p<*qK<o~xg{q`yn]yl]yl]yl]ym]ym]ym]ym]zm]zm]zm]zm]zm]zm]zm]zm]zm]yl]yl\ym]zo_}veyhWmA1p<+Z/" ;������ 5`2$l9)xeTnzq`wlZuiWuhWuhVugVtfVtfUtfUseTseTsdSsdRsfTsfTg=-j4#E!��� ������ ����������������������������������������������������������������������������������������������������������������������������������$ \q<,nA0xin}tc|p`{n^{n^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o^{o_|rb~ziwaPm>/uA0Q+����������@p;)oI8o}vezo^wkZviXviXviWviWuhWuhVtgUtfUtfUteSteTvlZoUCf2"Z, 6�������������������������������������������������������������������������������������������������������������������������������������� ������ Q+ p=,x^Nxxi}rc|p`{p_{p`{p`|p`|p`|p`|p`|p`|p`|p`|p`|p`|p`|p`{p`{o`{o`|qa~tdxiw_On?/r?.H' #��������� Sm9)l=-|o_}n|rbzn]wkZwiYwiYviXuiXuhXuhWthVthVthVtfUviXuhUh>-k5$0��������� �����������������������������������������������������������������������������������������������������������������������������������;s?/pE5qpwgsc~qb~qb~qcqcrcrcrcrcrcrcrcrcrcqcqcqb~rb~scxizjuTDn>-r>/<��� ��� ��������!U, o;+x^Mvwf}q`|m]zk[zk[zkZykYxkYxjYxjXwiWwiWwhWxiWzm[kG7g3#K#�'������ �������������������������������������������������������������������������������������������������������������������������������� ���8k;,n=,}fWyzkvfscscsdsdsdtdtdsdsdsdtdtdtdtdscscsduezjykwUEq?.qA02g��� ��� ���������# \o<,mA/}m~ntc~p^}n\|m\|l[|lZ|lZ{kZ{kY{kXzjWzjWziW{n[vbPf5$d2" K��������������������������������������������������������������������������������������������������������������������������������������������D$r?-vUE}qyivgufufufufufufvfvfvfvfvfugugufufvfwh|nxitO?r@/h:+# P��� ��� ��� ������8c4'n<,{dUvxhrc}p^}n^}n^|m]|l\|l\{l\{k[{kZzjYzjY{l[}q_lG6j4#?��������� ������������������������������������������������������������������������������������������������������������������������������������ <n=-o=-yj}~oxivgvgvgwgwgwgwhwhwhwhwhwhwhwgvgwhyjqwhtM=uB0m=-  C������ ��� ��� ������> s?.sO?y}nueqbp_o^o^~o]~n]~n\}m\}l\}l[|kY|kZr`vZIg4#c1! G������������������������������������������������������������������������������������������������������������������������������������������ ^3&rA/jZ{r{mxkwjwjxjxkxkxkxkxkxkxkxkxkxkyk{mtn_tI:wD3Y3'4��� ��� ������������ Nk9+n>.texyjuercqbqapap`p_o^~o^~n^~n]~m\p_~n]i<,i4$* u��������� ��������������������������������������������������������������������������������������������������������������������������������������.SuA0sJ:wr}p{nylylylykylylylylykykykzl}pwqbrE5xE4d7*&������ ��������� �����Y0#q?-vQB{oxhtercqbraqaqaq`p_p^o^o]o]vesTDg4#X+*������ ������������������������������������������������������������������������������������������������������������������������������������������T0$yE4sG7oa}wt~r|o{o{n{n{n{n{n{n{n{orwfXtG8wF6L* ��� ��� ���������������(fs?/qG6rx{mwhsfserdrcrcqcqbpap`o`o_sc|cSf5%j5%( o���������������������������������������������������������������������������������������������������������������������������������������������������� M+ {vD3s@/tG7k^~ywt~r|p|p|p|p|p~ru{cUsD3xG68u������ ��������������� ����'`5'q>-i[qylviuguftfsfsdrcrcqcqbpbrcufnE5i5$D ��������� ������������������������������������������������������������������������������������������������������������������������������������������������������ 5\6)vD5sA0{XI~r~zwt~r~r}r~syw~_PtG6vF5>!|��� ��� ���������������������A"vB0vP@{x|oxkvivhugugtgtfrdrdqdqcqcwh}dTg7&b1"D������������������������������������������������������������������������������������������������������������������������������������������������������������������������&7l>/xF5tB2}[L~zwvv{tzUFuF7sE6" O��� ��� ��������������������� Dp@0rC1qs{nxkxiwiwhwgvgvgtetdrdrcvgraj=-l7&7��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������� ?%qpB3uF5uH8m`|wzUFvF6qB3*Y������ ������������������ ������F'vB2|ZJxq|mylyjxixixiwhwhvgveteue|lwVFh5$Q)�)���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� J,"zI9uD4wK;pcswM<zI9k>0 1������ ��������������������������'\uC2rD2uu}q{mzmylykxjxjwiwivhvgugyktei<*j7&# a��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%6]7+wH8tD2|YJ{xP@zH8]6*/��� ��� ����������������������� ���'`7*uD4i\zu}q{p{pznznymxmwlwkvjviwj|ovRBj7%R(��&������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������"2nA2{I8vG7|XIwM=|J9_6* ������ ������������������������������2xyF5xN?ws}q}p}p}p|o{nzmymykxkxj~pfXg6&e3#F�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� <%lqD4zJ8{L;L-#  ��������������������������������� ��� Cn@1rB1qe}wt~r}r}q}q|p|o{nznymxl{n~qqH9l7':��������� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������D) tA'r������������������������������������ ������R/$xG6~\N{w~u~t}t}s}s|q|p{pzoyozouzWIj7'^1# 5�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� IuD4uF6~ywvu~u}t}t|s|r{q{qzpsvjk<+n9)1{��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&_7*uE4i\{xxxwvu~t~t~s}r}q}ryzUGm9)K'���"������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���9r{I7zSC|yxxxwuu~t~t}s}rwqdl<,l8) X������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ i?0tF4v|zxxxxvuut~svtsH8o;)9��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������C&sxG6~ZL~|{zyyxwwv~j]l:)a4% 2�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� #|K;wG8{TE~|zyyxwzusH9q=,3��������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������;!JlB3{J8sC2cT~{yyy~ZMm;*Z0#-��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?%hj@1xG6uH8k^|{ynn@0m;, R�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������F) svG6wE4tH8na}wM?s?.F&��������� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������" 0U3'wG6tD2}XIwm_m<*l;, L����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(2a:,yF5sA1zRBvJ:q@.1t��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  B%oh>/uC2tE5j]vjq?.a6'$������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  C$hsC2rA/tH7qC2p?/ C��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 3U0$rA1wD3L*������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(8k=- &�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������?���?���?���?���?���?���8���0���`�����?������������������������?����������������������������������������������������������������������������������������������?��������������������������������������������������������������������������������������������������?��������?�������������������������������������������������������������������������������������������������?��������?���������������������������������������������������������������������������������������?�������?�������������������������������������� ��������8���x������������?���?�����������������(���H������� ���������������������������������������������������������������������������������������������������������������������������������������������������������� A]*Bj���G���A���9���+��������� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���@c.b/\*9 o���K���F���<���-��������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� " ec-Z. U+Z)d-U&' `���J���D���7���(���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �� N#b. W. S(N$P$[)b,M  ���U���I���B���1�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� - ~d/\0!V,R&O$M#N#R%](`)C ���K���@���/������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 8\*a0!Y1"T)R&P$O$N#N#O#U&_+\'2 _���2������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� ���7e1 \2#V-T(R'R%Q%P$P#P#O#P$X'`*Y& f������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������  Ub.a2#Z1#V+S(R'R&Q&P&P%P$P$O#P%\(R# >�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� �'T(e2#\5&W. T*T(R'R'R'Q'P&P%P%P&U'`*! l������ �������������������������������� ��������������������������������������������������������������������������������������������������������������������������������������������������������������� ��� , yh2"_5%[4%W-T*T)S)S(S(R'Q&Q&Q&S'^)G�"������������������� ���������(���/���*��������� ������������������������������������������������������������������������������������������������������������������������������������������������������� 6[,d5%^8)Y1"V-U+U+U+T*T)S)R(R'R)Z*\'L������������������ ������@O���B���D���9���*��������� �������������������������������������������������������������������������������������������������������������������������������������������� ���?h5#a;*]6'Y/!W-V,V,U+U+U+T*S)T*U*a,;������ ������������ ������VN S#& ��X���J���C���6���'�������������������������������������������������������������������������������������������������������������������������������������������� G_/d7'_:+\3$Z/ X. W. W-V,U,U+U+T*U+]+U&@��������������� ������\T"_+\*`)E ���T���I���@���2���$���������������������������������������������������������������������������������������������������������������������������������� ���M$g4%a=-\6'Z1#Y0!X/ W. W. W-V,U+U+U,Y,d-* ������ ��������� ������ hV%])W-T,U(])^)= p���N���G���=���.���!������ ���������������������������������������������������������������������������������������������������������������� ��� # gh2"b;+_<,[5%Z1#Z0"Z0"Y0!X0!X. W. U-V-W. a-Q#-������������ ������ qW$]*V. T,R)Q(R'W(_)W&3 h���J���E���:���)������ ������������������������������������������������������������������������������������������������������������ (S'g6&b>0^8)[3%Z2#Z0#Z0"Z0"Y0"Y0!Y/!W. X0"^. b, c������ ������ ������  s[']+W/!U- S)R(R'R(Q)T)Z)a+Q"! �\���H���>���,������ ����������������������������������������������������������������������������������������������������� ���<i4$eA0a=.]6'[3$[3$[3$[2$[1$Z1#Z1#Z0"Y1#[1"d/8��������� ���������)+ ]'^,Y1#V. T+S*S*S*S*S*S*S*V*]+a,J ��M���2��� ��� ����������������������������������������������������������������������������������������������������  Ne1!f:*dE3`;+]6']5%\5&\5&\4%\4%[3$Z3$Z2$[4%a1"]+=������ ���������(2 b,^.Z4%X0!U-U+U,U,U+U+U,U,U,U,U-X-a-c-J!Y������ ������������������������������������������������������������������������������������������������� ���J"h6&eE5b?/^9)^7'^6'\6'\7']7']6&]5&[4%[6&^4$d0, ���������������*9a+_1"\6&X2#W/ U.U.V.V.V-V-V.V.U.U.V/ W0!Z2#_.d- ]������ ������������������������������������������������������������������������������������������������ ( oi3#gA0eF5`<,^8)^7(^7(^7(]7']7']7']6&]6']8(e2"Q%��'����������29c,^3#]9)[4%Y0"X/!X/ X/ X/ W/ W/ W/ W/ X/ X/ X0!Y0!Y1"\1"c/8������ ������������������������������������������������������������������������������������������������� ��$U(h:*gK9bA0`<+_9)_8)_8)_8)^7(]7(^7(]7'^9)`6&f1!$ n�����������2@f-a6&^;+[6&Z2#Y1"Z1"Z1"Z0"Z0"Y0!Y0!Y0!Y0!Z0!Z0"Z1"Z2#[5%`/ \*?��������������������������������������������������������������������������������������������������� ���3h3$gF4eG6b>/`;+`:+`:+`9*`9*`9*^9*^8*^:*`:*g3#C���"���!��� �8Gd/b8)`<,]7(\4&\3%[3%[3%Z3$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$Z2$[5&]2$e/. ������ ������������������������������������������������������������������������������������������������ ?b/ h>.gM<cB1`=-`<,`<,`<,`<,`;+`;+`;+^:+`=.c5%c/ U���!���$ HK!f1!a:)`?/]9*\6'\6&\6&\5&\5&\5&\5&[5&[5&[4%[4%[4%[4%[4&[6&\7'c/!J!��#��������������������������������������������������������������������������������������������������� ���=i6&hM:eI7bA/a>-a>-a>-a>-a>-a>,a=,a=,a?.b<,j4#9���)���) IR&g2"c>-bC1`<+^9)]8(]8']8']8']8']7']7']7&]7&]7&]7&]7&]7&]7&^:)a4%c. Z������ ������������������������������������������������������������������������������������������������  ^g0!hC3hO=dD2b@/b?.b?.b?.b?.b?.b?.b>-b?.c@/f5%Q&B���+\V'g5$dA0cD3`>._;+_:*_:)_:)_9*^:*^:)^9)^9)^9)^8)^8)^8(]8(]8(^:*_9)e1!A���������������������������������������������������������������������������������������������������������S&k;+jS@fI7cC1b@/b@/b@/bA/bA/bA/bA/b@/cC1f<+h2! v���- r\*h6%fF4eG6b@/a=-`=,`=,`=,`<,`<,`<+`<+`<+`;+`;+_;*_;*_;*_;*_;*a>.c4%X( 7��������������������������������������������������������������������������������������������������� ���+wi3#iH8hQ>eF5cB2bB1cB1cB1bB1bB1bB1bB1bC2dA1h5%H"���A u^+g6&fH7eH7bB2a@/a?/a?/a?.a?.a>.a>.a>.`>-`>-`=-`=-`=-`=-`=,`=,a?/a<+h1!3������ ������������������������������������������������������������������������������������������������ ?c-j?/kVDgJ8eE4eD3dD3dD3dD3dD3dD3dC2dC2eE3f:)^- _# e/ g7'fJ8fI8cC2bA0bA0bA0bA0bA/bA/b@/b@/b@/b@/a@/a?/a?/a?/a?/a>.a?/bA0e4$P%�(��������������������������������������������������������������������������������������������������� ���E k5%kP?iQ?fH7fE4fE4fE4fE4eE4eE4eE4eE4eF5dA0h5%4% c/ h9)hJ8gJ9dE4dC2dB1dB1cB1cB1cB1cB0cB0cB0cA0cA0cA0cA0cA0cA0cA0b@0cD3c:)g1  e������ ������������������������������������������������������������������������������������������������  Pd/ jD3lYGiM;fH7fG6fG6fG6fG6fG6fG6fG6fG7fF6g8(T&4d1"g;+gL;gL:fG7eE4eE4eE4eE4eE4eE4dE4dE3dD3dD3dD3dD3dD3dD3dD2dC2dC2eD4eE3g4#K"������������������������������������������������������������������������������������������������������� �� S'k;+n\IkSAgK9fI7fJ7fJ8fJ8fJ8fJ8fJ8fJ8fK9fE2d3#Y)b0!g;+gM:fL9eI8eH7eH6eH6eH6eH6eH6eH6eH6eG6eG6eG5eG5eG5eG5eG5dF4dF4dF5eJ7f9)^,D������������������������������������������������������������������������������������������������������ ( rj3#nQ@n\JiO=gK9gK9gK9gK9gK9gK9gK9gK9gL9gM9gI7d5%e2"f>-hL:gL:gK8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fJ8fI7fI7fI7fI7fI7fI7fI7eH7eH7gK9fF3h2#1������ ������������������������������������������������������������������������������������������������ 6\+lA0p_MlTBiN<hM;hM;hM;hM;iN<iN<iN<iN<iN<iN<iN<iK9hI7hM;hM;hL:iL:iL:iL:iL:hL:hL:hL:hL9hL9gL9gK9fK9fK9gK9gK9gK9gK9gJ8hK9iN<h9)U(,��������������������������������������������������������������������������������������������������� ���>l8'p[Jn[KjR?iN<iN=iO=iO=jO=jO>jO>jO>jP>jP>jO>jO>jP>jP>jO>iO=iO=iO=iN=iN=iN<iN<iM<iN<iN<iM<iM;iM;hM;hM;gL;gL;gL:gL:gL:iQ>gB1e1! d������ ������������������������������������������������������������������������������������������������Bb-lH7qdRkUDjQ?jP>jP?jP?jQ?kQ?kQ?kQ?jQ?jQ@jQ@jQ?jQ?kQ?kQ?kQ?jQ?jQ?jP?jP>jP>jP>iP>iP>iO>iO>iO=jO=jN=jN=iN=iO=gN<gM<hM<iO=jP?i8(H"������������������������������������������������������������������������������������������������������� ���N$k<*qbPn]KkTBjR?kS@kS@kS@kSAkTAkTAkTAkTAlTAlTAlTAlTAkTAkTAkTAkSAkS@kS@kS@kS@kS@kS@kR@jR?jR?jR?jR?jR?jQ>jQ>jQ>jQ>iQ=iP=kTAi@/d/  P������������������������������������������������������������������������������������������������������ ,yj3"oVDrhUmXFlUBlUBlUBlUClUClVClVClUCmUCmVDmVDmVDmVDmVDmVDlUClVClVClUClUClUClUClUBlUBlTBkTBkTBkTAkTAkSAkSAkS@kR@kR@kR?kUBjN=j5%8������ ������������������������������������������������������������������������������������������������� )Z+k?.siVp_MmXElVDlVDlWDlWDmWEmXEmXEmXEmXEmXFmXFmXFmXFmXFmXEmXFmXFmWEmWEmWElWDlVDlVDlVDlVDlVDlUCkUCkUCkTBkTBkTBkSAkSAlTAlYFj>.`, 7��������������������������������������������������������������������������������������������������� ���:k7&saOsiVo[ImXFmXFmXFnYFnYGnYGoZGoZHoZHoZHoZIoZIoZIoZIoZIoZHoZHnYHnYHo[Io\Jo[JnYGmXFlXElXFlXFlWDmWEkWDkVCkVDkVDkUClUBlZGiI8h3#* x������ ������������������������������������������������������������������������������������������������  De1!oO=vq^raOo[JnZIo[Io[Ip[Jp[Jp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp[Kp\Kp\Kp[Kp[Kr`Nq^LmR@oVEq]Lo[InYHmYGmYGmYGmXFmXFlXFlWElWElVDmXFmWEj9(R'���"��������������������������������������������������������������������������������������������������� ���F"m=,vkXtiWq`Mp]Kp]Kq^Lq^Lq^Lq^Lq^Mq_Mr_Mr_Mr_Mr_Mr_Mr_Mr_Mr_Mq^Lq_MseRr^Lk@0f4$h9(p[Hq`Np\Jo[Io[Io[IoZHnYGmYFmYFmYFmYFo^KkE5h1! `������������������������������������������������������������������������������������������������������ +nl6%s]KxsasdSqaNqaNr`Nr`OraOraOraOraOrbOrbOrbOrbOrbOrbOrbOrbOraOsbPujXr_Nn>.]- Y*i8(q^KqdQp_Lp_Lo^Ko^Ko^Ko\Jm[Im[Im[Ho_KmUBk6%<������������������������������������������������������������������������������������������������������� �#W+qF6{vdukYsdRrbPrbPscQscQscQscQscQsdQsdQsdQsdQsdQscQscQscQscQseTvn[r\Kq?/Y- 2c1!oO=vlYrdPq`Mq`Mp`Mp_Lp_Lp^Ko]Kn]Jo]KqaOl@.Y*2��������������������������������������������������������������������������������������������������� ���4n8)weTzvduhWtdSteSteTtfTtfTtfTufTufTufTufTufTufTufTufTufTtfTuhVwn]t]Lq>.Y- HK$p>.viWvl[sdRrbPrbOrbNqaNqaNq`Mq`Mp_LqdQmS@j5#0������ ������������������������������������������������������������������������������������������������ ;h3$tSC~{kxn\uhWugUugWuhWuhVvhVvhWvhWvhWvhWvhWvhWvhWuhWuhWuhVwjZzrau[Jr=,R*��,$ nm7(s[JytcuhWsdRrcQrcPrcPqbPqbPqaOp`NqbOqaOk:)P%��#��������������������������������������������������������������������������������������������������� ���B!q>,{rb{wgvl[ujXujXviYviYvjZvjZvjZvjZvjZvjZvjZvjZvjYvjYviYwm]yp`sRAr>-L&'4a0"sM={ygwo]tgWseTrdTrdSrdRqcQqcQqbPpaOrhVmN<g2! \������ ������������������������������������������������������������������������������������������������  Xm8(w[K~nzp`xk\xk[xl\xl\xl\xl]xl]xl]xl]xl]xl]xl\xk\wk\wk\zqazq`tRAr<+E$������8q;+{k\{xhukZtgWtfVsfVseUseTrdSrdSrcQreTq_Nk8'H!������������������������������������������������������������������������������������������������������ %^0#sG7o~xgzp_zm]zm]zn]zn^{o^{o^zo^zo^zo^zn^zn]zn]zm]zn^|td}tctM=q;+8}������ Gh5$uTC}mzq`vkYuhWuhWtgVtgUtgTsfTseTsfTtiWlB1_-<��������������������������������������������������������������������������������������������������� ��� 0zr=,{gVr~sd|pa|p`|pa}pa}pa}pa}pa}pa}pa}pa}pa|pa|p`}q`yh~o`uK;n;*+j���������M&tD3yi~yhzn]xkZxjYwjXviXviWviVuhVugUwm\rYIk4$8������ ������������������������������������������������������������������������������������������������ >f5&yVFvyjsdqcrdsdsdsdsdsdsdsdsdrdrdte{l~jZvI8i8) J������ ��� $ am9({cRqra}n]{l\{l[zkZzjZzjYzjXyiXzjYyhWl?.T(+������������������������������������������������������������������������������������������������������ I'wG6}ntwhteteueufufufufufufufufuewg}n~iZvE4d7' <������ ���)^0#vM=qxh~q`}n]|n\|m\|l[{l[{kZzjXzjY}q_rO>e0  Z������ �������������������������������������������������������������������������������������������������� <m9(~bR}oxivgvhwhwhwiwiwiwiwiwhwhykr~cTvD3_3%,��������� ��� 3}s?.rbsudp`p_~o_~n^~n]}m]}l\|kZ}o]|iXn;)F!��������������������������������������������������������������������������������������������������������� [1#{TDt|nykykxkykylylylylykyk|os}`RzG6R-!������������ =e5&zVFwzktercrbqap`p^p^o]~n]rbqI9a.H������ ���������������������������������������������������������������������������������������������������$AzD2}ZJxs}pzn{n{n{n{n{n{n~st~`QwD4L)������������ ���P+wG5}pvwiserdrdrcqbpap`o_td|cRk6&4������ �������������������������������������������������������������������������������������������������������6]j;,xE4fXyu~s|q|p|p|pww~ZKyE4B%��� ��������������� " \r=,~`Q~{nwiugugsfrdrcqcqbqcteqE5]- 2��������������������������������������������������������������������������������������������������������������������� A%~p?.{O>oazvts{{o}WGuD57y������������������ #Z0#yN=xwylwiwivgugtfsdrdrcxizYIg3#% m������ ��������������������������������������������������������������������������������������������������������������������������� D'uC1~UDwj}zm}QAtA2%V������������������� ���<!yF5zl~pzlxjwiwhwgvguetexhrbp?.K%���������������������������������������������������������������������������������������������������������������������������������������������3X3'wD4]O}}p{N<e<-! H���������������������� 4h9*[Mw|p{nzmykxkxjwivgvg{mvRAe1! U�����������������������������������������������������������������������������������������������������������������������������������������������������-Hi>/{J8eWwk|M=h;- +������������������������ ���K)zI8vt}q|p|o{nymxlxjwj|ofXm9&;������ �������������������������������������������������������������������������������������������������������������������������������������������������������  :#opA2M:Y5) ���������������������������  SsB1l^yt}s}r}q|p{oznymzm}prG8`/! <��������������������������������������������������������������������������������������������������������������������������������������������������������������������� B(u ���������������������������� ��U/#|P?y~v~t}t}s|r|p{ozotbUm8'/������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ 5yxF3vj{wwu~u}s}s}q|psxls@0L&������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������ ,k>.[L{xxxvu~t}s}ry[Mk6& T������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������B&|M<|yyywvu~tysgr?-E#���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&<zH8]M|zyxwx{{QB_1" 4��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� Y7+zI9UDz|yymbr>,4������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������%@X4&zH6_Q}uwI7T, �&�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������5 eg:*zH7ma_Po;+" a������ ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� H(q@.zI:sgwF3Q*������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� %I)t?.UFs\Ne6'5��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&@Z2%s@/wC16p������� ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������6]g:, ������������������������������������������������������������������������������������������������������������������������������������������������������������?����?6&��?gT��?^L��0_M��``M��@`M�����`N�����?`N�����`N�����`M�����hU�����6&�����N=�����^K�����\J�����\I�����ZG�����ZF�����7&�������������?��������?�����������������������������������������������������������������������������������������A0�����eS������?aN������?aO������aO������bP������bP�����bP�����bP�����bO�����ZH�����*�����VD�����`M�����^K�����?^K�����?[I�����\I�����4#������������������&�����L��������������������������������?�����?��� P�`O�dS bPbQ(���@������� �������������������������������������������������������������������������������������������������������������������������������������������(U&Dm���C���<���2���"������ ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������0f0 c/`+4e���H���@���2���!������ �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 9[(]/!T*W(b+W&- ���Y���E���>���.���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������Df0!W/R'N$O$X)a+J  ���T���A���6���!������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������! ad.]1#U+Q&O$N#M#Q$\(b+Dw���A���+����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������T'c3#Y0"T)R&R%P%P$O#O#Q$])])>8��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������* |h1"^4%V- S(R'R'Q&P%P$P#N#P%`*S$*��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 8`,c5&Z2$U+T(R'R'Q'P&P%P%O%U'^) e��� �������������������� ������������������������������������������������������������������������������������������������������������������������������������������������������������Ch5%^7(X/!U+T)S)S(S'R'Q&Q&R'a+=����������������������������*���0���'������ ���������������������������������������������������������������������������������������������������������������������������������������� Wg1!b8([4%W.U+U+U+T*T*S(R(R(Y+X& >��� ������������������� b _���B���@���4���#������ ��������������������������������������������������������������������������������������������������������������������������������K$j7'_:+Z1"W-V,V,V+U+U+T)S)V*b,1 ��� ��� ��������������� ~\'U$, �^���E���>���.�����������������������������������������������������������������������������������������������������������������������������' rh3$b<,]5'Y0!Y/ W. W.V,U,U+T+T,_,U$.������������ ������ ~^'\,[+b*N! ���U���C���<���*������������������������������������������������������������������������������������������������������������������ ,\+g:*_=-Z3%Y0!Y0!X/ W/ W. V-U,U-Z/c,! k��� ��������� ������ - ^([-T,R)R'[*`*G���M���B���8���(������ ����������������������������������������������������������������������������������������������������� ?k6'b>/]7)Z2$Z0#Z0"Y0"X0"X/!W.!V- X/!b/ B������ ������ ������#1 a)[. U. S*Q(Q'Q(V(]+\'; s���I���>���/������ ���������������������������������������������������������������������������������������������Bd0!f?.`>/]5&[2%[2$[2$[1$Z1#Z0#Z/"Y0#^1#_*D��� ������ ������*5b*\0"V0"T+S)S)S*S*R*S*Y*b,['/ ^���6���&�����������������������������������������������������������������������������������������������K"j:*eD3`:*^6&\5%\5&\5%\4%[3$Z3$Z3$]2$g1!;������ ��������-Cc,\3$X1#V-U+T+T+T+T+U+U+U,U-[.d.V'* ���%�������������������������������������������������������������������������������������������& mj5#gD4b@0^8)^7']6']6']7']6&]5%[5%[6'd4$V'0��� ������:Cd/^5&Z4%W/!U.U-V-V-V-V.U.U.U.V/ X0"\2#i1 @������ ��������������������������������������������������������������������������������������,[,i>,eG5`<,^8)^8(^8(^7(]7(]7']7&]7(`7(f1"& s���������?K f0!_8(\6'Z1"X0!X0!X0!X0!X0!W0 W0 X0!X0!X0!Y0!Z3$a2#V&0��� �����������������������������������������������������������������������������������������3l8(hH7cB1`;+_:*_9*_9*_9)^8)^8)^8)_;,g6&L"���������=L!g2#`:+]8)[3$Z2#Z2#Z1"Z1"Z1"Z1"Y1"Y1"Z1"Z1"Z1"Z2$^5$f/ - ��� ���������������������������������������������������������������������������������������� 9_,jC3fI9a>.`;,`;,`;+`:+`:+_:*^9*_;,c9)g2! `������NV&f4#b>.^:+\6'\4%[4%[4%[3%[3%Z3%Z3%Z3%Z3%Z3%Z3%Z3%[7'c2!S$�&��� �����������������������������������������������������������������������������������������F l=,iO;cC2a=-`=,`=,`=,`=,`<+a<+`<+`>.i5%?���$��� VY)h7%cA/`=-]8)]7'\7&\6']6']6&\6&\6&\6&\6&\6%\6%\6&]8'_7'c. _��� �����������������������������������������������������������������������������������������# ah3#hH7gK9cA0a?.a?.a?.a?.a?.a>-a>-bA0f;*]+ H���" d^+g9)eE3bA/_;*^:)^:)^9)^9)^9)]9(]8(]8(]8(]8(]8(]8(]8(`;+f3#?������ ��������������������������������������������������������������������������������������� Y(lA0jR>dE3bA/b@/b@/b@/b@/b@/b@/bA0e@/j4#& ���# yc.h;)fH7cB1`>-`<,`<+`<,`<+`;+`;+`;+`;+_;*_:*_:*^:*^:*`>.c9(^+ :��� �����������������������������������������������������������������������������������������, }l7&kP@fL:cD3cB1cB1cB1bB1bB0bB0bB1cD3i;*R&���?  b-i=-gJ9dD3a@/a?.a?.a?.a>.a>-a>-`>-`=-`=-`=-`=-`=-`<,a>-b>-i2!5��� ����������������������������������������������������������������������������������������� 8b-mH8kSBeG6dD3dD3dD3dD3dD3dC3dC2dD3fA0e1! x# g2!i?-fL;eF5bA0bA0bA0bA0bA/b@/b@/b?/b?/a?/a?/a?/a?/a>.a>.bC2g9)S'��&��� ���������������������������������������������������������������������������������������� ?n:*lVDhM;fF5fE4fE4fE4eE4eE4eE5eE4eE4h7(9( i2"iA0iM;eG6dC2dC2dC2dB2cB2cC1cC1cB1cB1cB1cB0cA0cA0cA0cA0cC2e@/f1  a��� ����������������������������������������������������������������������������������������� Ne0!mP=kUCgK9fH7fH7fH7fH7fH7fH7fH7fJ9f>-`-Fd1"gC2gM;fI8eF5eF5eF5eF5eF5eF5eF5eE4eE4eE4dE4dE4dE4dD3dD3dD3fG7i8'C������ ����������������������������������������������������������������������������������������P$nD2n^LiO=gK8fJ8fJ8fK8fK8fK8fK8fK8fK9e:)b-d2"hD0gN;fK8eI7eI7eI7eI7eI7eH7eH7fH7fH7fH6fH6eH6eG6eG6eG6eG5fK9f@/e/ L��� �����������������������������������������������������������������������������������������+ sk5#nVCmXFiN<hL:hL:hL:hM:hM:hM:hM:hM:hN;gH6f;*gD2hN<hL:hK9hK9hK9gK9gK9gK9gK8fK8fK8fJ8fJ8fJ8fJ8fJ8fJ8fI8gJ9hK9k7&;�������������������������������������������������������������������������������������������']*oI8paOjR@iN<iN<iN<iN=iN=iO=iO=jO=jO=iO=jO>jO>iO=iN<iN<iN<iM<iM<iM;iM;iM;iM;iM;hL;hL;gL:gL:gK:gK:gK9gK9jP>kA0Z+/��� ����������������������������������������������������������������������������������������� 4m:)q`NmZHjP?iO>jO?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jP?jO>jO>iO>iO>iO>iO>iN=iN=iN=iM=iN=iN=hM=hM<hL<iP?jK:i5$$ m��� �����������������������������������������������������������������������������������������Ce/qVCrdRkUBjR?kR@kR@kS@kS@kSAkSAkSAkSAkSAkSAkSAkSAkSAkS@kS@kS@kS@kR@kR@jR?jR?jR?jQ?jQ?jQ>jP>jP>jP>iP=iP=kR@l=,R%������ ����������������������������������������������������������������������������������������M#n@/sgUn[IkUClUBlUBlVClVClVClVCmWCmWDmWDmWDmWDmWDlVClVClVClVClVClUClUBlUBlUBkUAkTAkTAkTAkTAkS@kS@kS@kR?kWDkH7g1! R��� �����������������������������������������������������������������������������������������& jm6%q^LreSmYFlVDlVDlWElXEmXEmXFmXFnYFnYFnYFnYFnYFnYFmYFmXFmXEmWEmXEmWElWDlWDlWDlWDlVClVCkVCkUBkUBkUBkTAkVBlVCn;)=���������������������������������������������������������������������������������������������!U'qM<vo\o]LmXFmXFnYGoYHoYHoZIoZIpZIpZIpZIpZIpZIpZIpZIpZIoZIo[Io^Lo]Kp\KnYHmXFmXFmXFmXFmWElWElVDlVDlUClUClZHmF5\*.��� ����������������������������������������������������������������������������������������� 6p>-vmYsfUp\Kp[Jp\Jp\Kp\Kq\Kq\Kq]Lq]Lq]Lq]Lq]Lq]Lq]Lq]Kq\Kq^MtbQnP?i>,lN=q`Np[JnYHnYHnZHnYGnXFmWEmWEmWEmZHnVCm6&, }��� �����������������������������������������������������������������������������������������:c/tXFxs`rbPq_Mq_Mq_Mr`Nr`Nr`Nr`NraNraNraNraNraNraNraNr`NscPuiVqTBg5%b-j@/rdQq_Mp^Ko]Jo]Jo\Jn[ImZHmZHmZGq`MnF3V'�$��� �����������������������������������������������������������������������������������������I#qC2yr^tkXrbPrbOrbPrbPsbPsbPscPscPscPrcPrcPrcPrcPrbPrbPtgTvkYsSBi5&>_,oP?siWpaOp`Mp`Mo_Lo_Lo^Kn]Jn\Jo`NoTBi3# ^��� ���������������������������������������������������������������������������������������� Pk6%yfUzvdtgUtdRteSteSteSteSteSteSteSteSteTteTteTteSteSujXwn\tQ?k6'' x>p@0vjYuiWrcPraOraNq`Nq`Nq_Mp^Lp`Lq`Nm=,L#���������������������������������������������������������������������������������������������V)vSA}|kwlZtgVugUugVuhVvhVvhWvhWvhWvhWvhWvhWuhWugVugVwm\ym\uK9m7' ZJj4%wbPysbtfTrcQrcPrcPqbOqbOqaNp`MsgTnO=d.D��� �����������������������������������������������������������������������������������������0}t?-|ud{vfwkZuiXujXvjZvjZvjZvjZvjZvjZvjZvjZvjYvjYviYxp`xk[uL9`1" L���S'uO?{zhul[sfUrdSrdTrdRqcQqcQqbPqeSqaOn;)7��������������������������������������������������������������������������������������������8i4$x[I~nzp`xl\xl\yl\ym]yl]yl]yl]yl]yl]ym]yl]xk\xl\|ueyjZtF4`0" 1���, wt?-|sazveujYtgWtgVsfVseTrdSrdSrcRtjYoI7V&��%��� ���������������������������������������������������������������������������������������� B!wJ9o}xh{o_{n^{o_{o_|o^|o^{o_{o_{o^{o^{o^{o^{p_xhzgWwD4T+!���(^-yZI~nyn]viXviXviWuhWuhUtgUtfTujXqZIl5$& q��� ����������������������������������������������������������������������������������������� Om:*m^s~sd~qa}qb}qb~qb~qb~qb~qb~qb~qb~qb}qb~tdyj{bRxC2I%������� >wG6|l~wf{m]yjZyjYxjXwiWwiWvhVviXxjYn@/K"����������������������������������������������������������������������������������������������W+|XFyxitdsdtetetetetetetetetdwh|o~bSu@0C#��������� Il7&~hVr~r`}m]|m]|l[{k[{jZ{jXziX}p_vXGe1K���������������������������������������������������������������������������������������������5yvB/zkuxiufvgvgwhwhwhwhwhwgvgzk}n}[JvA01o������������W, yP?rxhp`o^~o^~n\}m\}l\|kZ}o]|iXo;)4��������������������������������������������������������������������������������������������� g6'n^t{mxjxjxkxkxkxkxkxkxk~pt[Ku?/.k�������������! ]r>,vguteqbqap`p^o^~n^~n]tcuP>^,0��� �������������������������������������������������������������������������������������������3bH5rcyr|ozmzmzmzmzmzms|oZHl<-$ T�������������$]0"ր^N|ykterdrdqbqap`o_tc|eUl6%0��� ����������������������������������������������������������������������������������������������� E(n;,VFzzu~r|p|p|py~rTDm;- B����������������� 7zH5uuwjugufsfsdqcqbrcwhuJ9R&������ ������������������������������������������������������������������������������������������������������� L)xE4^Ozvt}zmQB]5()����������������� Do<*hY{nwivhvgugtfsdrcyk~_Of3" Q��� ������������������������������������������������������������������������������������������������������������������� ;T/"|K;l_xkQ@Y1%�������������������� K&SCv{mykxixiwhvgufxhtdrB/>������ ���������������������������������������������������������������������������������������������������������������������������/Wg9+~O>{o}q~L:N-!�� ���������������������  XuC1zm~s|p{nzmylxkxiwi|oyTCb0>��� ���������������������������������������������������������������������������������������������������������������������������������������7irC2WG}M;F&��������������������������_4%׆bSx~r}p|p{ozmzmxk}qqdp<*/��� �����������������������������������������������������������������������������������������������������������������������������������������������J,!6 m����������������������������� = {K9zw~t}t}s|q|pzo{ps{PAR&��#��� ������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 0m=,m`{wvu~t}s}q|pwi]l7& W��� �����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������C$TBzxxvu~t}ru~rwH7O&������ ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� @xH7|yyxvv~t}bTg3"E��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������b:-SA|yxx{}rwE48�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������#6]7*yI7m^~{yYJY,��%��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������&Ec7)~N>sgsft?.'o������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������8li:*VEt|P?R)������ ����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� G&o=,ZIsm;) D������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� "M)vD3|I79��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������(Jb7) ���������������������������������������������������������������������������������������������������������������������������������������� ������ ���������������������������������������������������������?������������������������������������������������������������?�����?�����?����������������������������������������� �����?�(��?�X����p���������(���0���`���� ����������������������������������������������������������������������������������������������������������������DO# z���?���.��� ����������������������������������������������������������������������������������������������������������������������������������������������������������������� ^c. a/a,Cy���B���2���#������������������������������������������������������������������������������������������������������������������������������������������������������� X'`1#R'P$\*_)6j���<���-��������������������������������������������������������������������������������������������������������������������������������������������������1h4#W. R&O$N#R$^*Y&, U���!��� ���������������������������������������������������������������������������������������������������������������������������������������� 6a. _4&U*R&Q&P%P#O#U&f-I���������������������������������������������������������������������������������������������������������������������������������������������Bh8&X0 T)R'R&Q&P&P$R%b,$ x��������������������� ��� ���������������������������������������������������������������������������������������������������������������! cj6%_7'V,T)S)S(R(Q'Q'^,H������������������������'���+���"���������������������������������������������������������������������������������������������������������V(g;*Z4$V,V+V+U+T*S(X+\)C������������������( 9d���<���.������������������������������������������������������������������������������������������������7l;*`;*Z0!X. W.V-U+U+V,e. 3 �����������������2 b+b/X&1 ��_���:���.������ �������������������������������������������������������������������������������Df3"d>.[5&Y0!Y/!X/!X. V,U-_1!Q#����������������:c-Y/ R*W)`,S$$���R���8���+����������������������������������������������������������������������������� K"l@/`;-[3%Z1#Z1#Z0#Y/"X/![1#d/ `�������������*>d/X/"S*R(R(Q)Y+b+L  ���G���*���������������������������������������������������������������������� bk:)fC3^8(\4%\4%\4$[3$Z1#Z3%f5%D��� ��������0Gf2!Z2$U-T+T+T+T+T+V-^.b-H!]��������������������������������������������������������������������!Y+kF3a?/]7(]7']6'\6'\5&[5%a8'c/B��������:L!f5&\5&W/!V.V.V-V-V-V.V.W0"]3#l3! j�������������������������������������������������������������������8o?.gG6_;+_8)_8)^8)^7(^7(_:*i7&3������ IT%g8(^8)Z2$Y0!Y0"Y0"Y0"X0!X0!Y1!Y0![3$e4%E������������������������������������������������������������������<d2$kJ8b@0`;+`;+`:*`:*^9*^;,h<+W)���#��� \Z)h<,_;,\5&[3%[3%[3%[3$Z3$Z3$Z3$Z3$Z3%`7&b-D�������������������������������������������������������������������F!oH5fJ9`>,`=-`=-`=-`<,`<,b@.j6% m���  ga-j@.a?/]8(\7']7']7'\7'\6'\6&\6&\6&\6&^9)h5%2�������������������������������������������������������������������! cl<+jO=cC2a?/a@/b@/b@/b?.dB1l=,=���" }d1"jD3cC2`<,_;+_;*_:*_:*_:)_:)^9)^9)^9)_;+f:*S%������������������������������������������������������������������%_-oN=gK9cB1cB1cB1bA0bA0bD3hC2^- ��K( k7&jI7dF4a?.a>.a>.a>-a>-`=-`=-`=-`=,`<,`<,d@/h3# a��������������������������������������������������������������������7rD3jTAeF5eD3eD4eD4dD3dC3eE4i9)' / k9(jJ8fH7cA0bA0bA0bA0b@0b@0b@/a@0a?0a?/a?.cB2k<,@������������������������������������������������������������������Ii6'pXEhN<fG6fG6fG6fG6fG6fI8gA/S&=j;*jL9eJ8eE4eD3dD4dD4dD3dD3cD3cD2cD2cC2cC2cE4gE4a0 7������������������������������������������������������������������� M"sO<lWEgK8fJ8fJ8fJ8fJ8fJ8fL:e:)\'f:)iM:fL:eH7eI7eH7eH7fH7fH6fH6eH6eG6eG6eG5eH5gK9m<*0 ������������������������������������������������������������������% mpA/q_LjO=hL:hM:hM;hM;hM;hM;hO=gH6g@/hM;hN<hL9hK9hL9hL9hK9gK9gK9gK9fK9gJ8gJ8fJ8hL;mG5Y)��!������������������������������������������������������������������[*u[ImZHiM=iO=jO>jP>jP>jP>jP>jP>jR@jR@jP>jO=iO=iN=iN=iN=iN<iN<iN<iM;hM;gM;gL:gM:kO=j9( ]�������������������������������������������������������������������7tK9qbPjSAjR?kR@kR@kS@kSAkSAkSAkSAkSAkS@kS@kS@kR@kR@kR?jR?jR?jQ?jQ?jQ>jP>jP>hP=kTBrG7E��� ���������������������������������������������������������������Kk7'ueRn[IlTBlUClVClVDlVDmWDmWDmWDmWDmWDmWDlVDlVDlVClUClUClUCkUBkUBkTBkTAkSAkS@lWBoR?c0! 6������������������������������������������������������������������� N#w[IsgUmVFmWEmXFmXGnYGnYHoYHoYHoYHoYHoYHnYGnYGn\Jo\JnZHlXElWEmWElVDkVCkVCkUCkTBo[GrA/3��������������������������������������������������������������������" jsG5xo]p_Mo[Io[Jp\Jq\Kq\Kq]Lq]Lq]Lq]Lq]Lq\Lq\LscQpUCkG6p[Ip]LnYHnZHnYGnXFmXEmXEo[IrO>T'���������������������������������������������������������������������&].ziVukXq_Mq`Mr`Nr`Nr`NraOraOraOraOraOr`NrbPviWtWF_/ a+pYGqcPp]Ko]Jo]Jn\Im[Hm\Jq\Hm9' [��������������������������������������������������������������������=zS@zvcsdRscQscRscRtdRsdRsdRtdRtdRtdRsdRtfTwn[yVEU):l@0vlWrdRqaNp`Mp`Mp_Lo_KrcQsL:B��� ���������������������������������������������������������������Dn=-~ucxo\tfUugVugVugVuhVuhVuhVuhVuhVugVwl[zo^{SBS' �FZ,{gVwn\rcQrcPrbPqbOqaNrdRt]Ka/�0�������������������������������������������������������������������V)gU~|jvjZviYvjZwjZwjZwjZwjZwjZvjZvjYyo_zp_{Q@I#��� 7|ZI{yhthWreTrdSrdRqcQqcQtgUrF4. �������������������������������������������������������������������) qzM<~m{raym]zm]zn]zn]zn]zn]zn]zn]zm]|ue~q`zL:B���� 9n>-xhxrauhWtgWtgWtfUseTukXvXFU&��������������������������������������������������������������������)f7'yh{l}pa~pa~qa~qa}qa}qa}qa}qa}scyjm\uE4/x������� U(΂jX}kzk\xjYxjXxiXwhWxkXzjWm;* _��������������������������������������������������������������������>bQvuesdteueueueuetevg~olZn=, Q��������+ y}P>r~sb}m]|m\|k[{kYzkX~q_yP>;������������������������������������������������������������������� H{O>qvgwgwiwiwiwiwizmsgUi:) :���������(i8*|l~mp`p_~o^}n]}m\q`iVb0!1��������������������������������������������������������������������Y/!hWu{mzlzmzmzmrwdS_2$ (������������=dRzugrdrcqbp`qatcuG5+ ������������������������������������������������������������������������ ,N, UEwv|q|qww_OV.!�������������� PxI6xqvhtfserdqbxiaQW'����������������������������������������������������������������������������������� >]3%ΈbQuYII&������������������Y- Ύrb}ylwiwivguewhsdn<+ U��������������������������������������������������������������������������������������������+[h;.n^RA:��������������������, xZJt{nzmykwjwj~qVDA�������������������������������������������������������������������������������������������������������;|wK:|O?+`�������������������� 0o>.x}r|q|pznxm}rqcb1" 4������������������������������������������������������������������������������������������������������������  (�����������������������I%n_y~u~t}r|q|qv|K;$ s�������������������������������������������������������������������������������������������������������������������������������������������� ISCvwus~szhYQ&�������������������������������������������������������������������������������������������������������������������������������������������� c8*ԡyxxvy}rs@. R�������������������������������������������������������������������������������������������������������������������������������������������� k@0o`~zz^OA�����������������������������������������������������������������������������������������������������������������������������������������������������4oqD4qdwjc4%�*�������������������������������������������������������������������������������������������������������������������������������������������������������������� @!yK:|oSB. }������������������������������������������������������������������������������������������������������������������������������������������������������������������������ M(ZIn^T- ��� �������������������������������������������������������������������������������������������������������������������������������������������������������������������������������� 9`3# 8������������������������������������������������������������������������������������������������������������`MM��N��N��N���N���N���N���V���%���/���?M���J���J���?H���G���3�����$��������������������������������/����?�������0����`�����2XO�P>�P�P�P�P�?PTB(��� ���@���� ���������������������������������������������������������������������������������$ gV& ���2�����������������������������������������������������������������������������������������������������������W*_/ ])B{���1��������������������������������������������������������������������������������������������������9f4$S'O$Y(])<D�������������������������������������������������������������������������������������������� He4%Y. R'Q&P$V(^* ;�����������������������������������������������������������������������������������������Q(e9(U+T)S)Q'_+- ������������4���>������ ����������������������������������������������������������������" hk<+[3$W-V,U*[.S&���������� HT%M! ���9������������������������������������������������������������Y."e<,Y0"X/!X. W.!c1!!h��������XW'^0!Z+^*@r���,��������������������������������������������������6nC1^6'[3$[3$[2#e6&F �������� n^,]2$T+S*U,^/ _-:F�������������������������������������������@i=,fA1^7']6']6&`9(a1" 0����% e3$_6'W/ W.W.W.X. _5%g2!C��������������������������������������������N(nJ8`<-`9*_9*_;+l<+/����& g8(b;+[3$[2$Z2#Z2#Z2#[4%h7(3��������������������������������������������& pqI5dF4a>-a>-a=-hC0Q*���. k?,e@/]8(]8(]7(]7'\7'\7'd=+T*���������������������������������������������a5'nP?bB1bA0bA0dD2h=+s2oD2fE4`=-`=,`=,`<,`<,_;,b>-k=+ g��������������������������������������������5tS?fJ8dD4dD4dE4iD3E >pH7gH7bB1cB0cA0bA0bA0b@0cB1lF3M&��������������������������������������������9jB1nVBfI7fI7fI7fK9d;*Y(jG4hM:eG6eG6eG6eG5eF5eF5eG4jK8g:*A��������������������������������������������L&x[IjP>hM;hN<hN<hN<hI8jI7iO=hL:hL:hL:hL:gK:gK9fK9hN<rM87��������������������������������������������" hvS@o\IjP>kQ?kR@kR@kR@kSAkRBkQ?jQ?jP?jP?jP>jP>iO=hP>pUA]2$��������������������������������������������`9)xgTlVDlVCmWDmWEmWEmWEmWEmVDmYGmXFlUClUCkUBkTBkSAnYFrJ7! h��������������������������������������������:~eQqaOoZHp[Ip[Jp[Kp[Kp\Kp]JraOpVEmSBo\JnYGmXFlXDmZFw\HJ%���������������������������������������������6rO=zp]q`Mr`NraOraOsbPsbOrbPvjVv\IZ)kG7seRo^Ko]Kn\JsbOoF4C��������������������������������������������J)ucujXteTtfUtgVtgVtgUuhV|q^|^K:G#zkYsfTqaOpbNseSy^I=�������������������������������������������� ^~bP~vfwjYwkZwl[wl[wk[xn^vdzXG' q�Nx^NztbreUqdTqeSziWa8)��!�������������������������������������������e?1扁o|qb|p`}pa}pa}p`tdzixTD X����^9,ن{jxl\wiXwiXzn]xVD$ p��������������������������������������������;{knteufufvgzl~mvO>F����3tczh|m]{l[}o]mYM&�������������������������������������������#ZH|zlykzmtomF86���� ��/xUEvreqapaxgsK:�B������������������������������������������������/ctPB훋}}|q_:.����������T0$×v|oufsdxin]8������������������������������������������������������������5udVxT2'����������� Zrcylxjyk~ojA1��!������������������������������������������������������������������S3(K-#��������������� hD7ݟu|q|pveU&y��������������������������������������������������������������������������������������������8vvtxrV-!������������������������������������������������������������������������������������������� ]M{xXG�I������������������������������������������������������������������������������������������������2h{YJ򩕋zn;�����������������������������������������������������������������������������������������������������������;!fYrL>���������������������������������������������������������������������������������������������������������������������W3&-d����������������������������������������������������������������? 0 @����������?��?���������0`?(������0���� ���������������������������������������������������������������������H!@ X�������������������������������������������������������������������������������' sc1"Z(Y'4S��������������������������������������������������������������������\0"Z.R%R&g.7��������������������������������������������������������������������<g8(V*S)W+W'#�������  q���<�����������������������������������������������Gi;+]3$W- W. b1 % p�������;e/K! ���*��������������������������������������V/"g?.[3$[3#b6&M$������� D c3"V,[-`.H  o�������������������������������.qH4`9*]6'`9)d5% /���N&g:)X1!W. X. [2"o:(1�����������������������������#e>-gE3b;+a=,jA/-���(X/!i@.]6(\4%[4%\5%e;)T*��� ����������������������������K)rP<cB0b@0iD3M)k]7(kG4a=,`;+`;+_:*c>-g;*N���������������������������� [uP=iJ8eE4gG6d=-@a<-mM:dD2cC1cB1cB1dD2oG5<�������������������������������`<,sXDgK9gK:gN<e@/g@/kO=fI8fI8fI8fI7gI8nO=`8(������������������������������7z_KkS@jP?kR?jR?kS@lS@jR@jP>jP=iO=jP<kS?uP=% r�����������������������������;uUAt`LlVDmWEnWEnWFmXFo[IoZGlWElUClUClUCw\HP- ��������������������������������R4'nZq^Lq]Lq^Lq_Lr_MugUqVDgA0q_Ln[Im[IraMpL;:����������������������������' nlXwkZteTtfTtgUuhV}q_t\J=hI9ujWpaOrdR~cP:�������������������������������pRB{iwl\xl\xm]zp`ygwYI�Q?%wethWsgT|n[b>/�������������������������������I-$qwfscteyjmmL=��Go^vezkZr_|^L[���������������������������� @{i}pymtta@4�������pRD됅tqbscvcG(�����������������������������������7"vj\vQ3*��������=$x|nug~ouQA��*�������������������������������������������F/&seF,$���������9qc{nrvf, ������������������������������������������������������� ������������lNBڦyw{d@3�������������������������������������������������������������������# Qxm^�N������������������������������������������������������������������������ GpRG׬H(������������������������������������������������������������������������������������-]iE7�������������������������������������������������6����������?�?��@C(������ ���� �����������������������������������������������������9A P����������������������������������������������� Eb1!\*_* j��������������������������������������������R,a2"\.R&������% ���6����������������������������.k?-]3#c4#;���P&d1!S') ��������������������!f?.f>-i=-7��-X/"c7&Z1"g9)Z-�������������������I+ oL7gB0U1#! |^:)hA/_:)b<,i>, L���������������� VtQ?iJ7gF4R)aA0jK8eE4fF4qL9?#�������������������bC4tYFjP>kP=kO=kS?jO=jN<pUAfC4������������������9#~gQpYHnZGp]Kt^LmSAnZFpZHz\G(s���������������� �.zcQylYtfSxlY}mZT2'hM=tgS}kWW:,��������������������[F7Ήk|p`yhud5!;$ub|q^y`M:����������������+czwzi(b��qazkzg>&����������������������O=5}~p N����gODΚ|uuVH��� ���������������������������� ' 0����:*$q" c��������������������������������������������^H>^B7����������������������������������������������������$GuVK (����������������������������78#=�(�����������������?K@?��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/images/������������������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0017045�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/images/eclipse_logo_colour.png�������������������������������������������0000664�0000000�0000000�00000011134�14502137606�0023602�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������/���(&���sBIT|d��� pHYs����Oe���tEXtSoftware�www.inkscape.org<���tEXtTitle�EF_GRY_OR_svgF l��IDATx|TՕI$`&EiTY+-բUiZ~ڊBVV׶J[. RKw-njT P@ wL$3$�|=fs9>Y2nVLP  NA^!OfODrQjWnw*\ Y` g-#c-XkXu]TF_.4qqd2w;ٮv\<c?x='N6꒙"c`H(D.zzjHA{�.Q|in黎O~ pѩ5X&P+EFFXVZV_ٰ6B[#j\2n1vV} :I~ pKc} #sҼ�4.9wu-"=Ьx'ӱN|�ƘFͪC^=p} **&֌<s,L3f{>rgqlCyӟ'w7fR\+ŽF'u?jL; X0EWYXlg+**mp8޸\Q^Mm~1}>]`н9U_VP0xs=w4W?\AP|yM^/׬Y^ϔ ?Wʳ֘޿ySm>w|y4_Ijl$0[PLolDVE/l+kOy t ]7ϳ-d29DҜ^ %gI�QKo< %>M|XcQtzAVZ/UugF"- 2SbEDHdT7={v_!i =}\{pa|ӥ`{SB{L ӧOs~]gV,`-+DeAUhΎx&\7X E WAb #>Aar2ߙʎ9EKDI#zgu"b.PПMiz@+}_bF󁖀hQiQfY$ ~Fan{ L;L4iH~ :aȑk!>DԹ6\si0z1 *vfz,X["�< X6K. 29] &PWWԹL�Q^ϥv8Hke@%ڦk׮= ,///_3p$(H4-�W(vM@vV$[d8~eԩӤk֬ݕͪ/ǢьwO+87&z[o}:P1wZLGrR&Me>p5'LQ#ox FشiS TG~pMݩsFdPېa~KL)?u X g ]yf9;:+ uuz̙3=o&dxe,7 X6FU`U y0u5jga#pˤFxREEE#zEsbZiݻE(hv�H$2u~HEc!w@]].W?uΥ9Ș0"]eo+%`p0Pt"VBݘ\V^ ǏC>^-(h|9i?"߇|`|Ph 0 ms<Eʢh-P & :] |qXqw{#+ڹ=^Sc !HUrZ|k8v{^vOm+$?Y~AN +**|ͥ#y`htwP~0Y7Z8Ya-Y|B*F%Ǖ"ʼnV[w _ZCR^QQQf͚o8=?Zcئu;Ӛh l>}wPZ'&gh-ձX]lj @ܭO/*ߏSH!}6Vysvߑ%جYȌp$vq4I(4oGmmmf}?KAV3@}'1o|۩ uWG"]YQ^>nM}}N)E׆#w[K(4 |沲2<״_KQx[[ST>|n rpX& m܎L6( ~s̫S]L:;`p *O!Luݡ[Gn m[~_%_b&rS~F;e=79F m3i ~]%gao<z^E;5LE?nX&ˏ7$Wȯ$ W 1 Ĩ>٩IqFzd6hx<twv#Cn0@G"5"Շޜh>p)sź{H71i4bKVx;M�+[H[n EliaGsq1+ED"k\w9X{ %SE/_>l*d)2~!#77܈S,#]=b4F D{PEX&wрN["؝ڲ')2.͚ |2 T.] wt"J-9d"BU^: =^QjJmiWW>c۟FPkBds9QH$`p1@<~_F"^n7=EN9坑?ޣCM[S_[:eeey~<a >Q 0� 0�h`}:p%UۻִܳtU}P>%3ThAety㒒K(|Bw^N RoM[Ӳ1[U<NҠƧ٫y[ugl jȚn\ 1OʠʭjKK`{ l_2Ό.7ebEt ]]s}8u oҶ!4d\#bt2Na i>nGqJ*- DpT5+(=Y�&ŘJy,ӖEA όԇg-Dky6 u`D[Q0<,R; ӟȅٶa Le-ྎrީ"W3,glMJOx7k1xhuEUod\bLU!tɸ2okn2>dlD؋ޢN7&%Hj9o͙*Ps?+r+#e|V,\ȓWiʮ�e;xnZZ5Rn1%SEEV�d3hA+nseFa% ќA{Uݿ VW~Vo'dv[&:_kj׀g{GD7Qx_LW9N_u<QXkJQu+(juhŒyʭxgQ{/m}֙&~}"Z埻}iZ75b/7 ]"xuڂwvW=pҲitRbTր�h]AF)j!-I#%J{]�(JӒqB8X뀕 k7>5'U.9?Xy@=.+YՁN<k!ujҒm7ߖq0l#DsX52SR*jS{DȪcv@b�v1 V͌is@.񫥩e?5¯Tdxwv{وNAf^�2S(`[ 3Grnٶypkw @f\΢~x&QX`x#Jt݉k@^Ďe.PWrNQs>{rsG~::NSq۶}K 4֔~*%WEnIbă+Ră%GTO(}9Xΐ i)] I V+uВ0/~Ui�#,~{-YŁ1"Mɒͥ.նZog'ct5/2^]Z;P6*QAWī"rT~ ÐZ;6'?Qh{NVK5%rF9NKNtޜ+^-$}>/#ܦ@nޑ=j5%?%??%/J?C6τ@÷K'v9QO_VyMɜ' "ŴDe#^9+[YT`N>nUI7"ס<x6McfU[i@ye-c/OBx!/qƼ}|*EQe*<#*"z Oj˂? \l7ckك-nwd2L O!뮐-v9p1 ����IENDB`������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/images/github-icon.jpg���������������������������������������������������0000664�0000000�0000000�00000003502�14502137606�0021757�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��� ��� ���szz���tEXtSoftware�Adobe ImageReadyqe<��$iTXtXML:com.adobe.xmp�����<?xpacket begin="" id="W5M0MpCehiHzreSzNTczkc9d"?> <x:xmpmeta xmlns:x="adobe:ns:meta/" x:xmptk="Adobe XMP Core 5.3-c011 66.145661, 2012/02/06-14:56:27 "> <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"> <rdf:Description rdf:about="" xmlns:xmp="http://ns.adobe.com/xap/1.0/" xmlns:xmpMM="http://ns.adobe.com/xap/1.0/mm/" xmlns:stRef="http://ns.adobe.com/xap/1.0/sType/ResourceRef#" xmp:CreatorTool="Adobe Photoshop CS6 (Macintosh)" xmpMM:InstanceID="xmp.iid:7A2504F952DE11E29313D3A8D5322605" xmpMM:DocumentID="xmp.did:7A2504FA52DE11E29313D3A8D5322605"> <xmpMM:DerivedFrom stRef:instanceID="xmp.iid:7A2504F752DE11E29313D3A8D5322605" stRef:documentID="xmp.did:7A2504F852DE11E29313D3A8D5322605"/> </rdf:Description> </rdf:RDF> </x:xmpmeta> <?xpacket end="r"?>Z��IDATxڬiHUAǯWʹR+@I쑕i ET*CZ(CEBEEч[L*M[ [̥0>_wx3Ϝ3% 6lZO0@ܠA 4ȆD Sൿ`)覴�Ahd[ H.< �i,pcOƁ`vq42eП{<2|`?GV2wl?Ŭ�9G i�y'_8I&|7X�`5E pw(�%`#QI|8us`ҹlo2?7egC6 C`{6[0;ngJP(3ymý4:,+tD$%xԔv@H-򷇌-r8nYrPK r6s8XgF{)AڑVɳ,EB>7T.`kqP/S/6O{酃t6$ C (E@+.-j_W,L{+1کQ9mXn2hU^|.}Z{2WLlkp>ŀr}"oS8 d])Le`DeR &k*Fw3!{| _Z5Q,9҅ `6%֪IiT (`6t7݀I<#IiWSV̬%"c`'&/^5|[c[ZbflErHfn♖g{[Zn  Ɔ*e tf(f|E0-_^>=`s>NSyH˻2N&Яcˢṟ<nn X�Bna����IENDB`����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/images/iot-logo.png������������������������������������������������������0000664�0000000�0000000�00000004345�14502137606�0021312�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���]���S���{ ���tEXtSoftware�Adobe ImageReadyqe<��IDATxqHw"Xerw�l"0D]�#@`i{ݴGJe=#_|cnV]oQuWgw�d>{k�L3sixd]Bo�* `Gj[in״$J}_4?G<tHxĀrIpkl.#{<{*n&%p.W5 ^w/ؚi|wBs{_#w9A7o#a0+4'SM(,3߯CӡAe\w?omz r6L \Aц(Eʰa3y#)B}`0W"9a(ண,VMͰ[C G�/8!(* tRVf:cK swD|P[8%srZwଙI3`'lY,"._3 ? :B ,;`cD4iF97|kk騭70eu ?#þl%bUPp; $xZro_a-pe |a*ɚ/ WO1un[ցl5}/AOb 02Z[ȓgi -LӼT[wī :xߟ&XQþ}:N OAyGSoA!?xj O檠|�u$  Wr?]<t4)aK jN %]5Pr(m!Feh%o-u8|Y߯dqf&5LԿ>[(}2kH2/@{SfՎ7&}6vJ\;QТSB i5q|"*Cܲ 0I*PZ`r_-i.iyळD]C磊CCC7mMAKꨣ >^rE%b\tS<ܰLpG;9�ҶW]gFY6pl,PڢˋC7>(eeBMwQmde{urY{U-*:VCe%,|.=k= Bu <Cp|Z^/+QUJSDfw�Bmס/xhMP{0IS&:<<OU6,uP˗ېgѹj LsZT>(ـlTyxC\D*3[4 |E^z< :f㟥TnP&O1Coj̓*NQ&eԣY31]ϔlݹ3^CQ*o4ª&N<EX0QDl}Kyܱ!9i!P̡˧/{l<QԻqXfI8w W,S|~ Ģ4gK1b|3t\z5>m.TbA>*B(Lzy8}5[#AG02q|<�a' i6iZʖ\Kwm򸙙縜kJ(]<{̭~3q>0Ιk<fmX9htI;4',C3SOx<x"3&D&Y`n&P^\N5hcFy'LK1 ܝ9~b9q!?G<ˉ$nډ(Y1^r,FCdDA'b6(ϡ)yz%ړ:y 7.*`Sѷ96.)e o9vIY7(5E?uTR#~bY`9s+GeDqE=S~yW9bÓrh}t<pyex(TbZOw~DZW2`e=! 1Y.#tns3ST15@tJpt})H"ca,B=C6%8VS.im 2d9ȜCe09^9ä'�$T6˽����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/images/mosquitto-logo-47.png���������������������������������������������0000664�0000000�0000000�00000016201�14502137606�0023005�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������/���J���sBIT|d��� pHYs�� �� [B���tEXtSoftware�www.inkscape.org<��IDATx{xTչ? $֞z9U!+* ѶVk{<Vk- HkmjsIPx$U쯶"(H #3{3!܄pyZk;{F3QO-4_]?HT1LXn;fg QƶymI[]VT,B[T#c@ԃ t*nOU'iT=~H$k@ajLK q yAb*m"#Mpو˛Wްj?$Qi31sW^=gȻVRj5�('QYs~STe{%(SwLAT+z;YI.y>mE\/�.~|կ7߸MQTk9WOk{U7<yAJ{GN<sǜP>b[o5Y qM $*gj~Nf0 P^ejx\45=vF@ILQEl^Pt$*kI/:*<2ԜXY[{TxQoOD+Mګ>5X8o&^pTUF@<hS Ǝ@HƦnܔaLUuG~c*;Exmj!vs80S ;-[k@orڶLʯ1xgz[a2z=Up0.JT\)4Uvڏ-=H1Ygw_BZ @ɢ Lka ν5RڐfT=d_3Y2flb32g["ى*;"7N`>vCoy$wG؝(p(?F'o*7:JW}xiDXFWXI{}?1k~#3^i_#SXV;UN}6\Aǒ+~){{xD=(T <аnn"("7~\RiUD<(Iܨp?3“C2 EitN&*ft*2{S.4ى W ܧ twl |IMu8;ҹ2QxQn;eI{:d$^NT QKU Dy!]ğ|ѩ- |?k#{v<(Gy>GQ-9?dsh|MgV|G3aǞ5jVGavR(|{'G ~/?&=֦ !;p6$bu2 ϒT]K57 LYK!04g;}*Yo/5.^/P~-l/*;/7_dyHv'#ΰ̠-I֜+W"vҤ‘N[Mu1Us&#|8i�mT&*(HMkw {owV"L?NsW+Ps Px{kwi@HsJ)0EynY֭va^onevC\tHΞn^s�4QNC13Lx\4"i6clھ L�N4sWF5c><0UWm;tH}P pT(_vk UDEMtE̋vPf'"/s`PS^n?v{ K`{# /~]6np3a8Ԧ63+{(@IEe( |qRa]HoӢ p(=/ ;+%YIA}}*=N-8чI۳..jZ<1 +G[?eGuzTwJo(}+SsLC sH@;x zrsQZ1�TK7Y (U&꿜Hڟ?mG2<Ue#o;&`k(EJT[@"ДYQ}a(GJ2Sz ŠuhOܝ>O#qv/qoi/�wA_ŏw.J; | L\d|Xn#9ŋXtRڥYoҋ%O|FMi=5IkE ] 7D6tL!˧q!,\]<d׿WKny `v]D;ڱ81Z8FZkwι3&$iܜ]E1+E>LP! TT<)_S"i碻%�pUC9bfP�v[xֶzqw`cSj:葨{G0ҧA:Pkd 6c�{O2ׅ-3M(4U׳,\o�\U(Z῜U6(]^p6F^qlw@0^|b# ]fxx;N`yLFSѺҊ�SL1ykV\V6j"s#qEZhA^U^<t+<%LK|O_\Ch@/r F3eYŬnﻊP՘2ERQ 7τ;`_P싡UCKퟡc^Srbz:VDS`@|5%0�(Lol�UL>ei\!CG^7 dw wQT[:i_Ht(X-4ԛI{o.L*~@VXP0/5<PT?$^fV[JiS[�>x)G$\B2}=lyM g<5P^:u»# 4mĜAsW=O_M g,'< KssXqV H$0ڥmᆆ[644 G24?K+~"WY?2 GdșCk6׷qY]S</MX|= аn| 3Eӡ}@3ױB)CT<"F:W jmN1WSQzsi v#d/h=2W{ZNCmo q_ݎD&c%QiT>X]\+�[Zc |R"NIN-y?z-签 jg(26R&Ttq߁\aLX58! jcF(o,o�ŨwFpjX5N~5Y, 6&(hEL�6f=:L|E}_™<2HcOI<]M;μg DiۅF(T9?RH9r]f"YS}TA >Ddl:e-"ZZ34%ID3䄁r0^V"#>cu=q x/a`l>8EA!;;|w:^<˄'PJ]}DH[:?>@4٦eW,ϑ!d:>Eh:N ynjmzI|4zԷϪ|vǧp?+W+)Im1>br*]~%ܲxí3pߕuB/T`M`EYQ4+~xAW+ywd'+*`4'=x= -kfk9/i%A)7uUO~v`faC&� ?u"iGnu撣8)s$m"9ο涥(ܒtC$.i$;fpO>8y~CmH`! \\z ouƙ:L/rhۗZgYٕUi;UCvuOTBEWAQMy;)Qh_>:e \Ll}/7 rXF6t7h;jf/!gK>EC}Wwooՠ#!*cDU\{>/foad& }#{{􊙶e>]1^l)ӫeO@{UNivg}THu>yaÆ *qC2;klG&Ѿlx$L'wBI<m7C8#Ҷ2TR#`ڹSsǁ_Eotc}o:wut =,<Æܳ KdX,ܐip"2WA`Rj I@[p> ! Ze{=+v',x|=i>[+noO߾ǧSK^`Lę o[= 仂`1^%fx-m+s_ES},Rޫ}x,}h3uQkS\D %*dww הUo' jcλQR{ȞǶ)Jș:FSf,#G6`CĄKSEIҎC 8\ښsKv[䃓sa%"SQ0raaks签߼*e;c<&B:GUxUƔ]|kn;rRή4N r:Q{&Ѫc6(Y ,pܫM6*SSL(?2F/,,M>x)O7@GKIYxeV~;O|6PΨ2Yl vTXۿnWefNJ{OIլ@3ǖV qE:Ɩ&颺Paz.r?`4ѳHpJ;+[s:mJ-mO~o~,xt̎g)> L4[*tf"i5z 㨪[$k><d?#S@C#&dmdM3^*We. v1\NvFҤu:z {Wč[Uc =bH6; ђlW6 6S>�^O$DnAv+z[Y嬏),&P.[HWI􁏄[zuTN__E~塃</|1$#DnKL(hev :Vw4-^$w i|HDҶļAgP`xkL* d69's~ -t f/ՐSx{ 8KKU$+p|b=½Р mo´e>R.i=lHpvz/9ZF ƍp/:Sw[44ؔRϝyd?|¨WA}YSԬ(xm3irPʛtn*"\ 7m F*6!Ji)fYݠ)͆u@ %5A<Fj lWǥv 2sٕԿj͸dg[Y_"/k@ y{<A?SjVJN^Qju6:r?,D+=Uϗoml=xvgD^l܈.@Mc}}~I(M @,D7?>ܟnPZY3YTycEؗN<UQU~FH71Fcڴ~+f 7A(c1/b SE ~BMG R&VӦ[vsSPTƧn ؟EebaCθcCS|NHZTY;K2{֯h''ȇu\Ŀئ”.GvRk|=Y|g moWb8MyCpJc �%U>.SyWc ObxO9d&fa;{מ</"zsWx'Q<*fd oM}YKf�v'q_O71(g(C1āuŖ/]8}։J{HTv_X);efv yJ' AHUx_f]Z&1FNH9ёFH 󈿐_>P~=,l)RtZ ` /IL60V8<s)uy+Pf7ӆ#lykFuyCnS#]7`^9HU\_0߃7-<䠹6vy?)\9ۼg KjIXI\TooahǙTve@>|X{TEl.%si6b> 3=p4`> ;X| 5m"riSY~n.ag"eooKJi@zp`wFO6�v$N[x{Oۆ/᛹gv'U>mQ؆l[H.+]DH V-VUdo>w҃nsƜIĚF!d`WBM3^2_)ݣal@C݃~Zu2'/R|BRA7D9^S dVM!$iNfej3Di-S_mw{3DEˎXڕ 7=^1+04\gUMuĩHfXٷQ ehvb\#_RJ7GEި@hMRROTSgP3S]2Ji˻@^kxTXr|UBzL gh{pQoc[&;krN [mΐ>JMу q.كӊ'wЃ }{ܝ+kw4=AwRτ2B=LH)LzЃ~z|xb����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/images/mosquitto-logo.png������������������������������������������������0000664�0000000�0000000�00000017731�14502137606�0022566�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR�����4���a6T���sBIT|d��� pHYs�� �� =ŋC���tEXtSoftware�www.inkscape.org<��VIDATxy| IX"T܊ץ.hsClmm~?Om]�7rQ@Ukmn! j[T_WSKM<?nɆ `Wy枙lG c_ѫj޴[PVy.T^זtOE{#' ,k\Wcv?k'ߕےsܿ" h9 e%La+L^Qm} DϞ7~G(#6KcYdbr�ClX-1΢pIB� esGƲb/E;PT ,)黍DQ9R"8 ᶓXx{r>IboF (,5ge 7)<\3; WK)nRR߼n']Y콰nϷ� ?4Ԙ [PNc#:j,`AGFwsP] J*ᔆfA-dϝHmYbƉ%7;+s'{n͞"CY-1f{i稺'zbqR05Ѽsn7\Q/`{nalq9pxIž230YOq M6ZR],-_欰1S&*绀N5#~cF<%� r)l~ng^ m:ױ뿁3'S>&${�@D\n*2$oP΃.y_GU_k#kg?�𔵈0ʼ^ۜ$Oq? © ?缊⓰.I>K+VO] Pb>#E:B- 4 ߪ68Zj.v0[DKI^l-n3޶sNHpze̘Ep&}V]Fx~y>uydS�z 5 oY4fr'N<zW3\Tv[+ʼ \VXZuO?X{ɮ3O!RX(UKbO+ѹ5^sp蟮]0KSTj9ʵeE%_UN,Puv4ЩQXbxiSMp7]´s]gN4<1%f<M9u0k@9D`\P ͒uqP`抪+RRs&^`]x4 ,!\UTZq~{x- 0ޕL^%_<fG]Y1k̸x9aQ=Ӯ@@-JnvG&4R}N_c2Br<G K a2ZjNsխP/O+t*2rEy0f~/p~%!дWHI2UZS/SL" F!VqONם办#6qyيؿZVu?{[�Req B"G\/4˗(z"(9\/cs䷝LB(eP�Rx*כM1 IDWu.^q: 9}qEyGWjQW|^Nd'2oHB`;4Tf܄_ jּSÄg |QPrfX<:Z�^wb9rSmz8=py5:@4fAr)K(^Q_cҫ2s:F#.ZL|WnK^[ xe{v}9|][]4oaF}I[WJUuu&�%x[xMŸTI.H]`+0(H1í]q+ AE4fTL˽|4fV1AKW4f6d 3ѳ !w#7nWe/kL0VA'{O[cmc)[SHӵA[]+kmbo{-xbE|[Xru3 8-i/yNBA%f ӵ�WXE|Fuu ] �a,b),1 : @4f/3]}�V е*sl^vZ"hAC8,�,^t^=fGK\Ag!�po cfi &{K 9|˷;kNn&xs"kU, yA(U\,hsҪٛjx ȕښ\wv{V,(6CĂ-5Wz;  ʴh9;g(puo !^TvefK[zĶ;*]uG.϶&kiNI䰼*DuHk%O9b\ϽKBK\%lϽ'fs6o|D9|�`Pn<<kUoG(>\UU4f+|>8uvԖ=G\+ =!|@$n �Tzp򢘩@{pxRVЗ}Pdul.E+9I]N, $;o}ۅ:WK=tc3?W3eyڼ W�/^WoXG ט-'r8TF\k̨AmC r9:YXj\B(ʤ͈H;d;8k7~щqc1v-3GP,\r|%\wrB>Y=Vn"Q}<Z)U8U\TjQ…,Bkq9=L_sbuWAr?盱-Ro}U& Pb>~ץgq܋=뺜WWcZS[f9UV+2k̋XpKOo]eR R�0ƥT O/wm; <jh|@R$g^+3hCst7N|WiKׁҨch}8x?"su开8z`eaSW9VjeqsVUf0! ‚^�7'n=e!0F-ǜ0`~` KzuLN^�Dc&C<H."[4xy Px>ZR >E]OCgc~nCvs䇩[ŗ2Jգ3BHG <|I3GփNR Ee˿j&'??=͑x"3 ۿp0ho3Ha˶+r_h :q:"w*ׇ4漠*[ aLX*{1c7y�̎Z33L�9,W7^+U8 zc6:(fdA\,c�}Q\fT` !FvˢɜK؜k)>z1ks]}4 H#(|̢BJ ג_\ l9*wynٶ6eʈm&u~"ĺ`vY=XM+ܜPvWįO2umңR| J]&_~Ūۮ's+ '6NQҎv]šRvPs=@Շ:sLڼ,mVY#�wK?#S V~ @}휿�E9盧K' y޻>$uZeD0:X}y|&pWg꿳 "B6Kfo!n *dD$|S5<aפ5" #%ë7 S>&�$~hn`=q!�X60f5J g"S’|6ҴBX+k;+,.>g!Iҹt9)d.i;犪+yysO-fネgT^۝y GKKmBkXX]$%Rn]2VfE޲y-fx5TE ֡. cf7;}4WB�=/)�r_ v`3TdKT,\7sޏ:7ӸE%UK;">/gS!%D)f;'[&kÑ@rXGĶwvC?D&v%3}ڇLDz-Lx+R-_v{~<YܤPiomq"td7 n P>ɇ Lc' !c-}Xcm> K-ߩF*5Kjiq5ouځ53ԵyԒz/3US,dGk[w:(gفFzBV,8Nl9X)qJG(t;!5okxkǿ ƢY$cAGNh>E+4{UׯxZ _lVKKMT[D,]r'ϣtW܆6W#,a*9AE3!|lWơ3p7y 4p^:6 q/BBֶ1U4h:߄$BZ=1KTdo\X@>DzEe %ZcEޙNw txBoL8{l[4mqڏP-q֤͐K+�yY=QZc$)[Vm<x^/ ) Yx=f땋sZd _xL&Flv#@o*Ri(%�4#iSqdV[ B͠{aaϣڇJ*"T4Ki7En7<a cǗχ/pgQIԿ=? ue'< *oc"㡕V5Gn:vH>xy XQ!zWw'RM,qPHզ<+:XX|[x}iQis (69 #XZfebp / )2Ռ=uTŰD^K"=Ef²`EEK) [Y Q l[fO{/mL`R_sZ_XeHջlN3^j]苖՚9owPa&,,z*[Ue:3i$'s/"ׄDX-5'ޘ{pQ\?En z]\l"-<P?G}FKxT;:*7YEEa_h5M3G:kѪ~kFi"-E/U\4[@r%-5wh ;a[aa <ݘ61N6wLbJ$ܹlTL"2]~_kͨo~.}Ko<\,i)fh*7a^dϸ'*MT| ~^v'PSEV4fEcfEbMC�᥏l Kb(2CwΛјy53+Ie7 oi=ÄXл-a+Ԩ.h?JH䢰'fc__lݲ53h qRB#'i@:\)fj]; P~6E{ -}Fto,sDzTOXՙD^$o' ֊ ufY=J)}KSꫮM+>\{Ӏ+Q}"uSK2[s ؇u؈ˀNʥ{yӼ&a: / =2r;!jbGb% C^Uud?tSޛiJt^g@}gam:ʴ*>QD?KbkzFc&\Z5{ms^:aXw6R;rebVwL KdS_cF_n?:to{/j6$ KƫLF:es^x !w~toB 7[gFWDuOZ%@ɲPWgmύW-۞M4XKg)~?S>+3&$!V UWeZjw]3;( skB)A𖊜?"ri+I_�P+< A~J{ 'St2/l{|doon@' 5ZE= *(.nt*�9RUxk,JI_^=*3<kP QmhU5rnw^|$BCO�DTc:5'wS?HCmhyIGli23ZjF|/p SjM菿|yx&Zb@ V.NuQYT㾀)0笚8mN,)3^XRKA `/}°{yv۵-2yv^wWk$u\ǰxuG.2tB*ȒOU^lY~{DtzHn ,O6Ɵy6H _ġl]ȳ#�5Lkٚ,,j7_3K4ݣz*`BQIDܘ*I2e.miBfZBl쿸[5WTQ;(ދxmy3YSGQ'^U.'BҦhiS*7Ԛ0V5A iPXmPuCh\hsH^k[ @-7Z0ڤy>{mZ֪gaPБbCQZ6=LY:3 KcZ02WXCMcJlK9"�ĬF22TwُI~l?Wh } bVnS)AOk9l=Ӿnod!~qELSKґY=h> H,#pY *rE"/TqH`` g;MQZgNϭC!u&KdP [V=ExdPA>Ёz4@UչN\B2O7r=Y[<*fo nFE~eA* |m @1vb/97w$^5f.$W->u.;Ģ3=`ͅWܿFYIG1> ʴ~]I'r\6"gAJ\&YTvp xcӐdYЁ .x\"#Z ,+2"˔oDDˬ1P Nz'`}6y14YePYcn f" bkE mw]foeB�ueFV|  P-dO%B?eU7S5#Q剁]f]5di9:p fh,B Pk~޵.Z]I![V/{Q{th6q,. .׶*{FK.ޠ]uM+H9l&pcGJC94Q˒Y>ͳFNh5=G^dA]ʿAiݴ5" Hif@lwHVd^!nD",>9@pqVi>? [����IENDB`���������������������������������������mosquitto-2.0.18/www/files/images/mosquitto-text-side-28.png����������������������������������������0000664�0000000�0000000�00000041473�14502137606�0023763�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��,���p���=+L���sBIT|d��� pHYs����2ZM���tEXtSoftware�www.inkscape.org<�� �IDATxy|e3s6]b),("V)mnJRAQPDEE@hּiP*(sinJY*eQT,-tK;GҒ6w&7M|G'w9ssp8p8p8p8p8p8p8W9ى8%t,{dy͔G -p䋼9,cn.l^Qe/4,H >!^tNp8vⰌ |%8˲^hX5kn}Q8e/4Ms8#/a LuVv/{W<؎ &tqGz'w]p;,f:AGot}<ݐzw)`KAOذ1'xX_hp8yEzrrIanyOrZ歞hc\> c@r87Ò,~4s8|>5˺G>9-W--<ܜ4|;:t49+�J/aTp8[ 65GxBciyK< usSJk.ΧMpr~.0+ܕ7+Tyk'Y^30U$W9p8]gyg �JP2P;Mu懹ر'QVfC1�l�>1D1G7c6Bp8z\?!:o噬W|x$ZOے܊N: M}Lg ;TDp8vʚo JיCNHb,}}eɝ_kk̘*32(+0c+g-l(4'r?v4˜:<p8q?!%[88QGrXFO֗X7W)EJcX qFCYǮݕ0‡b sągd;XZaf*\q_e@8ŸGw(|zX8,9vzb9,r3/G q:e.@ٹfFaSYE8nh>y< -&HYId|EoJU;:G.$+ (,6NUǙRUgl:eN4-$+/\vFd؊D87bثL ;p4; F a^. ׽h -a @CɈ&nEǔU(؝~*'Œ'3$znOS̊'1+?!0&M=qW.pҊ묎Mcy1~Ms0p8r&+\ 9|icꈔ|mRfmLB{{6qjziҘ2?N~cm`9y(|gޛ c<U*s:G9mi&*؃9,"Qz粈1BͿI=ol1e~rI@leykbmƳtnc`a5tKp8zg7%=]Pe$`t75ԚV| ԈyTUo;1bC*GPk< 6Fkc~p8q( smc%a.8ud0. efS}kɸcn."FKp4OL6(}epʛ>o�-s(]UN?jM󧬳 3i/]oMqZ~_,?~QwVʪ^vmSd1QCƢyiU~1υ'3;aeiii˶:d5(emID3"YYDŏ2C 83^+&3Y  |4hw c'JMc P ={oذ 9[(72\T[8vM:9,ehmee&|~c{s9&< Omns zgt9! 5s_>suӞ�:H(hh5B6F q8\IכEm:?U'`Yg.ۤ١|uety6jqÆ߆H8% 4;XOiE)]d 0WG͑XO(5|։nX%&۳p䌿oG9-<8}J'EE ̚ɟWEFV frPBI-GOHל<]'+kβ`2ޅg@V']oD8>g;p8ݡd7<xmnl*_h<{ceeL9 cd}k^~QکƔVI(Q4(Ih ʚ`;Jϛ垈Ό!i㓢,75ՙm~)JLYS}DsG<qX|sJzѼkzGWxZM4+Xc�5_^j0?BZb6 DPlm|f͑#دe>mp8qeۧhv{Uec D䮈re-9_ELV 5m5W{ݙz{^_u8j2ՙǔϼ+Wuupqͥ^Kaxg�ĵ(+3}Cf?P Qiր oQ†P2 HEiQ/+/{/44k%׎dxS\�^lTU}s=7�N3%<OսU(Tw|tesxm|g{(t(Wz`?۠_ļO vHDsP /你m!&Hךcgꝝɸ٬5p[UOQL:e&&+j)XWw lqL׼6`Lg/XT E.NUn$gXXN~if@QBN36P]Eʪ̠LFDt65 sq+rw\t &G5߭Ihj4K>Xΐ_G`o^IrxCm%fxS\P$_Et`QBUGx@|~Xk}ɺ)~X~Y DlsoGcsʦ|6v9Jn[nS1=6V2izduvm=b䜀 gJ i-<6 "t'z+i((NB\Ir^گB*q; Ky_kӵfYOmk:,o"./<3y"ǔܻPZV�[Xo~vlxq#>f<Uxp'St2YnX{=2G,+3Pcy=>nߢ ؊kȼg*h7[+ nr\ \ ܖ(y)0DW<:+eur^wlE (j  <•Mu/a| C<j5o/1>N+T ct*Zf"YaN1_)390FBoo˸.,%-"SF^}oHvkz3Z& (cgFy=r3Ί3J+jQ@`>zjh0km؁T XJVXwDə<5ʄ>-p{f}qV�VW Jr}7ؔVTfNϜ)V%w׎?R g># sGm\sA<b"g`(*uSisጩ2C@_ *@%YiJ;)%ITec 9M4C./JK3(#@0PZݠzJI9Y)sM[_(*Z(u?X 8z\*]1^8|ϲ Oa/υrmSJ2C^UK!<1Xኢ-Fra~I|HxE kg SF<a]F7V֜gyUqGg]`EGb{jU^Zxկ./]m=kx=bT.xbY[[{F|\{G5I3)s;@>`=CU2~b뺌jЛ>tXѷ6%1`d8W<EO9T?&+ zޗU=/{bpeaIno M9v;BOOsňno8vk$YYs.3ux7 =5'cO[p)꫷(SGD`8K+k"Y^MUƃ6[jYm'zZ#NYo</Ah-oO _2_^.C['y7RgtdIJ,ޗ r8\,?͟Y0L4x;m:|p^Fofau%Os)#1Ynz+WݦOOg"<翈ȽC&y6]hL{D":h֕M4Ge*/0enlq . '%́a^ڬj񃈊;<iʚjSc'M;Yn z˩"hDN2'{ L.'hw1@E毛2]}1^ɳ 4; Fb ךup)wc<NF|vB.CW"EҘ~<`@R7j ,"VQ~6eA󞑿SBAx~6R+Jf|~v̖E(LX}ުr~=Xܺr9gFN%2�4}r"$cfjޢ@UkUN?mkߎ m'ʦ_|۞N^NיP}1Uf]UU=f+h .aL[N ާ[G8S &#B}`?`.4|laeX2]H7œwpo;o]!>ؔaݔ޴+= F!!YYsJ;;O;U;oCXD Oed<,hiBtj4߼f;=!�6&jS/͖?49 Jh_۶R(^nKъ2HC=>."Jm nW8S 7VT.%w5KݴQX[nA.q֡r?X IU*=?ez핀N薳r{Q!n-q4=8z^Gvlw n/J&b ;/[]|_!n�d �#%0C+U]n湽NB쏈'pA?n=]e|O4;vL̽[3U=8<YRQAC)5H?x Pzv-'feеʡ1f)1 [n[ToްxS!*Ø;JRfȉ w/`qY-3T5 ,369ɔНXtݴ' V7ȶ|^:{f�'GGJ){xj4-)99;+�'fi.ﻬUOSVsI68`o2Q/�eݘꚩb lj{<u_KVKQns~Μ=߱S]igTCvIי ر/ܣAf\elOikCt^Iyo/rH!tV*ފ<g@Z_QIr*A6VGJyz^ 8Y=E9�t*+|_/XvSޒrsjŪ+>,7tIwmԢө;,xwзq \2&h%k|De26CM#\{n!bp=]h^W䬦T6^&݅5j,e70v;?y@awu9M }Wy+V?~~ȓ>pXAp-pv9vQM2c <*巴fZ(5)`Ri%DZ"t/ OK yBe*loR@ZצEr7'3xPP|ND<9uS',S$cA~e?ֶdyj[Ė'j�H[,WJGlr wD)Yf@l){x };^LY zs."mI";oEcө_yx8+�%�T:Y/EVЬW?dpS>8s,At|@SӚ~b4mcS2fQ:߂G8M, /ka35!!z\a#m\v!Ha*maʏ7{Z4߼&a&_h"Y#l:k~vy8bL�+D+(S;^$^,ED {RN Xg>%ئ<\_JVD֛)3;;*6 4[7J.~AiKk2j!`ᮦn�Z\{Br֫O}m4~3 Xc' Ke58r?O;G>b][&5UOhp_Xi+ [HHhMEêvczEHHNbA[nʩqT (_]on{<~Hj#{=Grhrn7.PUL! 8=7x<<dj¢RKv~(<w]tvlEb~ q{fC,W8]~|Z~ʈF(bT"H[EvB"v[8iFC?\ #j"JTQLh%DDXryBJCY[eۢ^wyBkb#)X*:"#zp-,5-Rh߼I41xd30=HldN׺d\7yDź)#'eVx/f+Gnʈ[*鶗79w8yd8m ,\w;<Dڸ) Zu ƇgN"ns[5aޙ|eMw EzCUF,9 ?l zpi(>.ձ~GzlK,ކ@hT'll)q]޵q!]oV>TVeD{9 (ULܛEUcWl':5$NyBmמQo,]{;K(Ķ`)0k08cj G6!jxä6}<]#",b//\& $qxlV[fwhY`X1 $nSہsKss&PzwwDjꈈ5(|,\7͝Q;۩qTCgJ+w 1UUÀ^丮o>VQGyhU;yX �!dk.?B*G09!n񰇞a((idY?ŵW[";"Qd)Qv u<Pu{ I�� �IDAT7ʡ #* t|!v>*/d-_)J1}n&Y<kmKTsLzxOA6b|{*^nΞhgm%o;0ٯU hh1V{H޿+AێZDKmYXxaL%rv]÷EViOfWA堰C %2eNؿ4-`H||mHD%*rXObvy>4-Hye)nӚMDjmA5iq+YB!w6e7=YnrGDB#] 5rHU Gv.W$T{;B/&- tl'A{77߰/.!J 5]v;&~ 4^sp?K#hyY÷V2ʒ!<9/2YU›=9YEc;,Aݨچ8, 6npbcm<me"\M[I\p%V["vĽΑ6E7�?.^ێB;2ǶI!rvx }̀6vQIZ8Bkjy<uI}:mWS^;zm О-vGJ{vC!ϙ=�(je-DGaÒ v6X:&kv˜�;RDIevf(UE),{izq#>~TT3YfEZ\;oi`poّ+FM fMX42eS௶/b;nX6/|DmU]xm�p@^]~1U/v󯼢=zK κD_۷;)Xx(.R0ϼXZz%L6˽x]Llכ5h(wk5m/G}n(a^$S-[o@s\ްFX$"¢}S{ؖ, pdd6YmdsCաBz").#*=煟yi7>;U+I`+KkMK:e i(: ntҖR\ɖk`!vBy]B G;"1#Bq=FďZ'W"",DwhH6d]-$ 6"al<2pk ;(׭,].D7LqgYCwQqelG.x/XbTtߴO9))33;ec*M͞OV餁A\mRBrWK/B݉B*gq>:ln(0xjUU ψG}S4&d/b;A8-~\HBkP䲉fPjV pv$\.y;KaO_1ǕA㏖5@<ژ2cEt\ },_w"Q:)I9t ~j"U?(-ZE$ hTb:",�;ĖPЌMۖoy.Xv$D$׭wق+ jijk_{'ЮG-Iqr ų^Oɶ`7y`�j÷] SV#z 0Ց3qRMx-w&m̕ƺ7XӉ_Qӆr֨LA//{I"D0;0q;_$s oq@Yz _n%V\rc˫Z'VU:D{<R,zlh;&>!~\~#"ʡY94Z,أX%\:ߓ 4 ~/y* qYhWBe]<j� RU%Q 4*,sI5+rcitES#""?h`ArRKb el~Jb  GhՈ~%~t%v+D=x,=-WchWp2>+z(qNJZ#>=z3{L {K1aOMk/mŖ@;)YiNgs je=rZKC ;6Dؓ`B+*cD|ƈݢpOOL/\9>|KkMKcfsTU 筛| ayݠYˏݐm-Ɨ#\cP0Z6eO^*Mmc|UF"`̡ؓsdRó- XoF؟#nо(m%FT(CnX6!X%ܹZ2U! 8 aڌQĒFD|ƞ_HCXᅁfkT~O+SJݞl\u%59ƦSvbSQ/\uWZrdkB7 ߋflWU{n۲ʽqJ= 84'y!{DNڊ^ludHmPc~!wAO̿ڣnGE8,jodyT!-'Ql}O> $pϞ^v'܏ID<E˧-*H^X>cemc;M*Tgr[󩴼(u3OmW_dI{ӘH6Xc_iE^>2Vtx.gz<Lj0ϼpIyٹ2vC$7u59i d)9|m֔vwPl ϪQ <�zێp]x]"qkB%O~l^zVQaK&|hͦ}bOśn=*+Vy(/pʤE);$\(} Q[Z53ƪtшJQoq[1D<}l ,4k@Iweΐ7fD ǖ{B \n)iۖ#eV!_Rn6ʸpbv]#<NUuk/wՅ^%$%ڞ=M[x_aAkp|_<?/L[|aW5mn eN @"C[g]?EO . UVr,6Y~YcG71&uA`\blIܜًgМ35y_ۓV6!]ldeYhKiu:%v50xֲ*d!$&o2 οW#q-f5"f;ry K*Mh`lOnlZD6sDp7FBT?T[�cJ3;s9� H ;a-j!׊tupiiI'PM>{!M ˪ZHLi8QS3YT|)UMItV\1/^@J+WlvDMuտ ;8vpuZJKpZķ&YJlQ\ޣ)e'+Rf%l?<BЕ&`<KjY|b&+̅&YS^s{{�J&l:Ququ{S4z*1;bw`o+ϻr}M oAd_>&7 <hfơ+ZqKTMVQ�Se<!9l2c=UhW,UӾk¼CRU'J Dxj\F wϴ&AXB�"bmáKOf sۚKqj5c+!m1YaUᦦ:v3ΰ:$ư$p2 q.k/aR=<fR>1b-K0}ѻ40,S(L�� 53"5X s EY\ZaV)мfGD M-I]W?]C;`EK+] TP=r+-ܕnw2T7mif2\\ݖ/v=7�)Yoj0? ePŵW�ezYiuEiOE(O%o�ȄlIgy ~8+ہrJ{ϣ.)Ts<]HN {{l=!Dz2@/5q>dQyÃĂi7[ZYs Ƚ;2̭=#V~YA7 *1K<|oC>Z{!]W}}m{C[)_x�k| nؚ05ՙ%TߓY+֛ a{h3sDVd@{d<i_)hXۑ5AބV: @WnRkŀ34cBK9[zGcop[cǰ0et{ xvL@Tu9{zp].' *Z~YZ {bY"SiS\xgB_¢޾# ߗ6DA[ e!66U^zjjߞ8CyIE͙wybh.H>h&hÒ#sUZEӬ3EF�E*?43�<9rP`t_]sx2PNoiƞOTmq[B EGv{QY;u=cQl6w- \Y3Ek|| rqc}xegpmIԮE9,N~u+ξkYR5ÚXo~| oƄϨ(mrSUhh�rgG,bLE9,;TknSam%h@{ܛ>Jgw\e`({Q#l+}.d=?(C)늛UJU,[V:,^Txv͏.ig^Mxaǒ5!adMDآԴ?JۓGV?f)[J; ĉi`s EdjW( eL*QxI&,_FD#ʡ9 6IC4<%&.i:vk]T q>+<K<(Đ|ÊoMq:@Ԯ4t:ִ$9Jͭnjvt .ܘ2"z9oM@9T6:,=j;tΫ+ǝ~mdџK`L~v;9(tP 2oڦϢHWK%>5Uώ֬Fb-0mJk& a;?P{u'V. gÎA޹>9)eg�ڪ Z*O|X ӣA|]ۃ(I6L, wjr/CB=fSΜشG*Stw]}Sշe)jr-wF:f`m&HjB(0t ҁe&WUvʕW/]8,1v+iO4q"oE>ٽl:͞ 2_Jך]S?d" fEYIF˦?K+tA(x?p1ɪpNŵ&oO,0k9y"K#Hq|c\ouWcW?ɠ]#pCV qzm':w?~nz. qxBOߣ(|z=vB1ݕuŦi&W6sPcܸ+o{s!|F3F7 K}WoH. Cso&ce_/fp5ݮoJǁO<g?jmŖQEW)?7qğOј~tz<@t rG O%Z%vDLwsRAQgVex=J|{#<cCi՗z(wuoT^qى̮޸aEZ)Tk5͝MN> ?b{YN*UZ�Xaꁣ6s?;1uH է6L~|#t~ﮟ:rɠ^N RW\Y?T8{W-lBy O겞?]v"/pR{xA"(wKq�2]oSiDDH(mջ"SO]}ͶQ c,[;zskᲉKJim=eݔWn#L'8eמ,7Dx8$  xyl_d-maԧSAڱS#c'xGu$04@|ݬ*x[Yh5˪A#" Q(a#j]E+OZ| yk#/ "2O5̛"^만8RQu07J%fkRU< a1DyO he-GxGsWx5}%*xUX!% {f_i&Yi~E=/?aVA9i{[dݔ߀m 5pku˪YRաYc=f&P#rvGOx#k'G:<(_ZOZHñY2G؟>P].Œ|^*ܖ>vxtj &+ >〨8)U=6 |o$k}lq81(O˷&AUغ;{tm!p"Jj7oz` LiJzZ!)6U -4ԚoN[BpObq6T\Uĕ,:;W#&٦s}#zp6tCkҦ 'Z4B�8ptxbȁVؘ2z.ʚ[V^fiiI MW V6ˬ&ή- �GPt&>llݔBKc!kDpRG~uA9S!vrXVɎOHt\r!eȮF9ޣhk$HBNJ{GQNBX'U䪏--i3iqMWn+aͭTL҃\H]p:f? >Eԫ(KP%\cw f~ZxXk>�֎/_7mm\o^nrn(MJ<95*yYᱦT1Nv*ߊXORl 0bNz<³% gJǮN4:D8֡ۀ69z. d{E੬Rh5o$ލt"4έcG÷غ ̵@>iQp8f^#W&dKohq]_p% ۞X:$uԑty@+ St|`{NJowˢyDX|aa5_:xwV8<ip8v<CnXvI5MDrS:,KkM 5sGOj(/ya'7͟.;o'epX�W>%@Ӗ1j|Xݚ:O:<_Y\3@ wo|,ccuX�ͽ{a(\RY%DZt6­9wɝ_kF KךͭjdhD@QI>9ysl면u-^%:4"@񸂈< Ao)`B{@[^L"`l|'j>Awo6&VQ|>JP/YiN B$xG˭:ñ^0(8 N ױ'a!=4:HKj0Ϭvy+;`2 *h7e;MDkm,: 'l8%:b9,�x5b e;nIvKYIxp/`MmLG^<*` Myf_pb;,O,0kLV*Vs{jXqK@vKC:HYWG$b zg�)Tp{ty'jR:)IF /��IDAT.簔V/|;j\p8vn%L'N ױۓ覆3a`*+3 .A}S^7p:RKv𿖟4$\NK<F2ۡ}DUwgD<⤬^vFLF((Cx2@"n+mZg2cF?nK @S<.ULZ\{is,$غ KEWИjM^ r8v?t +�]7dێ ԤWLǖ{m.S nzmt ݗ6ehSF8 @>. dNsI+cȂmN"|@:V<k5rKunaYrZ[3g):sgK]Ҏ6֙ˁq8;B>pݘuSF.Y7ۣG:΍^2mp8E",'*v؂ܲ|+~p8pKgV:p8 tBV!fYp8+aY%p8NsXCTVᶄةpKgVp86`gC<V.p8a7bM_p8]òY -dp8+ ZX0����IENDB`�����������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/images/mosquitto-text-side.png�������������������������������������������0000664�0000000�0000000�00000025313�14502137606�0023527�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��Q���D���8���sBIT|d��� pHYs�� �� L���tEXtSoftware�www.inkscape.org<�� �IDATxwTǿ3KM"셨k j,Qc,K4vc4b]KXQ (*wX; |9N3>ܧ2q(q iX%� YIԤgbc<3Ut>ؒ윝+qi$&׼>)1yNy߯<'BThݱ")?£76OxL;$�20wȗ+xf: -{q^XfVf,ۡe>O{_v#9UU|[*hV: рqm| e:'�swg% U{ [>J;`*#oa}2.^f% )h&SX鉑OӣCC|7'X)ٖ3l?C7m"ET2'ƶ~tXШ<!@`̠زt ,͍m/Ǽ@_/r6 Pc#}`8}Il˱aُ1EO^M(Lrec bSKUsaϺ6+ axQlZsbBf5}ElB~;x 1E_~ڢQEOB“oU gD8YeeCõ5.{*e<#%- @ث8b~^0Eci95e0"QBo*!aј؟pZc1pvKxJiP^g8~6mWEٗ/b[!�~]U(�ycOKATl'-Dc] : T(Pps&U$۵iU@"X$5R%%-+cǾ|O_S)JMH=6=3Gˬ?$W( ĎMн}Cb<UG3 0Z_4QFS-p’Y烩K=ѫScb|+Tg@e&u<�Cԟ.bUm,`∎26E:+:zTˏ<1>` 8X5z|jz6VS'k'\PٹRj�yuZ՚}2^N G|}q%uѹu]LӕOH]qKsc܄?M$dd>89X~|S"=C0ExZ.?bv/ΣdʆۥX$ 6_"�7È![@OJ&% PYqsAjqGѧr;ءO-;?8RUI"̞ԓX1'� efͻ8 esͶ|Ƹn06'q`-Mѣ#i'r|eô )N]t() V0m?ċ~~׈ >"7|127ǑӷTO_OuYY`҈Nx|R:.|F XDK{>W'k0Cz3p ͱwd"bsH^ %4GE:YcWxhD oN}L[JNn>[v�=n?pD:5Ơ>M޷6M狙!&m+ו~ftgT*"Gߚw_SFv&42\[4ƃF`]X`8ڗMS}!'2 0mLWCeh<y^*㭚] vep'ɗ$-3S8Yf5GIe A|R:R2Qp-7{WTxGAPp5'lBDTl ^M@L|*,5<`lHoJ 2nC41k j?\㑒Wx6whg+34$1�de>E#mE;spT<}woH:Qq)4o'j:M\bS0s?bҴm0s|7qjr“Q*GN"OHDW}z:q09}W{3w:U¨f} a}ZI<ʼnsA39XAf51e!ri$"UbS>EkZ۵)q륦gcjדѼ'zwnKe&Bv}'dA c:LMxwnkE- R9>{_nٱztlD^y|CCŃGŦ`EǗXRhâ0rޅj xN8yd'ԯ]Q*9 a{{*fF޾!zwn\!a n;kL\)8I.M gRRJ&Quj8/SOMԅ{|zEcD�<b *wZZ߮!zV2`ku'· ^K|q ixMݖ+ETͻq=*$O*ét)~7݊)J:|O\xÕ[!r+AmCmddb:rn<ťOd#:e%sw%deEt ɗɋKz>˒-Qr_r\ndw,($>ày6Cݲs2ryӰ:u<1%⿵ܼ|QrT( Fvoqԃ3r<"?N<uçauLN0./i4?$}=15 B0D,f}OIuXcXWna{O&X@|7'$UIӮEmXTQTTru wmJ>򰋦e`=|FW\\⏠� °/ڇ}�0l:Q6 ,Q45 Pu<¸;c/f Jd7!g}(zyͻ1ao%vƶ � ܾO6%Fc}_< SA+XE 5MxsV"}!`Nُ{=w!ԄD,.m>#y[6AhhOBRdqu P(jK�3st =qYŊK7ҍ'g[Ru SҩI$Yj#&>3#=SfLB^PSˈG~er.US08rYsXQlup Ivijb@]Єh 2+#B6ЗA;7F5Wπ) +JX2 F f&%er $_I[}JQkKS8w)9]Y!)3sDZnԱ~=VqEcڣZ WlHD"hಓsR_8gnv2 P:s^;sR٤ bG?3+KS31vכߺNx" -wCNi(KT3xE'[9hÑ:HLΠ:ho[˙A*pwAz:y:*I>|D,abal?pw;ǁcկ[$>;8\Kg X()2Dgo.!񫢿4#E> yMϜ- LQӅ,eblخhN낷3~ QQ.mTjݥ uSeHQI$"p?^A]tcC}|=ztlT$cp=TO&տ5-MB¢1w+?~W'2(phgZnN6`YoccQK1r(zQߓd^;h>6+`E4WQI qF*Tf rn�%яxy4jʘw:|Ojv8l8H�0)hldg\0+UZ]ҋן�hѤknp4_oǮ5 )lodXT1Ji(K yy"XJۚK7kؓZf!F5kY.'طЭ+&>g/?TsDu7UO^A:^NؓPy|,!jh oצ*Z9mO5И23]E@-Ϫغl>Yaڽ}CY7=:4}A* 7'tec0~h{5w*u\S񗎾Etaơ7< P(dYƿȳwAܨ?D.ZC\7ьk;haY&E&F*2LA+¿ի@rlw%O*ֽk/=z[a k{VW_6~B2rLtFTZPϪX3VĘ-k!er$/.#&�s2jHMv=xO�x:5LI'4s+!:f`/d=71֯ƻ=GHff 9Szihβ Fkٓ{yӪɬEBK~ضO� .ߩ'i2~?7ư F/I W\z;fg'a\s3#3Xc˟?C(M--LxR;I#Ur&\F<θD@ا\ZծWxMub(g&{[b-k!jjb2ű4jW{PvVuyхKS^u m}Vuh#,ͅ{Ӷ[*�4j=T^*cgH,9@zm&A\[tl$=]@;5DtN3cܗ022p InlRhЪfv +aդ;qEr8fdyM W'Z5W[8Z?>i6s+kǒNN%:k׈;qD'nMK+XHvwb8~62w_8KΖ."oX>7@ozҡTrԐMC9eT #+Bn?kND*Ԗq)Dԥ: Ed88MGM Q>3)C,b O~Ykiυ -B-_-Mi~JmV9'=5= 25w s +豣5DӋ!!* M;}Ue&&Cst _$fR;Q�ﻤux&iMP44ZpM4CLcu'2V5<V[=ffK4gO0˓xQKMۥI0ѩ+) $)<}3Cj=J]pu99P3ef0jYFubS0k]sޕ "fBv'L|5yۥihYfQG=DɱL<u]q`bR`ҖjePiBR5ª;,UL3_&'i%Qv!騉ZiDu=GI˜)9⢃@YYOY[bӒ:_ zswu4O:k>\(HN .}dkoV&Azl2LY49*|q�!y02]'WR<fMkbJ.L,%uGk H4{K,&<j.U�QOK431$B3rPL>:(ґe^{퇴Ք2`inc:9R21m=/DcϘhG{ TƔԀ5ELCBT_&]c%8116 QZ8myD:MPڂڋ+@3:qu{Up"'pQѴ̈́E{fY%, Ao~q)G\W-Lň~odUJZ6ܹLGdȃ2kOhڶXS.D5GO4p5kE?U&BNG[{< ~dzg@�sCwAeLqZ8ɚގ̋6I<ϭu\4.C/m8Y`ADA�kNk Z2.\(@J0?f#(d?<EIb֊A9iМTv!jke1Dl-Z_X &Fڠh<ԖI)D@<c` 1CQ~} D1h<@/RÝ, M ܵi3%VȱK|gxiӨjscTS!D>Dd�sne"F0/u{FJO/W ֮~S^OFfN![DBhʄ><)a@KA35 5QODifCJ gou[M̊k D"0hP(=ztPMUrn? skia8M{43禉h̟rz=<]/nձ-NT+;Ҵfw8{Ǡx8cfa`<6@Trn=ȱueiVЦm)>" wy?%), �~D B<->%Ŏ丧-zLN¢uǨ_SLUݕ[_ ԄD8D"7*Ucb�}nBf^žVmIMx_x3CoI4@;oU{ 4,p@̣qv ^-|7̻M[5@yˤ*}�tj])Od јڧ,L(/R:5.102 ЀJ2nH{"ڝX()HTJê-DJXDR8yO]/g“M1�@maB{ne`$cm8V8A"0j/TsK!]zP*9<}aoԺ|9�*egL!i<wN\^& =9dw7z*S\Hث8lyp^EK9٠sz ;|MTrs &۩8wa$1ͮl{k#Ufp֮aSu/#bU9a6v9.؃I#;JQBFΟGԵƘT\4ށ|qo #*Y;JՁ/G_]eOMԫB-!/| fExd_܅8ؚc L!?m ׂO uТq ԭgaѸp U 4gC8!X3{ee$@��b=QNxKy>\}nhI Bc2@/^YWnfֽ|�lDA�Э}&b+3;ۨW-&}V'խ{aDkъ>rQ׭C>}o~؍f aԾ05Vrk�ަתQH;kD<)ڷ&Uv6}͌Hw|;�g2Iۘd\TJ$",57Lktz Fd1@5{FFA_ C ѓ1gslٚ'fpnHiѤiUٰbOy %B¢pk:�Ьi:> yCؓLg~J"?(Xڅ%4Qobyhkؼt >+�� <IDAT\G߯dȓpRؕhϫij+ 3mltmImRRկ ȫ:n>zk.MPiѓ1}l29קDf51ai4c鬁*,@ wFP[ҜT75qD|HI=1qxGI̡Qscw\,t/u-wS` %At|NxUsect*v C=,}4k mea0Lr)'� c#C=lZ<Jk.mH"֤𾾘9N"dY�_ۅ8i9Ԣ]'Ǝ RIǂG^)M8oK,v�Q(-#1bY`Ğ23;ԪJ,X{T||9o!19x2 4j3 5<cxpdVG_ܢNZ.Xp)aea%9+Lb G!]%$-j?(C&Z%6.Uj祱>.skyuԢ6,P;oS= 98)Yjz6.C8 ܺ.&/F۶ym6?/6_ԂX"=#3+,ày#O~N옡b^-0;ؓo1y.Ľ =}agZ̘oZ 磠(;|Sz5JT<nMg[? _}T/CS Mx%uֲm$-FhU\![�@춿NqqMS7o=uѡ!~iB Wf0qxG H"oQE3fs1ܻfGƦ]T6:YЖDP]pj>6.E(һ&TPjz6bPY߇ϻ\}u6__-Ma"SC:X4?q̄9}}cRxp((]”Zo_jV+\C )%I,,hg'KIm]Rs[aKLCBr %\Z-X$(á痠xu` [1*=Z-zL;8`9/p\ɞc|Ie؄4$f!lΦ v tN-$>)7ɆW{&L.ىV珆:r;LętwE_o ѷ1:u Â}ѹ01%&m$,K07``/Jtd/YTcdioUp]@NieAv<a}3ZÖec³>SZ<"O�`Lm`4,/L2Qpv:BH4Evm&@%b~_�E%$@GC*:>Z4AՌRNp@A:5;pn@b�.3@h8TtiZoQI^Umu2 aeg~g%KIհyhe0z ( <%Ħ ȳ5 G$PiThݚTCdminKGL-jضQ[Sj ߦmVtAT% 3,�dA+zJ<V]6*ЧKvv~:f5B[` ?_))#bl 74(ޒX!Du7gOLngUl[>Tqz(@Z7-f v%}duתhSH@4\`!0z󟠱@@/IF#^SB`~X4o쉍G:@)De/͝қZ5j[{K[s!ך |tBy#OjgT匟k=򰉪#?/fdǡo,ѷnKZC=bFΙ܋rk�|X$?UajEa/^YyX8j,`^IJ_@,ajp<hEOGRFv"3rxqˍB (~ dٿ5& 5 >롌~\! �P > yGۤy>NޥݫWc(1kSc@BpH)'f&R歼K_ەp2`6"eR{ Cbflg],K *o>M ~] E=K@cZ@LnVI|԰Fo[l[Wde cX` Y~Y>uk:WD cq.PF7(_8iC䐛Otd/  {)( 0@Ly)P~,Xp �s P@a[(9YeWD@@@CP >N2&* Q(k%QO8/,Ѡ����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/manifest.json������������������������������������������������������������0000664�0000000�0000000�00000000634�14502137606�0020304�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������{ "name": "Mosquitto", "icons": [ { "src": "/android-chrome-192x192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/android-chrome-512x512.png", "sizes": "512x512", "type": "image/png" } ], "theme_color": "#ffffff", "background_color": "#ffffff", "display": "standalone" }����������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/mstile-144x144.png�������������������������������������������������������0000664�0000000�0000000�00000005517�14502137606�0020542�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR���������И���gAMA�� a���sRGB����`PLTELiqNh?���tRNS� *lY5"v=MdD}OЛ�� IDATx[ے ws $[ l7Г>:_$YtcX:ֱu,}_I>^Gm}>)o,0͟mqmy}8>;X;yDjM{%0Wxm#OώOgcg!4\9 ysH{+|y]˓\-=O˓lOPMC<?+;�(٭ !#:|D4mf`\',>$(Rzە[3X5Q$nڡhN"(R9,%m]@2#B$YU{Edxq [*dܞ|W bVdwI:l\ htyHNs>mB<~ W\ OpeGEQ܆Í B1pR i6=*Lm<9K=U\j6ec!rK(q�hYU|﮻�u |+ 0[#gkaD\#\(P _PeBH ZƖ1πy"<vkY6R#qQ�[';Z*X9(, H2݌cJWs6)<b&s6 .1b*i9H*~VwfSo*k #fpBQX ؜ރ9V*No%QvDU0I pVZ89KyE}*^)k@TFu!ݪyH0;n9!T& Aэ19DT)4ȿ ?n]PB&I[p=y)8ZyyBۣ ˅N 0YU"!u3f8c�0@<jL˘Ajhn2f3mё LX~i4(SF|]/} mg)3IUU&D qEOʘAL&W)3sFfteNM@~ Y!]4p`T"~*7!a+a靛onLT33!8}o5E㣉Ό00gO jU {�ƠZ(t'KuHC߲DS4=CM0X@% . b �3hsL 9NjHR?6?L5|* ḄOlt۪/.@݌!gMC\ J@꿘4d(D 6 5`Tʎkf 刡2Q2/yoehJ<CC-;!Sg?3r2SgF $˱ +Ra"dwC -F{Gu;-KUX}3,u!گC>CyrC>DX͇L#fj}tqg;/d)NOBNݪ�sjdxNݬW$�: W�[ SՁiJ/Dq<$UtJ?eZiU03{+W͕c7Oy[\̄@,aH/w*/_.~�3"d_F'm.f]X-R ʞ~TbLuN7sN2fͫ]8!ńh"HBBOHO徹˿T=6. D~*VtWn<ޒBi<ۄ-vCuo{y]8p,%[RZva5S l[^ ȆG iB1qd�ÕF~َ :=`6I ϳ??@lHXǛ{ȵI=p*:w\\Kk١N S.RJQd"=yX qYb-̢p0!BgQ:j:@Hxk<(KN0L}Vvۆu8+>VmjM<4Ш7r)r48?+b:QhFrqu,nlڋ44]k!I}qkuppz6@-kN/z8ЃHs) &lڅ3p[O0o{{3 myW&i<w W*g+ϙmSWmMR="P7r뷎 et9:PDLK,O#CRվuīa캸9bͫ[B{e!ޏlm;Zw2 il*2 wx|zhpW}Ka:kܲQoky;lޟkxnLCF4ϗK}hF~3ǿyk7P͏&D]? [UZn_Y]/(MLz>L<2tcX:ֱu(w���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/mstile-150x150.png�������������������������������������������������������0000664�0000000�0000000�00000004775�14502137606�0020541�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR�������Oo ���gAMA�� a���sRGB����EPLTELiqu���tRNS�ݼ2V%AanK6?��IDATxٲ: E<% ) CWU{=5}((Ҷ,˹������������������������������������������f2O;GkE]~j;{ckzܰ[٣u#;n?+st$I9lаA5hew X#םgakjL? r ԰/r:e+g{3H?H@_|\LEG{Tm!S!\&4 Y؈¦ \z~vcrLت׻nۯ^\]ON ξ9Eݯraˬ¹B=x0IV4_2ʹUk^<9=rL^a\I}Qb.Ʉe(=0? d9^-' K 51<|٢c&nVz5df+[G1j".QG^<_sY;,uÅcΚ ׊YYIfN+UhI0QZWSfNTtW&<}yJBc_<τfeJQw6hêu]źBWɄ�v=>VV^JmxWk$\]0ǟeDT,(k[EȬt֊£XL̺Z;}E~Pl (乕pVGkkB&mOmTգVǧq=*cie ܦD %z4*RƈbU=._XpүD*�u0Hq*EhJ݄ ZF%3GTRj8ǻΜ˓׃qPK[1N7Aαo*}$UrI4qjK+jl4TSeYL,4)zGZvֿ9MH�ɧ)yRIcRRBevIs8LR43oV=oTiev̡=V ;JkF^ّǐJ.u3˃gGOG;+ɧ ϬfځVΙCM-BRL'_)Ǧ-49JR0s- OZhU*3j%H'vWj3 XV?Pby?6iw)*E)M]R5Zx1D{> Ma8LHjӰI<(IͱL/HKz)K |)RTQk1V ނT]Q*|.oOz.OSq0t8hUqKǴNސ^wXR:9^:>XvS9oe۩Wz<RZ[;o,XB^vl0i,j-ӹMIvA{1|F@S`XfMIce? DNiNV8ٲtEnYkh+w{Oլ75JmDs[}ף(9>6Wd__Z GOunJ_pٷj9ɚ+)R�neOf gw=,^Ys:/YIXyqK:n_ufGi]C7 LFb6|ntj&"#I}7[ʩaȭ;f+OnŽ-gI!E -|r*=<̻uGoUS5pl[Zln\JxQ=NϷ%mv5wp#&a~:GSjiL/G]~Mg�˶m>~89*d/ kS|;~Rϡhm:zL=V|qhR; ReUw3'a,n+ZKr|&}99U7n}2p9s|mB]ZΛCB](-Eq@Py{MCva!ar_fiۉp=yi2 k0StZ(:I\uO.*W-%x^BhWɕv@̧t=:8֒ǒ^IOָ%_.#zU%m  603l`:L̬RL$BLKs�����������������������������������������������������������������������;jZ1ϕ���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`���mosquitto-2.0.18/www/files/mstile-310x150.png�������������������������������������������������������0000664�0000000�0000000�00000005065�14502137606�0020530�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��.�����lF���gAMA�� a���sRGB����9PLTELiqhK���tRNS�2gT ׀B$˧y�� IDATxr _ ŞQ Z&>+T2(nH??����������������������������������������������������:ڮ\-nzl2^RD]5RL.[uģhfey E}Y.|nݿwr*f^ _as٦sN$͵`fPI^Br}U^Q~d zҗ]a+_j8ZN(zԺy7'kN8(τE翾uu'HU md(iP:Us5}r'2mCc"XgNLu*~lNj FakJ /& $ZiJ16L/E2P`kseq_p$Qk.\d)/,͜"ks22NO./,<[rs+R e81z$W6Ėa⫙LVn':JIV,dz1$qx+5cԶJ*eIe䣌EˏKm򝾹LI48)':^(=&F1EnUSS Ɉz:6^bɷ8Gwo=#.>nNbQY햽U:/mEݗE(wAj֎b:Z{>fE| 2HoQ~; K5.l#%:u.+Vך:|qM]{בY(#]u`vR|JF7S 6*�,WK6"9$3`yA-LVU!.H^V#*e) eYtF\֗V1FN(7sQ&׍Ԥ]N4C6j,{4M;?.ᐥqL%P\tdNmtNI,YS?YuatZIci;^,;, 'nd譃\zfwR9Rpq˭2nKdJRo[~0.T;#m_]?/J?2pFf8͆Vpa$`dFr6mT2\/tC!tn%^{Frv6b.)gsڄF u'1Bi<qܖB<bL=L?bI40tl.G+&|]B{T.4_J/܍-yAiUMMe.7vhkw@0qk'G4t:]}7KzTFrk{g䡀*GG%L0;[W,#ίH ;�k*Wꃝ6p%љkqm'~a.vpXÆ Onn^ 2(-+zJ&yө${n|܎�;JD`+lf͞u|\䒖%rsgUaC[F򢋡>ҶYOʟfRRp/M˶1{ sSlUyKSLa椢Q{_w>֛=ZwbeM9(ڨɕoϦy,ƽ{3ݼy&oCl̛j>l\yxn@ gW=&ԛxsUrzxĕx9\O=ZYxtOTc8Z;?Mɓj .z=8?sa_98ZZ&ό˹; E#ǎ4{kk_s炝lZw3KgGv'fiv>Ӥ.>8C_gщ'V]O k.ѓ%YVW(ܪXЍ?Nv>WV|Ӣ|tD/F$ڞ-s�sa)˒K"xџ{KTCeRKs!h+MNH\J_fus5H\r+.N_6qγ4Ἆ8*)*\(hyn8o pZK ����������������������������������������������������������������������������������������� 0 >���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`���������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/mstile-310x310.png�������������������������������������������������������0000664�0000000�0000000�00000012403�14502137606�0020520�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR��.��.���s���gAMA�� a���sRGB����3PLTELiq���tRNS�˺$~jU4D�P��IDATxٖEl#Ov孪n!Yk^ Ŭ���������������������������������������������������������������������������������������������������������������������������������������������������������������������������\<44?_ҽ?O{h/_2F^$-eA=]~?4&iy<<P}h奴_?E^=,%t'y»o'iytyyћ>ogߠu35hp|xGGz$|Ǡn~d£ ]?OMMw|$/<,L]<qrj<<K.j[Mt FU?N^K!N,Yvf^p_^qi.J%. ٗ5$|d.j'BeW0GgLQ7,K${ޙ}AT跉 TroWwI+︬X7I`S z.7&`R{λ/IEy o˯*7jG'\LbL2~r%BM]&)K3ܮ_eޱ|}{^þ1Rv(Wwn7'ޖKȋOrXLQ,3 yQM\r1Ḽ8yA\QDm hrrq~/'] vQ/(1mwgZ Q//);LptzD}-#ϑ{ٯի(5`\J}pﴇՋL]ԯ Ď~TG9N.Ÿ:YS{Rg\{0J{δ?Ys:B%NΥ?vE!&::楿 G.E\|a.Gϭ$\>WJRuG:snKN<~t;˾82G_S.~[#M. |âwڦBv{br 6(+89U{�‰RsBr8+83}=9昄]z8ؿԐu{ aHص }ʐ)6[d_|GqGBeqr?kqWϹ zߝ5BPTs~&lEP]GCۗy;lcNEK'HzF3G2{9: 6<!3ur-Sf ЬbyVm1ϖ uvU9jіMI[+M Q7B**O]*9?ي ~i ^PF3qBgII0E,Ki{B2~N/Oܑ2ԾSf,:3)qӴ6JXVv]fݧr  ;5!<dˤьr߯RoPx)jgneR+sn~fq8g Oe;=RGf]~liԍCsnvOlt)Rz>t鲨C6٣1T :˜ F^mu^˖4MN\l흼`j wy_u<Hɿsz Һp|2@p$otޕ$)fmahT9U2nkH?8 Y׽mNրE긜nY^ S{86LrOϴ92lx]ܩ>U9(=c_FF nuA9)ިRYB& ]Aa&By&Ȱ1ʟlYi>lR6Lѿ 猐F񋿴)Y٢58}?Vr^:^ܱ׌&TFMs}k!xY0ChΟUI&~zڛM_un-jFh{6i;4V>й ])zO$^+TM`dRxIbacTQdܜv1Tǜ6+-}#!^Em ԑx9%"_Mޖ?֞-</MV wlP0AHcQh|l"^ֻ'`!XEo]mՇcg7v !XܤuzlzɎ~[QاdI|8'Įظ !8G\Rl(&=#^ب_oa(E:6RWv^ܬ{"l}mG˲{#Fe^\&qFԷM֋ⲙ(qJ?C+nGKGTJ\V_Z'_|�qT&#.m+eȊ˰R\qi3AҞ]3ctE.ղeߥT- 'KAY,75[tKtV*Z"RS謋�f]F8-1bI_/*15#imG3 ["QҧO+vULEڈ z'gWŵQr*8@{fq(cȄcuDwfe RܤR4B-)s1VVyXVEXp16Zn;YV=HLL(؇2nRiG4-\keR"3-H跞 '̂%Sȝz BX΢S%b,`3=)1`{gK*wn3k )::}^2nhf7REE'bAr'^dsoNLY*;SZXeKdH4Eߜ\jX*ql@6kͥզȬqNx4̦ۑxTVO4\$.cՎW1v6z"g]H:̢UVދMp43x=GF= r]L}vq߅˓Ji886хtOI}kU0I/XX[tCA. B#]tV rsb%,h�IBYh 37 F>AܚN6dnQhD=rTmc#:ι҆Tj$qY|* #ACϐԗ58a͗flGUU|"uA4TRt%mԐ{3]YmSACY+bZ ٬W:UC,jҡJm/>'Ay]t}F}t7;LOQwNޤF޼n#͍]xkinhš|nlh Ϧǒ+R?[sc#O7IbԉQ6˺V|m*"kyu^2wE궻SGO^ZYb@1yD?eԋjy#.kwYnu9y5ή9|o"1m\+?k80U.Gg^4dO xW2C f/.]J{wYs68/4޽-'�] 8`v$vꜷ nZ=|ZoF*\c&&cd tzwFs+$?}[ Lq쮣M{"^׋u{|R婼׉;Un=$Q *[sٙ?~ދ,$[GmӞ=ɗ䵋V\O'k=ՏH8kCG\ u]aWL1uImLw|E[cٴ ^Y5ڼh_36q Rշ1CwP5-2w4lUH??uӮ1357 8'M5~ TK}7/4ög?tpݍmeN](SLmt͊SztKםWp8xM~zXNM.펮%ۊ^_S9#0Zwn7cs(YgenMUݐs6ܜ-f;=*t-v&kE]J]:7U ݅q 7eG. τUx8>JLg:7QGwtH/jw7F,详v0i'-Kjcq\K.^jTs%󤟓s N]5$p-G.&Y4}VЀ E%_lHSGu\6I/;y34`!%do%΋�>ŗ%^~cӢK3L=_+? x),^4je?xY(�զSر, gzT9^G[K_/\vjQ>ff"q\L-Y-ZSxA~Mc3e?yFǪ[cixXkK`{x,,6=f-]VM%|~ͭ-veG[0[,-RxcI:+bb?wﹼo4x,dghhx,Igڔ7f6|Տ5c7e&npe-<vىx7euUpi2BG Zsk}d!z>SXQ~ze+:VlX-]V\~8*\6-Z5os&Mʗr̘ȯ8Dp>XQ[d3̏Z̐2w+K<~O}x8( w;Oc_dZ RJrf|ƫYfڛ.dqn0Ü.;hGŌw F02>\.…r.L(^|������������������������������������������������������������������������������������������������������������� :IŇi���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`�������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/mstile-70x70.png���������������������������������������������������������0000664�0000000�0000000�00000003724�14502137606�0020374�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������PNG  ��� IHDR������������gAMA�� a���sRGB����ZPLTELiqV���tRNS�& [gG/|=rP5)��IDATxZٖ8 w[B *cyeTݬXbŊ+VXbZY81Zxa]|5Q<:xGc𼪡XF㯯@<I!ojsexTݥjUWwJQWpމqc~v։N "�QJN O^DpW ^f2 YC,p^B`as܂͒ VМraiV�x6�y38_4g,>&E<Zr�6QwZ4+L@e H!x1vӿ[n 01 & O ZcPP:J]D'v<l|q1`ISס^{I_g#K�7gF($K>_!?oUHrQ: D!(ryxI4E>E^C2a!lzs@kZ45$ jy|$4Lrˤ"5y X TOa*@w/:FUd )ŴE)#bЪPnbHoJEo-Eb{o1 hjIiLWyDᾗ*^ Qh6R䴉jU:%/@Ćҷ_IM*^@!R |jMVRIVl=D"s> ԲHB/ɧ]:)z �pW*1d*jŜKV5z'VYL<u2h7�p/ B0wREj (*J\߁TJP ntk֧\)Km ¨y pU' J6@ Su'rB8rl{V;45F;"6l+ـdž{.*T!JH*gcX,-w͏wlTQힰ}E<�=<*{ϑ \\`Xr#=9zwy|ڎ�d4PPG| ȿ3I Sv)P% -=I+ReL+hx`ˁcu~*emP&4૎$ 5o-hm&pG[x7ctU١G>ȯmO*tM Li]PDCA15k۳_7A[a>s'u.znW(44chTz;ivlM=v�OOq{h~I$)0][ %4TtI \#JOn&Í:jgװp_|N4HT"eZI'n̬Po֘lN|F<U*f'<0s!2=k}i=عw62O~ GXk<uc#VjQ{WBUWͩ~ 4>>N[ eM4z4fub*'703*7_eh; #xg/wzr cp'ϝnפ3Ł~U]!],e_6]pbŊ+VXbŊ+V�F(���WzTXtRaw profile type iptc��x qV((OIR�# .c #K D4d#T ˀHJ.�tB5����IENDB`��������������������������������������������mosquitto-2.0.18/www/files/safari-pinned-tab.svg����������������������������������������������������0000664�0000000�0000000�00000012557�14502137606�0021617�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<svg version="1" xmlns="http://www.w3.org/2000/svg" width="933.333" height="933.333" viewBox="0 0 700.000000 700.000000"><path d="M103.8 83.9c-.2.5-3.9 4.7-8.3 9.3-8.9 9.5-11.2 12.1-17 19.3-2.2 2.7-4.2 5.2-4.5 5.5-.3.3-3.5 4.5-7.1 9.5-22.3 30.9-38.7 63.5-50.3 99.4-4.1 12.7-9.8 35.6-11.1 44.6-.4 2.2-.8 4.7-1.1 5.6-.2.9-.7 3.8-.9 6.5-.3 2.7-.7 6.5-1 8.4-1.4 9.9-2 21.6-2 41 0 21.4.9 36.9 2.9 49 2 12.5 2.9 17.2 4.2 23.4.9 3.9 1.8 8.2 2 9.6 1.8 9.1 9.6 33.3 15.5 48.3 3.9 9.8 16.3 35.4 21.6 44.7 8.6 15.1 27.4 41.5 36.4 51.3l4.4 4.8 4-3.8c2.2-2.1 8.3-7.6 13.5-12.3 5.2-4.7 9.8-8.9 10.2-9.5.8-1.1.9-.9-8.6-12.8-55.9-70-78.6-163.1-61.5-251.2 9.1-46.5 28.9-90.7 57.7-128.8l7.4-9.9 3.2 2.9c1.7 1.6 7.6 6.9 13.1 11.9 12.9 11.5 12.5 11.1 12.5 12 0 .4-1.5 2.5-3.3 4.8-8.6 10.6-19.8 28.3-27.9 44.1-12.3 23.9-23.1 58.3-26.3 83.5-2.1 17.2-2.7 46.8-1.2 64.5.8 9.7 1 11 3.2 22.5.8 4.1 1.7 8.9 2 10.6.9 5.6 7.6 27.6 11 36.5 8 20.9 18.2 40.3 30.3 57.9 5.4 7.8 18.3 24 19.1 24 .3 0 2.4-1.7 4.6-3.8 2.2-2.1 8.3-7.5 13.5-12.2 5.2-4.6 9.7-8.6 10-8.9.3-.3 4.3-4 9-8.1s9.2-8.1 10.1-9c.8-.8 4.7-4.3 8.5-7.6 3.8-3.4 8.1-7.3 9.5-8.5 12.2-11.2 19.3-17.5 20.5-18.2 2.2-1.3 1.7-3.2-2.4-8.4-6.9-8.6-14.8-22.1-18.9-31.8-3.3-8-8.3-22.5-7.7-22.5.3 0 0-.8-.5-1.8s-1.2-3.8-1.6-6.2c-.4-2.5-.8-4.7-1-5-.7-1.1-1.8-14.5-1.9-23.6-.5-23.1 4.8-47.3 14.8-67.9 5-10.3 14.3-25.5 15.6-25.5.5 0 3 2.2 14.6 12.6 3.9 3.5 8.2 7.2 9.5 8.2l2.3 2-2.6 3.9c-9.5 14.3-16.5 34.1-18.4 51.8-.6 5.3-.5 20.8.1 26 4.6 39 29.5 74.3 64.5 91.4 10.8 5.3 14 6.4 22.8 8.5l6.3 1.4-.5-3.6c-.5-3.3-1-9.8-2.2-27.7-.1-2.8-.3-5.3-.3-5.6-.1-.4-2.5-1.7-5.3-2.9-24.9-10.5-44.3-34.2-48.4-59-.3-1.4-.7-3.2-1-4-1.1-3-.4-21.8 1-28.5 4-18.9 17.5-39.8 31.8-49.3 2.1-1.3 3.8-2.8 3.8-3.2 0-.4-6-6.1-13.2-12.6-7.3-6.5-15.1-13.5-17.3-15.5-2.2-2-11.2-10-20-17.9-8.8-7.8-16.7-15-17.5-15.9-.9-.9-4.6-4.3-8.3-7.5l-6.8-5.9-9.8 10.1c-28.2 29-44.5 61-52.6 103.1-2 10.5-2.8 41-1.3 53.6 2.3 20.5 9.3 43.6 19.2 64.1l3.3 6.7-3.6 3.1c-2 1.7-4.1 3.5-4.7 4.1-20.9 19.1-21.1 19.2-22.4 17.9-3.3-3.7-15.8-30.8-20.6-44.8-3-8.7-7.9-29.1-8.9-36.6-.4-3-.9-6.4-1.2-7.5-.7-3.4-1.7-20.5-1.7-29 0-61.3 24.5-120.7 67.6-163.8 3.7-3.7 6.8-7.1 6.7-7.5 0-.4-2.6-2.9-5.7-5.6-4.6-4-17.5-15.5-22.6-20.1-.6-.6-5.6-5-11.1-9.9s-10.7-9.6-11.6-10.5c-.9-.9-3.1-2.9-5-4.6-3-2.5-16-14.2-25.6-22.9-4.7-4.2-7.1-5.7-7.5-4.7zM586.7 90.4c-3.7 3.4-11.3 10.4-17 15.3-5.6 5-11.1 9.9-12.2 11-4.5 4.1-20.2 18.1-23.9 21.4-2.2 1.8-6.4 5.7-9.5 8.5-3.1 2.8-7.9 7.1-10.8 9.5-2.8 2.4-5.4 5-5.7 5.7-.3.7 2.6 4.4 6.7 8.5 7.3 7.4 9 9.3 16.3 18.2 14.8 18.1 29.5 44.5 37.4 66.9 3.5 10.1 8.5 28.8 9.5 35.6.4 2.5.9 5.4 1.2 6.5 1.3 6.3 2.6 24.2 2.7 35.5 0 9.4-1.1 28.7-1.7 29.8-.3.4-.8 3.1-1.1 6.1-2.5 21.1-13 52.2-24.8 74.1-5.6 10.2-5.6 10.2-7.7 8.2-1.9-1.9-6.1-5.6-19.2-17.5l-8.7-7.7 3.4-6.6c9.5-18.6 15.8-39.4 18.9-62.4 1.5-10.6 1.6-37 .3-45.8-.4-3.1-1.1-7.2-1.3-9-2.3-15.2-8.4-35-15.6-50.2-9.8-20.7-20.8-36.4-36.8-53l-9.8-10.2-3.4 2.9c-1.9 1.5-4.8 4.1-6.5 5.8-1.8 1.6-4.5 4.1-6 5.4-2.7 2.2-20.2 17.9-21.4 19.1-.3.3-4.5 4.1-9.4 8.5-5 4.4-9.7 8.7-10.6 9.5-.9.8-3.4 3.1-5.6 5-16.2 14.5-20.4 18.4-20.4 19 0 .4 1.7 1.9 3.8 3.2 14.3 9.6 29.4 32.9 31.6 48.9.3 2.2.8 4.4 1.1 4.9.8 1.2.7 19.6 0 24.5-4.1 26.9-23.7 51.7-49.2 62.5-2.9 1.2-5.3 2.5-5.4 2.9 0 .3-.2 3.3-.4 6.6-.5 8.9-1.6 22.5-2.1 26.8-.4 3.4-.2 3.8 1.5 3.2 1.1-.3 3-.8 4.2-1 10.1-2 28.9-10.7 39.2-18.3 25.2-18.5 43.3-47.5 47.2-76 .3-2 .8-4.7 1.1-6 .5-2.7.6-22.9 0-25.7-.5-2.7-1.7-11.9-1.6-12.5 0-.3-1.5-5-3.3-10.5-3.8-11.2-6.7-17.4-12.1-26.4-3.7-6.1-3.8-6.2-1.9-7.9 1.1-.9 5.3-4.6 9.2-8.2 13.5-12.1 14.6-13 15.2-13 1.2 0 10.2 14.8 14.9 24.5 6.8 14.1 10.8 25.8 12.6 37 .3 2.2.8 4.3 1 4.7.8 1.3 1.8 15.8 1.8 25.3-.1 34.6-11 66.2-32.1 93.5l-4.3 5.5 2.4 2c3.2 2.8 16.9 15.1 23 20.6 2.8 2.5 12.3 11.1 21.1 18.9 22.6 20.2 31.1 27.9 36.4 32.7 2.5 2.4 4.9 4.3 5.3 4.3.8 0 12.9-15.3 19-24 25.5-36.7 42.7-83.9 46.6-128 .9-11 .8-46.8-.2-54.5-.4-3.3-1-7.7-1.2-9.7-1.3-12.6-9.7-45.5-14.4-56.3-.5-1.1-2.5-6-4.5-10.8-7.2-17.8-19.8-39.9-31.9-56-3.6-4.8-6.6-8.9-6.6-9.2 0-.5 3.6-4.1 9.2-9 1.8-1.7 7-6.3 11.5-10.3l8.2-7.3 7.3 9.8c6.5 8.6 10.4 14.4 19.3 28.3 3.9 6 12.1 22 17 33 4.2 9.4 13.2 33.1 14 37 .2.8 1.5 5.8 2.9 11 2.7 10.1 6.2 27.4 7.2 35.5.3 2.8.7 6.3.9 8 2.4 17.8 2.4 51.2 0 69.5-1.5 12.2-2.2 16.6-3.5 23.5-.6 2.7-1.2 6.1-1.5 7.4-1.2 6.5-6 23.4-9.6 34.3-12.1 35.9-32.5 71.9-57.5 101.2-1.3 1.5-2.4 3.1-2.3 3.5 0 .3 2.6 2.8 5.7 5.5 6.3 5.4 13.7 12.1 19.4 17.4l3.7 3.5 2-2.4c1-1.3 2.9-3.5 4.1-4.9 8.4-9.4 24.8-33 34.2-49 18.8-32.2 35.4-77.3 41.2-112.5 1.5-8.9 2.6-16.6 3.1-21.5.4-3 .8-6.9 1.1-8.5.9-5.5 1.2-48.8.4-58.5-1.8-21.6-4.4-37.7-9.1-56.7-14.6-59.9-45-115.3-86.8-158.6-4.1-4.2-7.9-7.7-8.5-7.7-.6 0-4.1 2.9-7.9 6.4z"/><path d="M339.5 289.2c-9 2.7-19 8.6-22.8 13.5-1 1.3-2.4 2.9-3 3.6-4.3 4.5-8.7 17.2-8.9 25.8-.5 16.2 8.3 32.2 21.8 40 3 1.7 5.4 3.4 5.5 3.7 0 .4.2 1.4.4 2.2.1.8.5 6 .9 11.5.3 5.5.8 12.2 1.1 15 .2 2.7.7 9.3 1.1 14.5.3 5.2.8 11.5.9 14 .2 2.5.6 8.3.9 13 .3 4.7.8 11.4 1.1 15 1.2 13.1 1.4 16.7 2 26.5.4 5.5.8 11.6 1 13.5.2 1.9.7 8.2 1.1 14 .3 5.8.8 11.8.9 13.5.1 1.6.5 7.3.9 12.5s.9 11.7 1.1 14.5c.2 2.7.7 8.8 1 13.5 1 13.5 1.3 17.9 2.1 28 .4 5.2.8 10.3.8 11.2 0 1 .4 1.8.8 1.8s.9-2.6.9-5.8c.1-3.1.3-7.1.4-8.7.2-1.7.6-7.3.9-12.5.4-5.2.9-11.5 1.1-14 .5-6 1.5-19.8 2-28.5.3-3.8.7-9.9 1-13.5.3-3.6.7-9.2 1-12.5.2-3.3.7-9.4 1-13.5.6-7.4.8-11.2 2-28 .3-4.7.8-10.9 1-13.8.3-2.8.7-8.9 1-13.5.3-4.5.8-10.5 1-13.2.2-2.8.7-9.1 1-14 .4-5 .8-11 1-13.5.4-4.3 1.6-21.9 1.9-27 .2-1.8 1.5-3.2 5.1-5.3 21-12.2 28.7-38.7 17.7-60.3-5.1-10-17.5-19.9-28.4-22.8-6-1.5-17.7-1.7-22.3-.4z"/></svg>�������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/stickers/����������������������������������������������������������������0000775�0000000�0000000�00000000000�14502137606�0017427�5����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������mosquitto-2.0.18/www/files/stickers/index.html������������������������������������������������������0000664�0000000�0000000�00000001756�14502137606�0021435�0����������������������������������������������������������������������������������������������������ustar�00root����������������������������root����������������������������0000000�0000000������������������������������������������������������������������������������������������������������������������������������������������������������������������������<!DOCTYPE html> <html> <head> <title>Eclipse Mosquitto™ Sticker Generator

Eclipse Mosquitto™ Sticker Generator

This page used to allow you to create a book of square stickers to buy from Moo. Unfortunately they have discontinued their API, so if you want Mosquitto stickers you can use the images and create your own sticker book manually.

Click the logos to choose which to include, then click submit to be taken to the Moo site with a stickerbook ready in your basket. If you choose both logos you will get 45 of each, otherwise you will get 90 of the one you choose.

mosquitto-2.0.18/www/files/stickers/mosquitto-colour-deselected.png000066400000000000000000000251351450213760600256070ustar00rootroot00000000000000PNG  IHDRbb\zbKGD pHYs..*' tIME+3^.eD IDATxWYƷA\L€& F ,a΂t(VPF00T$rS/NNNNkD!11@@B B bb !!11@@B B bb !!11@@B B bb !3DLLLP"@Gȅt="!R?:Q:<>݃ ͛7 bph`CO 7fXLZ ̰A*XϋCkjjUu@Rss3ne\&8;ѱ73bHERVVV ~Wa"۷oSQQx Ė3677irr7 ы/"8D$Iϥq*((_'B}I|VB:8 !B>wP| 1O>q i`` Dy5n8?K.QCCѕ+W|||244^\\ՌFF>cpxx(jKJkJԦg B-޽{%*7oޤ{>|BU-REE}ccW7 r`mnnvr+A`~~^^\#QVV1Ӳ2Ǭ]9(!>48B3bhggG8555Cj1"PBM~F"TUUrjxVuZ$—DA[DAYy*Cn߾23NRjQ3033!{H"]r YyJpHfƒӳQ-Wrvۭ-q4WUC#L9]PIQ\!ugL!>Zi(5h4JP)rӚ}32x64pDkƤaV%adh֏?ƬBlW^kdMXBX%%%a-6J!^YYquSI …4_  k Ǐ.]qqTfk5>TQ\H8hFA=˗/m579-r| Q*IZ]]J*MNNR*R/QH#dV<55!"m:vqqQlCy-..~:VcAV4HLfCg@h{ͺ?aWWWڏA]ZbϮFOfr.ΰIPO%&&& }uŸM~_| ":|b"Yni>d&1Sevvwwidd*t:ZM:f4^ we DRR_L]fyleqttlCCCVbf!Ir xׯ_g؆,W Z|VVVXt?#fj+:b,b#+!RTW7عZ\\T eFӘK%D(!QЕ+WT'"lrX֘s0M1$3 9"RpK$j J_TZN1+F ,!uYKCU3$I:::RaԉAaggǖࣣ#J&*ǒ)_ŋ]y!>7KhT?-k-//ַʊaZP24zf0@i"Ҋ$@Ƅ`7CvÆS!ZR u'x 7He4euu5xgffM“bM'cղ ;ekkK%?_pJQ!mX3L 1lVF_ >;JARɌ)!öxErz5\ٸ٧8B-) i5$+}++zqpCX!澹!ntqAcRSSCx.]ԯ "؈=BKKk HVjjj+9QڴI[BgyMѹIgQBhi~&?7 BDJRH;_.$Y~hT%YhVsDyy9/*d||\l:qO>%JUUU%b!)k"Gr_B_rW+M" 5:ŋժ۷o=?޾}~1J|5E|͜ (fDxiiѨ?Β{iiIM5A6$>чCÍ"X㵵5Ws1==h.>5or"AL8!zHH#$aήy<WI㩐q0H$zaI6Τ/5wq;7իT}Odn[\\TD"@ +w6\;FJNqậILNtnPxɬ+.F9q@+HPԪ8~'/(矎?߿4!#ڮ.V;NvRYɉ8'''jc !kwc˗/x @49MDn*"1{zzCɌX @M86b_"^J9nܸa C`-immuT8/t*|̒:$ '8Fd>= q駟|5?~jW]][[S;!)-!7jmmmyZ4$0@oQX!KV@b!i xV q#4ީ$aC}bZ K[rN1L,/̈gq-y41 n Nuqtc|JlCGfS!~|,vXfHX,BC,D:'!7IbTBt48ĸQ5A2ȹe/oG?ku1pnNa7fwulY+:y.NF!P iS;ybbBtN'\.^(j?55eMYYoKx!?… b @~rիWb lai [P\wnmVVVĿXo*akkk5??j-/Hb d[K!d6;[cDE(ɤ"Z] *؃Al;[pj/))a 2].8n~KşFC--YFb1VooYxѾs JF%4BEÞiIVRWZf7"iϟInx B=ܿNjH$|?F}/_=zw+@+$GGG}גkydt"{299njwxxvr9Fd6 )"]}}=B?\94KMM $Umm-EٙϤa I:_]]esoQ!vf'sVv[O1ǏE&k+9"$$޼y#zituu^XNO ܄ݳ;7odR$IKJJDd}~+9"$EB̏& \w9NB*ǑpYW^}TtnNxʵ-H psgvd? \ D";k$qݻwD/3)/^P ָrxVGڰ$Œ" ;L4H?44$:dS4>}D"ᨭ>LR"Pdu) ^QH!Htqs]HsJ;4Vx\---Q"BNDBSFҙԕҪܗčBpZZK^ҙbLm3q"߫SKl׮]smuttnDK@#ʯ'tMCBzyG2ع!bRMj'r#%ۋ]srq;׈.H;>,YAy!sK5ǧNM3$<;;;jTڇ4B}P&JR*3s"=xjz<WqZYYmiUC !&< DJ`" ^y^e4Xzh4:ۗzh9:O@HkKn^HR!L&rggԨxxh9T]b mM!vn_κY7D"ਲR-]]]=Ue!#Nuuu0MӦ ~NاH )JۚوIxnrLOs=u1͌ Ɇ$& sH +T]]s_[[ZZ\8{aҙq`M>***=GDYaV܍ D쨋ࠧj &gYT%RE8B,YhHK}Dzrdma'xvv{,Q__/!XI ||dl4x޼zJzAf"-2gϞ:+}4fwC%DjtVr#Ęt6gBlptnB_3p'O3=<;AK ^qb4z{{]JaRxv VF$4PYYj0Z{a9ÇŮVxyK<};$RNÕ1f nS]]3]Kr@k{2OcV섭 fkQI0p^% rYiSr@ [mZcS/5B3$.Y]_\54mX!$ۈКkoG8|hloojq1bB'}QQsy$IU" TƀG+×/_mtZ!fuZ'L{)VPPgDC%g"#a\Jh4jyH\NHtyyJ~b'bS<V6bXhR֊ĝ4ccc|1XRu5dCqcӕd023'?O{{{Cܹs0XTҳB ٰH^3ol^.UUUgѨZdښhNoB0`ڵ$S% t$$ұFaBCcCcrr !Ҫ˟?m%>~(AGD܃9t$/_u!,4ba/~Gc #XDADSt$%###x !AXT^7#1 'BFbIyq@cbUQw!H !V t˗/C<$ƱXL-&ѣG:7lkbX/*@=1CFHz |Ϙ|@! pQiiw`;FUUUWWgI twwSEEE*++a;K[[KvdFGGy# 1TD %RI lnnbIrIq;;;lN!b<L!11@Et3tt5p_i !!11@@B a D"wfot<@ ȞR#FK$t:iJ ibb"go޼.O\'fxeJ&gV\\L===.]jllÇDhq.joo/= m;*找>\#Lx~-g & twb|:Aaaommy Ct^/% Zt_fgg=wmbMT*EæF_r d]fE"خ IDAT488r}:D%<11qTrs˺:5553h\U]]]5jjkkÌ8H{^~m fa+HPMM % *,,9&HӔH$ڵkt}鴩dom:SCCC(6ⅅK"||ŋĐpmmu %dC-"+H͊f{{FFFXBa SSS,>M2!kkk>}#203+ʛ7ohjjJ?,ښ(舞?xBcxxnmu$} >gddFGGՎeE[Vfk?wIIwόmeW݌g?88&jeb9~{{X,f2??O9??>>@EfFψ0wn>׭|6ו^Cqq1qC7k碢"Kkf%%%y= hxH[*頷7o`[[[muB!:Z^[Hĸ[%~lÇ{ooChOfꂖ/biEYOA̗ÊTUUǏ?ֹD3ن~F<+ jVi"[A\b6 :::Lݻ9?3ʇzo4cCeeeDc 's M^GLnd k{z5=yN|%0q#9??o44H$۷]Br8]v3C9n׭'WB #_NI350JD2{bf.A3Ũzښ}yypb$u Ą9pm#Ñ w0~gf w@#F^!"+ߝgwL:㝷aU4j>lUY.\ ȒՇfi1mYB+prq+ n[2 k@b"YJI <)>v'HyB FNI !1_lVf}p!&q/A抎IG)' 5i9c 2_8xAFذn9_LçSV0['NaG=ŧWYBA=t6`9.KV*SӲ0U8wE S|ϧbXdK 1) JخK #p‰wլ5ȔSTTj  v K qh*5o8c P*ψ#1) “$rD\Qoc=z-[xCvĮgp!.brq\{Әp;/?ȑF#?velG>1ʵ .ą6(ޣTm BR_7cKF[J߶N .ą0o>ZƺcP޻4\?-wszB%1בU`Q _A47ڞ>{ѓ#aR9Øx`n>]cwV{;| sBwcey !S47x~ ֯%>Q+O2ϽuEܼS5J5,sٵm=tnSۥ1&W/g+sq̓W"1vH6\.ÄQ=07>Т6:~~FӒŚjp!~6dHUmbW ҝ#+[߯ǁ7N3xctf)9,.} ; ,ԧڨN'G;3oź^nN(]liU<>95{^cZbuBшL7 KEAgxVm`OQX1b<#mdou*NO^mZ4!97u~Yy V?}!#7֗9*\mZ `؞YJ`+9K!&6.D0:!^,yL͖7q:[cG{[AJhSwy_ =#_XϿO2ȜNv5+ݟkd+V%ķVUKAX<>+[ ZN Ljâ+2 9ILJç߮hL`E`찎, (7Z4A#"Ǜ#V%,]4Sh,Xo)>rlFO.3w.Ľȉj4"f-fCnCв ]YB2s}Z0YYWڳqZz2l d_0w(j&exփ^bԤ|gkqr+\F#E;\k'ݘ*i FC`%o4M,:z6̪dV#Ndn |k0 ͨɦ:y~Y[tbRLYi%khR'.T8`'ݘت<3YJwŹ+Ql Z` g/G2>` s\S8c ]8Afk[{bXnܷ[҃OT8wLLV%J$bK&^,FO^\W );:ѭTL:2<{-W'_6u,Wh?~/Чsc`}L(jLe#d=z SnRi4G.MqQdҲ Blf$d "֘ 2DQġ -@RgD2FKwMlu L7M"ԅ&Qreqw!VN'LMM MC𨧈K$>]ج10u\o'gٺ|YhDòuG 2_YOWL6rG1g=}A@.4$4VҍE 1q*,S+e pJo]hM>{8} ӊi9oYD̜v7|^}p u{ | I^oq!6#nޡԯ]4VEC*(f67@`%WQ g gz1ɛjjjb%IV~ר⃅3#^PӜ+6cϒ9z\8 Q7_18!NN@L,- /J9;޹ 9ڿU4ȵ/ B1= l]1wʻhB^s|>o$3ɖiYb*J_~~h\Ic!' \k[i#|6#>ҎEz1jr܊0νt 1Ɯo~Cdi_Ƙo)_Az@2A@+7+vuBʴ?.>h?p2E,JRt,4C0=Wt" SGB_Fla[FSfڝ%viS.Qd d@2n䟷$$%ȅjoH׫ORܧG㙢4i`̓Hq2c)У}uYzg@=eXgC #Zv6"˥gdc䎾KW"e2S?h)%IZ0u\ozH%khVq6aAU};]aJgY|dX$ (UjrgP/ ڍ=rUʣwinEy 8 >Jgv wǒ_gOsY #,ӪP=~%JoXPZ&m;oӷƢgdžL,ڽEvc7dvNF}g԰!Q  ?%&|˹c̰IcM:·-;xR6k7.2ƬP TEySbv( g?iUGig69O*.0^A(3:?}26~_ {d(O u)@i\v L WyZ^.FNSOja)I}\]ȵ6>3UG&!K= ` Cl HRkpS6~"TGU!R6F 2jG6>|OĨ;HPVnJxxu|ʺɞbv'u BDArX_Pׯ%]_"оl](@} 2wDʡP(\@}\2t{aZ, %$< i 'vx",O#K7kS"XE<#fQH~#PP9ےSjQ]FABl4ai4Oo!>h"OwԈ4v?qpU_K',&H`qr#>EqFfT'&#+[IKEr_A~ פwpt%Cx̥}< PA EIR(VUQO [d9+YJjTzBEnPqϓ7%J#,2Ծe ̥}~|4^GWg?.hդqiY'XdkcS{kx óoJX3ԑu#>ghM܊*<&ױU8Ug845 CUQ-*پP.kR_t(IBl:1^&I?̓z@<ԔSLsg\ Y4nMV <` B|PTRR-Eq*CWWjMd'G;XcgkwHXcچҒ4"sbqTr-&6\8 ]fwK]V SwWgrU\B4%,BoE\B!''/iFNv[&".@@}BpyK= oeYMI-#OJNJQRh\H1ip IDATДv@S "A⟚΅d*艅RtjqxIQ|}=Iʋ%1 )H*Ju' &`&K?곘25,Bl:1dfn:jXdf) x@)(BukqBus7i\e??+%҆`E5 /\I4L bQ,bR'( 3BreǠj J߲ 1%xeHYPSܝ[Ħ"f,ujKEQ$_Ȕ<-y==)wbҌ.`bojXߊ5ԛ<;GEN[Α.&1F1.N2O&P5th,Pt5aBL 1+gQz)u4(/1 R)w=*2 1[Ħhʉ!zˉ(b#sRI*1$ $eAܣTDMN7%,BgU* W. &eQ[{;C2u ⚠G>YYDöƆxMz b-)[a(~{{HwXAO,4%,BZMb =ZO8J@;B]KdeJchd$!gu$ܨv ƈ|2%ZY,bP`*dMf !R.7ŸrڒI6},RJL )D?K01ީ7:E}&H#:B^qB^>B` r"rׄ@ 'PYIM=T!dE>#+yi_ZqQ^n,U2hBL}& \XᦄE ~ PԚ<#E46Aw"8¦V?zhHBm~Z0͕^W,8k!4@KLBL| 芾$ %` E1DZb3< *H c$Z?je̬njX3T4P^\kD}ҳ$l2V*'ZDJG@AkDAEIc_.*HgxFOлk4":uNߙT34%,Cފ,S5xfq%6hKU}%D]ी,[\Hi>OH01(KN&)>1tDuMPAV !fx+RayP'<^jVuB 'oJ2`u4ẑO s KJ%yG5_AM fN7{ ^znޑn5@m%r rK}NpZ.IPI}(ܔv_/Xb;.&F *,oL b7gr`7:kRE*Nv-'3\'lRqOѳkh.%Bu}ߠh><Ǒ3/=ARaђbMq%2R=jn e(ƅdx>A+èP^=eYiܳdR6[LE`lBan !?2:p6zP5{ FS>{x{кxy"C%Ql !gK} [XKJMKLj E,[ӗ"H9=n@^RJ~[f͔R*۲]RM{/ٝ_,(jr7E,F)@n\X8{;jbR =#V7ȏ9|6NAWpG59CkpZٯ,Ԩ(y܅UjרH.HvvOt$&.j!D}Mbi  e|6:GײI ԒGtL>$:I acKJyѓWSZ3 i.rO\y<6(.wh.:K b'&6l6!nր|HŇIiyؼi_yO ~9@9jr8qC8)~b>}ǮcL >nPrϖ 뚣ԳrK7h-H/ xY7U,JYD TY ȭ7{h2tlU4b]z ?|yde+1cVLeDNɩFZ$ U|0g(i죧/p\8ڝZ!Ǘ}$N ?uﰸuSĢ8R9ry8Woz.mꑓTj V{4V/Ɂv8çocИ~r% ik4"czXNvEr3˿% й-i(z5cÉ1L`6L bG[zX4Mp3D*xvu y:y iIW󓜒'ne?~[_MΘ{Gv#r}GҒFN|N^T!ֈ"WbmX@ʴ7ebR:98(.N3Rva4e뎒Ƿ !ĭ6cƼ-7rVl8fkCQ0sVR]-iL#d_)3r?9wDL\7ve96GͨP5 %п{S6]E:Eb{uj\ْℸYr'6[|P< 7mqWFLbxp 9@4Vu^`9ciP1g#`ٺl6 7 㯅E: 4"ص#*U2U,Ne:A7tr9vLh>;2%)etQE#.r>7YVAr69ϴ]`;=$ inߍ!w 1bJ%ZXhVMcgØNviNVkL 9}=:'ϑ7\ QYYSN 1LمYJ̜Nl/ߤ 8O[nӜ~Sk-lr 8p6N(Uw.(Q1r]]ɮUR wӜ?|EmS"Yr$A8r-SM{.q\Zʰ6v=G/GےkHY,5=^G?Ƃufit\ jT.,,ϓ9`BV2b ֔<ȵ_Tj f-؆ ;itQſ,~mXa( ۥ1/gGNPթw5ޝ#nҘ@w6L#C f:rIHm?~)UWaf /S΃Eakf LE[.^lLaYHK—3V\7_bT~L@pU_ _ A.e%|;Cz"(IpNvuʻLԴqJ$N)Q1;ְR~m-V3E,^[7 "?xɩCӦY0X8'bɪC=yn?"ϣmbGl(rLכ LO0vLq|<Т1ѳaZr̠KLbw7gԤhy^E.oa_OƘ0;s1\oFuð~- :Gϱ)) ۂijP;sKD,[cz0YņDwg;mGl.[ӡȇ̆{^3ۆҋ V!m+"k*62?3,5:5=j Vkx0K'G;6T1_S=C;'o0qԪMM f3 EkM\*ZƬIf_&k8!1O;mqMBuaB =:4 U*LժrFמ<g'{2]ӷ8f bwH6Fׁ޼*ZMfCQ!_XT7.jQf C>~&fJP`yn(SFFu*3a: ,_"Y1toO/V1{PF-"r})$bs(2X؆ier:y|N.bӥ S5N##n8]hjyaݚ"\c-8m}^fV60X =;bw7' lflޱem>ƛ_2 Fz3QV%<$b:3ݢAk5 V'@n3U[N1 cB׃{6T1DkެԜ+> )0nLs$&cV_'QJ!XMdf`9C !`dH;MY&`q~l;2G6s=?ar5_٥0R`ĀLVCWmxhL6[$;^& f6viɟS `N {\e-xdNXW\MZ)i4"si z8bol`QhV-̙0Pr7j'6U|`X p5t1=g{̝.,iKt$ O RE F[gŠy\:ӂi U qՀrhޠ.ۃl95ޝ w1xmSz;5U|hư"߭)B9Xb-@k V.0+/Ә`{ތ}xcɪCZGD?:' LsG|BncB4Ϧ=q mn(^N_-{/0͡XWm"5*x``w;Q,ڎIZukdy AI :*\0oKT`#-= SŖm˅XP56wS: ^ZeX _|ԅG=fQpqɱp~܊aCtccp!AZгcCy6>`EpDɰulf @΍Rӟ}A b=|=*0(`qvVɃ?"%;a b=)0w[LR 2V FBb*y#J3\Ic{ZE/EBjԣ9aBl(oޝ1Q#щ} (k qr1H i|Q<1K],F@2oRTKy[Vq yUK~!nS뺘U"-W/UY\%2vutOMZuOC׊FӤA ,wKDa#ǜ R(.IDAT;]ԯUWf}䏒qR7]~挙q0.Yાڶdp!f!Ty us}#2EHpa>b£&8bْ&8 b-bc8>b=P8fџz2\9fX]^9#Åcn\9fbY-b%cFKp!7\9f] 1ǬMB1osW\9fbYcp!5G̱s5a=gd#&6W(IzNшeԒ\9f #nE-˕q/ Gφi'oB&Јb/sKĮCWpC(jT+uwHvuq@?1)@@׬qiUp]x>;|ʭŶ…cXXյd|1}+^0˾hREsWp/ϼ˸m`V5wYlZ[{8yN8<)b'O\9hD~瑙)l߿D2wχcס+Z?kP;p!Vd!z;:آQ*8zv!>r @iW'#?xaxD8% w<3+v+YmhxEΆ]h-#?s8&BXBsXeѼa5p;ІՌBT%NqL\DUhgbD+<տWI5q=< 8v6Bk{IB CZKh5Ep¿ ;[&!^Rs8& C EZJxUTSТqu3kôVX95U?|ťD@2ؽښvxvFpvz`P$mer:]s+)s8&_yOLaԪ;_oк_7u ~Xger:6rxy\N$f"5=g8xIFbRr\]K4X#HN@jzl29pe(<PfBT sg%ãшHJIGzF6(hr.jQSG6 _yWcgpl"c_8 jUmmhMTE7pWyxq*>US .FHD>C`HSiW'Tfu r4u5O7q=,9>TD.|< sGP]\<= myl{^RYT\HE>A899r)89uW \s=)qrТ6_Brjp˕)u(RkpJ$_³wd =Pjju`C4 AMQD>u~n.Jw?N +[Y輁1^}3\(< ~s7"΂O&50̂ ;#O*^.+څVa6^u"#/kI\d_Q0i9iyE Lj &P5Xb?F_*I\+d+1-ۋa_,bZi`B86&-ǃǦgfcœux X-Z8olu,{ 9fJ[CxLufƹ+] 51ٛCN+B8l~FQxFO^he mL© ca<{e=i?bn~.^1SV"5-j4"~Z]*JSn,EdokiC?AL}ny99|6&|)iO1>A35,^ Q&&4.!-pW|?[̹+da~BVCYOW9ʋ똯 Md hp̘s,6Ķ >:! |˹YYͷ!WڬB\eڨ@AT?-)ɝ`+)|ʭXpG|<вI B!6͂1iLOds̀WҦTd \%k/j,1W,(5תw@`bNS3d!uѩu]ЪazdžG>AtB=}.wA=Ժ.,\q_αvinT}s7ڿ {c뾋v-+Ш"+N*@prӡ*T*#aS3d 9vhٸƫ|/u0 }uDޔ{Ij4h{_I)zd>UT̫#&6/EgsJ>bjNX )) .QEmn72q]|kC#]]0s| ӺVMЪi9 Q5X1̙0@z{]ۅxJ:aѨnL kUq U]s; }B^ՀMnмAPGk9꽞P BώF.?G}W9{?G;G6PNr߭)zthߖ#Wuq/ΆM3]GpMTcuoA'CAaFr:ǭr a=bb]pmש xEqWRP }&mqWxbZV+2~nm2dhaCzw  # ]<@\x!^ŧ*yP`kFx{N'V*Q=MDrn,(Z'/&yGB hy&U3W\>puܻ/ąk~ YKJ]A&PKR> +Wuxy[IKu~&+[î61P?/Q߅#j\)2XQ,)Y[L .L˻#^y Ip0P"]991L:BQq WQQ"< >Lx=ncO*R~3\T2Wp`e.w}nfR -Qau{UT X*fR觰ͯ'cHF>R2x2(P5MnSPu 8;H_uM.Զv_EJ=mD vbH/f% !mŪyEϋzSOи^"]79%?-.&y5#9l:z-խ=/4ID{>%T ;[KY nz/ux=n)4=06/Dab7Ы oE_ F;ڻ<s.˽Bl&& Kfj9%ѨB-NVcO4$J ceY#5YY"6H*s30#_Oϗsڥk< owte#\XSZ XvJp$LwCAiX aٵ^C@(#_Жҫj]Ց+{*~5ϲboz}w @ p/A_2=l/9,^3F]YOnvwz& ۸y߯yo cڳ\iޣtEV..uX:h^( msw58!V8w&wsiXsFr' Fjaɳm*^|gov)6WnEEXXh {85LmV]73Z^㬜a,;(J{8&Rl:۾AXs^Dw쏡% cMtNNӨn!+؆NL8| q#qW [# x|,XZ^TU) g?zh'WL~ q֛P⡑S jx췷LP+rǧeGݔTΪuۜ.oo|"<'}blaw0>^&>#ޗ1;3ez3s3W g"s㽏j>WWzi;tM;i:`Sd/^㧧+ZXKo b7~Q3>]Y=ð#SE勦[cjy;hEQ3flmږKG8欃iۧ҄ h- wNC&9Ul1xi&^ɮ9/øQm~:VWU30mHlOI@4,_k.vZe뒝.k!l+VU 6?74!oG!bϾ_t7wve2bǝ/a.|;g|0$]%kW͇5t+ E%(FMmEAƒ-ģ\Ef~&!6&]&;:1ua<Z]%(0޲]Wj9ciQ 0y(ih\ab ALٿy?Zc Vj#4q󓱭ןX10SR# %0`ϖ}Kԉq:5ۉth0yaz NVzY77#j/NO i!|f"W`%VYM};0%-²+WP' &:C0Eg/#r51Y^0xI\hEjH ;Veͅ HV3lGא4ٷBAL䀭0`nWb9A\ALD$WMI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DDLyǁIENDB`mosquitto-2.0.18/www/files/stickers/mosquitto-mono-deselected.png000066400000000000000000000163201450213760600252500ustar00rootroot00000000000000PNG  IHDRbb\zbKGD pHYs..*' tIME]IDATxk[@TAE·yq#$]շZsHB^KUuׯ_{ bD "@1 bD "@1 bD "@1 bD "@1 bD "@1 bD "@p6уm1Ƙ,y ,EQcyzz2<FijJ!*ac7WW(' LSjx88ƋI@qq2&c@I6]At:vq>0L ]>Xt:p84Ʉ A f<""b*. nooU bh"U@H '1&2/"h~~<<<>V˴v@š\__ _!6DM D(EAAH"Klt:%%*c~e9LT𡹃Iʲ6ш7kZ5 H/+X@Vh<ۭ꽑 ?N{#>3a0}bpr(2ma6reB4Z C*.{vK{Eĕt:N$!% l} 7旗Z CҦC1DLþuGi*"EƐ]Y@{69u )ͣjmgЀ+7p-;u;v1)͢vV6!jb0Z{7뵙AںGQ |4ڼ1"+DK6 c8 #*H<)r8PG0!ad\,f'z4H{I #cm9*"NJPxJ$ MFl '-xJ $2I\׊-N0N0DKۤƽVcqUqLm3>Vj$]i}#l;R kDi2pjAKP܈&NL LM2;Ҹ^=J6"Gwb1iLjt0d#׊^`TlLykSlDGՊؔᑱIyk-w}} 7 >iͽ +3 $!y8QaXoH% dYIh|IaD 4i'bކ=#"84vXDR} aMmCu!G@ٟpLs& ozfie#O\2?Y?>C$}r pĢ1,^bԥ߿ I{% "NX?FI\ӻr( IgidۗXH5M5 %&T9˜dJ~[̐&)n5MCn0HC QۆL&)fi"N* ďAMqW#ɃXrD 8;G&&I6䲛o5"[#E,=g&KwUl}@Sk4v1ɀNA(Q Z!d>Fi̟|i51߳*$fcg+m Gt}tT5>N^Vje $BsԚuH~{ie4kKG"Na$"P%S#8iwnkT}ɼE6Cc$Ft9~tLjbi^B8˲ndUZJ'4kLA$"-1D,!k-.B٥Z+2q5iPQzMx줈ݝtw\ ==#D8&}D! k6\vt49=e[ɦja!BӔXb& cpnooW1xYmIG=#J"F b)!¦lQ*!bCl6}`#b x{|@IHD6)!osmD QCM]"@ax%.j!=A1|,2Y1) bSAbHOfD Y1dD ¤,"aɅe@(BPT1Č=fYGkY-,̧!pֿ]<|_nO )"bޯ>LC asMr8)qgʲv>w]?M-ɡS\ ꓯ/D ~k7$:J.e,lI.|>~']#bMS($#H2|O 13mJ?k#-f3K..D)/K`͝H$1~rID|aBj|ӄz|mo&soXH3lFwQeʧ$KIRt] q/H"lA"Oj[ƦYJbӸWg:$i!bK lZaBSї/cR3.KضGM!n|:kD t:NoPټ%Y}#G`<Pu 5P5fCjb潄 ۮKK_ڶ?!Tg[h^[S~x*agqҗBɥT/z=SעVEaz^NӴ%GL`;s1~||4EQ8ļ2EQ8)2b!AKi1,M(tim4:t:5EQ3SV۳m!NA 'd)>)3([ 4^! 5 %"(hҜO'$hOSfGtvJ)iJ"m25Y.4[ht4n(]HGđOl׮B=Ѻc~||d52^,־@ jEId,):n%d>Դ$$yÀ`;UZկt5BYo.ִ8^DPdS~}XZt~YEl7CGāHy &E#YBoK*: K>3iCx [z-4^eYF6ĨDPdveY&X,<2Y\Hz&גi~VJ>aʲNf1eY:+%8@kɰ4ig;שR1Ϧ,Kq&///,K/dnR< 3Tn;O;,ˠ;kDԽ/Oh } '62eTvV1c#h6NӏѨvztݠEUziΎY.QImIYfZn}xl6>/MH7]$fؙ9"iR{J:d"~4^! |F.趕G!r(&f]$ 1iz[HFl!⿄,Pۨ #FùB XFMp[\b$ᇇ(."nU1)#*iMI7"oԒ*^`4ij[DHF#~8D|ɛgB ϑ~pn'd%b,}ӻJi~ 4s Prh8M,ElNMF~r9MH1$'2Qwn,M@4Z;9\=^z6O_G4ڱ\Ns9Uduuueө3]]Y HhZ"Qk 1@v<>>rBpX[>j|SzFyxx1@$,Kg{}}FP%6Xbg2D9"B @B2V`0[c1v;Ǧcw?JZZ-3LL1Ɯp|>Ljݝifۙ =_~SuR;ߙffߋ['CmcJ w$\[&,:'Ͳ{~~2gnk%U<==eS)q1www!TCPMvΥNOB+h|\fF6Yi,6i-  Xmn(R=-)޷V=(LǸgd]Dl9j4ztQfc{zT9iwhTy9%!Ut:uRk0Tѳ*zsuuu 2F"~/__ڴl6 M1"@h>l2FX4iSMqqǻϪzʬv#;=z-fkX,V~/_L^\ ;SeUBCjCu}պb#Tl!n#8ՙYעu{XV묺5?bk.qu6$5vQQD|Xӌt}: )/l&.MdԹN܃V jX'yeyq:gi4ulFėD"|RIeYIU=)W}e.777{sdTXu%Wt:SJ4 JrNi;7럶#t}8a@mePC6iތC|?p,KbR۬Su 18eCr@ԙʗeiQQ%K+UcvPp 'fsN`#&!te*2f /dA"∨j0]Ge,V8.ٵPM*sqK#5lFUt>{r)WIX1PITi` z2TJ:eЁ?^צ#J;﹊qUNʶ}Wm?fB Nᘚ>( :'[ F~_VITyw#nj=̼Sc"3*OF4Zt6ǤKFdQ'ir2K86m1Tf߫=TВW5!eDEFũ\gk) ݫ9eleY;C}___~OE]Vڱ\.EuS1X7h͆W!cy{{;{>N˪S:%fF #Ę*R(˲zRC]N^rϡ5(˲ѧ5qחR$N^](u<:B*l.˿#,nQeYyRhWzDZ~␯Dt:'3ƪG+O:Z9ʒm'o?&nH^z9Sz~~DSF pJ$e"@ƧN@D"@Ɯ5 w)*p8!68Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\9p!p8Åp b10\ ~ =z~=MXH\zX,¼iѳScQ$Y`-; z÷F5 Ria\H|>?zwnk@$$g;/P n. 2' ~o[ E=H48ӥ)M䕿򀓃-.Ehd b=2itwxc"mׅR ܌q83vhGLbH/H~a|B'‡tz^PZpvÕ1|y1*D"1=ZmPY+<}XA% ~>BQΣp#`УmZ5d݁Zmz…X v6V>/s?21r/8x"L*vu{򨢗8惇V/:ƈ)Rk`gWs^jx|o4oG} Ip>,R4փ ĵ۱Hacxb墱CC+Fxzwn y[+XRX^gnp!VA`]oL fwxƻv;9hԏo[Ktkwij&S_& ?-xU3T*q9|<}N_:ߎEJ q\ JbhTqcЩcOem:'WkHHJgcsFΚ/\o^; )<93DhPOAB\qowB6 HHJoHxL\2b➢Cp}f8 D <ǴٱN׏[c߉!jϹv! :UP$WeB\ jX \/ފ =JL;ѡUsWX#@Q<20O0f~1~{^~!f-[HQѧKS6c͛B\/>Ϝcp2T4^tϜE$e?H˫+"Ȯ`t7,gdcڂ?q7*AӞgCPykJ$bT3#}}iTfDl*MLSL^o6w)yH"0~xG*˺8a&-{BsHtk& |0' @,a>L3 Le6!S2 ~L~izwn%3߆%6R ~*˚}l MJ%]"1L LDdp!зkSdcӈKxFnyL(}zt7ѥ-&r /?,=GTlT$_sS Gve#Ay,olδ#]Xi1<֛ >޺&1Ȯ|.'.tGxR~?\4>8PBL|uvmSRk^/*Xtܞi у+U#dfal/n·xI$b]7 cJ2~XgHq8r(N`Їꆩc{1M4gD"`؞U|ܱӷo6CH Q2NѓcuiJP?aƵy% %NvU6_r~߿% mƜ*! s[@Bx[_Je9Kέ˃ulȗ|mS&7b^ a؀6L'R śsGYzuj^i3eJ>Սٍ0a2SVXrIN$Y,nY B->Md^j dJ? U Ec* v~hiUGiR |2a>]™>zmVrf--Yp>ڃd[q3`LGEco긺8`qhPܮ)c{1]/[[I*yEwFm0k!p2. eE1w+grW+vjXt@% }VY9Ur{7mN'ڶkyV{ކY L6KrvBnwPgG[Z<MR|kcPʼnP<93+q" I_ݹ\a֬_f+ĭ!={)pA kUݫ`@YTl-lcs~n!Acrlԏ|}86vW3鶬^rӆ PAg)5\ TEbZ+}4O@*5>fvu2rX$BώtQ\M 'G[#L(Y;Cr#K7hTyCG"|˘;#fOy)gh\Ie23+}+2_oƌr ٰ1>9 SB]fuaj_$W`ᏻ4R û }LzE, }0a8w[L,q73& ԞeUzUMt %.^"ll{f)-&|Ē?ASܹGjVU@ :!w KO)!}1J>!}_\3|8{N_ت{1>HY1gۘ1K!nTVNP퇤"бU-TxwHiy^#l`z1{X>ws6$d{F$bʼ/+g<=Hds۝ڳ:ʭUgzIcޞ4@#W:9H@FL]2KVye7R}R|1La5~\Zgsd៯x ,]4 y3UP9zD:h90/!/W[1cvBܨ^ •mגeµ ;:u~ mñsDiTǪ%yyVkg]`Wn\3l?p \ &opS5Hz+f',ЭxrێBBm}=4Ї>=#[=^$W`=%vÚ5/Wz{]#mpC2\E/fdh8f.B$݃[C \~ _#_`Ӟ81!&En8"DlФ,T*/(m ~ڍwh!; IXbf j7>'@&X&#TrBf#,'R KGDoMIt"c$v @;׏;+{iZ%I?NL#J$nL $D"jaPb@ORIjxU% "1KՏǯcQP9rg]wv]5YjEI+K 𤹓=">{VbL[s{K P(IYoOFPBR:VqX<&Z#>zђ!5&=IXNɗa:z=&g)NuݩH}'1Ҟg nVVr9 k+)zN-Tw"s%#~ޮ_ W?_O%0o0~pDPgYJ 뻹8tB`IYf5R|gYx!V}Ɔ1GaqMgf6ڭXm,$brNCn ih{u35d29.E|#\ȘD (E{Rn VwUKׯ_3TS*cX>g/aR?v!ͲmIXKR ?ݛ^ %R4aB,Ĩ"j}6.vqn^!vũIvD`)g￁ g P!l1>/S} mBcJUScWwB&uuLblXOQUam%%#:"$/xy,Tl.7[ nW ~j,jA,H$ -jX[ /_x^VMۗe29TU=䂭qI%i)igKlB@O_HWw/$nNd'vʎ8|K PG{\,JL.;3̈\h.d@q @{)OS3q3g]ф4 :޼ToJX˳;gU{>@f^#óoL;0ٟh7@Nnrr HmWAa}ۣOЂjTm2j`;RRu IDAT!£lji0xyF92UY :qӄ@1 D qF̲ #{iT.sec"йMvsvj|TS-(RiPߒ!Hǘ0 ! 1ˌgIFC;Ybpo^pq{@"<3;Έ/m(,ݣL3b⦤ԂψB\@E"5)!Kjpo}C NMn|T)LuT76C["&Z0)7’@_T'$. eS(u@?17MN0#B}X}^hP^2N 9VIf#>@NF2k$|N̼+wX=g|,(j%6sR `aAԟ*,)3b2"@ o7R6j `؏ , 瓜n^4&BO@󳦤{.NTG~}IeON>JMTwP5 bX" 1ʒ`)J礤 , \8nC=7HnUVSSFn,t'.FUXf*5"#R2tJ %AD>(+H\ bʪhL&BEzӱx?P.'>PKx`D~ QI3^PSjjP``%MFUmSyy—`gGSCO)DBXZ$dЩH$bԩ%<$yH.ƒ叚Hbz), ,bj#= 9N_ܮvj$[?;PKAVU!RjAЄ+@݇yVsFە5|>&ԲȰd^:tG׶ ;%;6n# P\,r=" >KO r|c,XP"9b,8Zdb&(GQD{ڑ)ڑ3&D$bŒ_qmrcHjr%JpUHvCjygYP\X1 !Ԋ}@ԹoM!k |u=Ry-o7V4I*>X.$$ n?_GĕD z43Ls#k |S=RxoRn`٧16G"G4;\ ϪZQ]J=K…&*m, 'Cñ}Em￈7%8a.g.E+K ,1YhפBb%4uCD,4D^m^ӿ5m{}HmB ݔ }9YJ-Цy] %7YςT*g5d:2FF)q@qm//O,A/RZ!v+"k@ڔԶ4uFfҶHpf~%ݠ7@=\{ qcl8I*-q>SU4!Kףm;.*Jeq80 UJ. %5'ߗܞunM~F7BT*K+CCbz3df!)m&2vܶ}zCO{ج,-$*zFQI?֭,-оe=5"K*O#XY&_Ɔqjz9;ٽ'v.dr5o(BI77.δͻ8yɡ3$?K$dmnNd[mD4^vN>}1!z~dsmȗܖeVVv.; oј6#+$׫]"O{֍b3A:4HNʬ盥.r=#O ]g~> %@Ԝ賷tb\'G[|5c(fOH.P½%UzUtn@7 zm#볘ID<DAq,;O$YHh1LKymeT Bk0 EثBr+KNn\Q|ˆ.dZRÄ{%oYܖ Bdf%ĹydQ>d3AVt!~ۄ%tm:t,_w|89_?NFN >f;7_?Ng4 W@G\%Ln'!b(gf>c/Y\EsyLb^>Љ0r[H#۟ &R\>ޮ X3 u~^+/n.03v9Sc?!8*i Gv%JI=N.3r3Sߚa ƌ 1ÏԬa-R;B M=;*Z鋴2%mQQo=ɔ^R\1Nگ+ QܡZcNLTqm$}p? '3#wS^ŰdS`@l(Nkjn&,-gfv43'VOޱD", jXM\V#gwbƤ~Lcq9HT@B]YcyƍqOM}ȅx@ިL?^e*Xݽ Ln/+or(keq_ӗ-aјo=]l@ˆZ6E;/-XD!GP{cXTVDzɗVV$ǂva/!*l_?X_=Gbwۙ RR9l/_gG[tlE[>JשFx1cBceIBFj,ݙ7+;_'wЧO|\;$jJx4dsYM"9]{?~P}%J8p"շk3Rx_">܎ kƧr'/dAL%zW'a!ê۵)s9w:>Y9K4VvO2?qU~ݚOD=;6&[HN2psBmKyfYڇ3bRKč#Hv AG"LjX %2'rw|%J^/d_ k+)970ӵ0ԩOޣ'/V. ev{׎!xҍƊY 1ZC[1L :cX1o9*D" [rr }mH&r*c'eNᤔ |V|f?rCŁ즨 ;[+,; [ Q.+MrJ%[;zq1[!|3ZԨ~ AP@qy]fM"'})@EǨWc6ˊ.vbpvN>u>)*.`zQ_xuiլjd7'rUYWoѽ&E"7S7ș`Zygp{|>*31uF }o(aP h% EvͦxN=G!=!>e[2g诺X,!;_\.W)$kPLzƎ 1 扔Lz {c{Qݘ%sLi؅"6 G4V {?o8!_7$o !&)&@N HGvqԚ?ڶzW!5=ܞb\5Z]GNY'S2Ž' e!cAxkՍ=Ň+ _.cG+ gr%2>|ހSVbס*Ov֘ XDLaJ[ÍlQ*8VzTПqIN@'dTAX(GID1R'zth2g eJ[mz[ EF=ݨXUGPC_4kT eL( Kzwv!=Hd2SзFIz{Yzvl {_Hn~?wh%zߝ{6M;䶽:7acAwJ {;k|?oE8}|~7XQ(~{Bu1]|˞|x36 ADrr MLT&+-,-0q$=)Ͱ³ZW, FkPi\`Z8`;ݱz84IGOdΗ`(6bcG&cᏻ6TUw{ݕ(.@p2=H6 ȑrB/)c8=#<˒J.W`H`pز:tc55S@.W5ǎz׷Slyi1!;<3G& 1?O7O4 o ѳ+|LJ<71%TP* țqby ?BO_'AE" hC>f@P⻵l}ƀ9&ʲE,Y)ױ` Іf[ӕ,T!/1͊{vh g{rgo#>VTaַ Cğ@q_3E9m7hٰScn]PuT !}9rR rl:NnBvN>>[)1aΦ\vi꿎1͆ R ZM8geqLS¢olA?Ta RӳѼ?pˌj|P(S!j/FʭPq6#THPial0wK +󒐔s6 6j%6E mƧ` HHJ)Jm7OK8[?VԨTB|meި`cmIn0e+):#i'MMB,$y)I6]wb➒X[2| LJ%2rBNثn:˼UqkJT:!NIu'G[2GB8>ʲuYnz!^hk:f.ȊXnbe~!' ߆i6|mj6* LX"FnU~J%l>%޲'+~=hTlM,͛ *@0lomJ9}R)8Q .ъ˯]R2ѷXTb#XɆ,S$5-{BrfoUeş8.}%B 8X,GdR ,`/eeM辦`ֲm 2}+dz>"cWNbX Ӭ8:`CJ ,YGqdecMLS j.uT*l>ʹQ=& _'CC^/n!-긷;N-w> -׼+Vuo(w+pοo"/.EVXLSEVkInVH? 8{^^9S^`E ># dmI>$1L+Kۙ\5jFQ(za+K ̞:F3ܯ[NV kB\#gofxޮ4FQ*C\Q|nx2v7ӈ.ĥP*L&~ fh qӮE] f*Un]B\ljiضmN$*欧QqMfO% 1hEsc 6J%;TǜBwdː#H#&Ks5ӧKS ϮW^ƜB1O|M}0"N ~iXf~ЏGizkap!@~ _D%3wz&wW',|R=\+s-p!ݨl܏-L#@w_+Wus?["BCVAN1\u`gp[W3Ge^ .g WdlqVO#2o뀬Hyw[e=R(+z%D6@wB/ os]d䨇 $?׫ ) %Js^*/ ;[!X\XIDATTQ#3/ }>|EseEr,q7ew㨦 ,?=}sWyűe|Ge^p!ȚkMsWKԅ U头r%Jf.݊<͹# .1k6!efY}= ASzr!4/rW֯sGi=OB}>ak +>l+*.U<6P#_3=~pq{RJjT 8I*GFP(D?x-p!4uJ~݃,R ?Ş#WZ+$gfؗdXF.\zf ڶU]ħU*{rr b`gk^dɠ0 !Уc"$]ХM :i/XYνG8|&N܅BC6h:5FF_:7cpx龜-MszR<3$µ8s1/SX"[@toM}`cmBģT\'81M/7crU{^\ Ѕ>]Bj!Qy~l=Hԩ^SC屜|Qn yvMa98uv5WEZ/_Qn.='Ffu` мe.I)qVb!#Y GpS?xTs~%B'i{1\[("NL Q M:wH۵@oaJ%>-VRF?LA w/L4Ƴ"׃̂wAuunS$WS7?瑜Av=?O,d۲(J8k:Z]psđнCCٱbbr%lT۸,}F~&#lXBR:FLYԱv*'b/Ff:G&f6\M(b:6ƸaYYga˞8ByϔbF ~hpӹ}33lݨ/0eLOH$;tju㓅!!IxD큡oH(YH#:7P{EzmH!|aw]_f:7lm,:8r~0 S%37|2 ׿{947} ,/HK²Y#==-HKpwuŠζlZ XJ-0c} SE4ΎX1w4Z|HO&E4;7گí#s_-$¥Q*V/gy6^Gl7'r{'̜Q]Q n.XtdniZ6U_sbfN](Rպ$ǐk+y͆)_ IJ/GF/"|<7&d{b}ЪY,tӌ7{v߁]BnJ&_m~[!^Ly#uk|-Ux{`gC!Ce a-d0i娍wt^-ޯtBP* nT.WfrRr% 7uPJ%h?/ȜW(},a-Y9k<81 g/E"n/D`骽ҍjj +Mjb۝bE YJ%NG/cRl֕pC& &~U'Vǿobg^ZqѷkSmp\m[E 5T*qb$=}7^5Z47{6GƵ51Ͷ8}!455xHlw6xWcv -^]?I$b̜O'ψQ 8rQ xkk)jy}p=tjՎ8}1)Ze ;Dzw&*Ͻrr 0uF%߽{-\_U/ߏqgc8s1|I{{]zֿFDYԔ4B|2.k3̬<|,--гccmIl'䵿`ٚ}prE; .ft">M30wv XvN>\ҶfO]O"c.m*;h:y㕇yf~a\;]Z@{vh*bSpB8<9cľWb E?x=SA&"F9yh /TG uk{Tǵ۱XndpLHJ5LGnmOtnjSR&'᫕{.J`ݦivRJ.OkCQp3?ԮYM||4wJ. ?WjM sR3 vfǼ;t>l{3~/ކ}4g'6\`6Q.߈K}FnO1R)x3ZmPҳpMKBNi^dg=hXNek#>!U9oU?fH<>͗Ux<SQO7'U_"+;3ڌWBmXk+J!-е]ֱqakc*gaOr.ᗍG5F2zh]M f/98{1BsTJK~F&=n (A]7" ^T{\_:9WOŇI.]jA JE8a̷ f?|_P*_- +G3M|46q"D2vhyCT0{! QxG7q"|gÉm*y{VZ(عj2#"@IAT|^_TqR?/&G܂W 6Xd<:xE}]bhl ij&Nhx)̝; 4l >}' hg  I>卭*4z{j]ݥ'~<"|k^N%ǥ #}KbX+c|/8VurE'QNijx~B3W*]_ ) qV]޲h^OXP3AЖ$WYRR_[u^VXwc^E^?,:ŝϣ3z-\+" 9fsְJJ}ե6W9S8/OwVEi0Lfky!j?qA"&ހGV^u{KK4T-.-N!\Sk] տ%͑W$`TtjryMrf/ĹSRYp,ֶZ ilOR1aƯ}ۜmݜQj46s:%^ʻB[ uuaTC{!'k'M-FPQsI A-B"Vk>ЅjZ2bz/bgp),F:9`Ƥ~*iICЩ?P]k[[.VV8W*TpVԷ(Y)z`h^M mbӳ5x< (3n7U7}~[BY;piZ4E Nk~i^#6W٨P(eѮ2ZEZzφL 4e|p)…؈x$U!5A[CRJ`O' ¬ek"G8ۣ{@gY潎fnvnԹ);2R]>-oXŶ*Y:"|H;()m}l E;D5$ՠaycM:g1*qF;;B3Q{K2n3UY zRvn>]K % \`B5>)/a`}a, Vbih_*]M?a Zb2U}4Y|x.Ԍ\\׾ɪ.c1. wUc2a6.M6Fzl-nҥck,[0Z8=Vsw̙x'=~hɣ{(mGݖ]Džy/x9@ӱ펉ghԗ1 :k7m>CU*G׋Ǚ׶f/z|` ޷M[LˎMNho#d+x gk>^sI:A<OL.O4|_UEnb(X(S Y̙kpEN;#yh;oNC[!M=ҧ6`#fm}q82FG/}ulP1Wb_z=Ԫw:q[=ϛ|R^U|hͭ ʶbA'_o~qvj=;~ܱ掜b8p2?Mjws~x>))sbqLEj<;<ꍡس`P{6?:W+]9CF}vB"Vs9h:a@ꔕ[D;+D]46_'Tee~>Y&YM;c`o)oo a )>-1_W<8|[5g`PđW$>њY}2G{x{5W374rnrZUuuqBpk/,E^~1odNVhHJ$Ep=%)FW  ~uԹ8ۡ<¥EAQ-dd#5#^_YZ QRz%Hg꺎AL6C`M8"DplƵtL@~A t:fOzތc' &"_PghkN&zds2j {/h QM1&%$g`5+Bg;աe01٬d}&D 'F@W?8) &¤/, { 1ټ0V f 4ȒD0qpHaB٧C6ALZ}է@Y)٘4w-F v^OllXc 1Y )XxgD 7{PcHIԷSzC~K͆bPH渞lz џ &2A&pÝz8/ci_X*1d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$H21d b""DD1$cI &"ALD$=.Ka%sIENDB`mosquitto-2.0.18/www/man000077700000000000000000000000001450213760600161002../manustar00rootroot00000000000000mosquitto-2.0.18/www/pages/000077500000000000000000000000001450213760600155755ustar00rootroot00000000000000mosquitto-2.0.18/www/pages/documentation.md000066400000000000000000000042001450213760600207640ustar00rootroot00000000000000 # Man pages * [mosquitto] - running the Mosquitto broker * [mosquitto.conf] - the Mosquitto broker configuration file * [mosquitto_passwd] - command line utility for generating Mosquitto password files * [mosquitto_pub] - command line utility for publishing messages to a broker * [mosquitto_rr] - command line utility for simple request/response with a broker * [mosquitto_sub] - command line utility for subscribing to topics on a broker * [mosquitto-tls] - brief cheat sheet for creating x509 certificates * [mqtt] - description of MQTT features # libmosquitto API * [libmosquitto API documentation] # Other * [Authentication methods] - details on the different authentication options available. * [Dynamic Security plugin] - details of using the Dynamic Security authentication and access control plugin. * [Using the snap package] - specific instructions on installing and configuring the Mosquitto snap package. * [Migrating from 1.x to 2.0] - details of changes needed to migrate to version 2.0. # Third party These are some Mosquitto documentation hosted by third parties. * [Steve's internet guide] - a broad range of documentation and examples covering Mosquitto and the Paho Python client, amongst others. * [docs.cedalo.com] - includes documentation for both Mosquitto and Eclipse Streamsheets [mosquitto]:/man/mosquitto-8.html [mosquitto.conf]:/man/mosquitto-conf-5.html [mosquitto_passwd]:/man/mosquitto_passwd-1.html [mosquitto_pub]:/man/mosquitto_pub-1.html [mosquitto_rr]:/man/mosquitto_rr-1.html [mosquitto_sub]:/man/mosquitto_sub-1.html [mosquitto-tls]:/man/mosquitto-tls-7.html [mqtt]:/man/mqtt-7.html [libmosquitto API documentation]:/api/ [Authentication methods]:/documentation/authentication-methods/ [Using the snap package]:/documentation/using-the-snap/ [Dynamic Security plugin]:/documentation/dynamic-security/ [Migrating from 1.x to 2.0]:/documentation/migrating-to-2-0/ [Steve's internet guide]: http://www.steves-internet-guide.com/ [docs.cedalo.com]: https://docs.cedalo.com/ mosquitto-2.0.18/www/pages/documentation/000077500000000000000000000000001450213760600204465ustar00rootroot00000000000000mosquitto-2.0.18/www/pages/documentation/authentication-methods.md000066400000000000000000000103061450213760600254500ustar00rootroot00000000000000 It is important to configure authentication on your Mosquitto instance, so unauthorised clients cannot connect. In Mosquitto 2.0 and up, you must choose your authentication options explicitly before clients can connect. In earlier versions the default is to allow clients to connect without authentication. There are three choices for authentication: password files, authentication plugins, and unauthorised/anonymous access. It is possible to use a combination of all three choices. It is possible to have different listeners use different authentication methods by setting `per_listener_settings true` in your configuration file. As well as authentication you should also consider some form of access control to determine what clients can access which topics. ## Password files Password files are a simple mechanism of storing usernames and passwords in a single file. They are good if you have a relatively small number of fairly static users. If you make changes to the password file you must trigger the broker to reload the file by sending a SIGHUP message: ``` kill -HUP ``` ### Creating a password file To create a password file, use the `mosquitto_passwd` utility, use the line below. You will be asked for the password. Note that `-c` means an existing file will be overwritten: ``` mosquitto_passwd -c ``` To add more users to an existing password file, or to change the password for an existing user, leave out the `-c` argument: ``` mosquitto_passwd ``` To remove a user from a password file: ``` mosquitto_passwd -D ``` You can also add/update a username and password in a single line, but be aware that this means the password is visible on the command line and in any command history: ``` mosquitto_passwd ``` ### Configuring the broker To start using your password file you must add the `password_file` option to your configuration file: ``` password_file ``` The password file must be able to be read by whatever user Mosquitto is running as. On Linux/POSIX systems this will typically be the `mosquitto` user, and `/etc/mosquitto/password_file` is a good place for the file itself. If you are using the `per_listener_settings true` option to have separate security settings per listener, you must place the password file option *after* the listener it is for: ``` listener 1883 password_file /etc/mosquitto/password_file ``` ## Authentication plugins If you want more control over authentication of your users than is offered by a password file, then an authentication plugin may be suitable for you. The features offered depend on which plugin you use. ### Configuring the plugin Configuring a plugin varies depending on the version of Mosquitto plugin interface the plugin was written for, either version 2.0 and up, or 1.6.x and earlier. For 1.6.x and below, use the `auth_plugin` option. These plugins are also supported by version 2.0: ``` listener 1883 auth_plugin ``` Some plugins require extra configuration which will be described in their documentation. For 2.0 and up, use the `plugin` option: ``` listener 1883 plugin ``` ### Available plugins * [Dynamic security](https://mosquitto.org/documentation/dynamic-security/), for 2.0 and up only, provided by the Mosquitto project to give flexible in-broker clients, groups, and roles that can be administered remotely. * [mosquitto-go-auth](https://github.com/iegomez/mosquitto-go-auth), which offers the use of a variety of backends to store user data, such as mysql, jwt, or redis. ## Unauthenticated access To configure unauthenticated access, use the `allow_anonymous` option: ``` listener 1883 allow_anonymous true ``` It is valid to allow anonmous and authenticated access on the same broker. In particular the dynamic security plugin allows you to assign different rights to anonymous users than to authenticated users, which may be useful for read-only access to data for example. mosquitto-2.0.18/www/pages/documentation/dynamic-security.md000066400000000000000000000602071450213760600242660ustar00rootroot00000000000000 [TOC] ## Introduction The Dynamic Security plugin is a Mosquitto plugin which provides role based authentication and access control features that can be updated whilst the broker is running, using a special topic based API. It is supported since Mosquitto 2.0, and should be available in all installations, but will not be activated by default. ## Concepts This section describes the concepts of how the plugin operates. If you want to find out how to use the plugin features, look in the [Installation] section below. The plugin allows you to create three main objects, `clients`, `groups`, and `roles`. This document will use the term `clients` to mean the clients defined in the plugin, and `devices` or `users` to mean the MQTT clients that connect to the broker. --- ### Clients When you want a device or user to be able to connect and authenticate to the broker, you create a client. Each client has the following attributes: #### Username The client username maps to the username provided in the CONNECT packet when a device connects. The username is unique across the plugin, so attempting to create a client with a duplicate username will result in an error. The username acts as the primary key if you want to change anything about the client. #### Password The client password maps to the password provided in the CONNECT packet when a device connects. The password may be unset when a client is created, this will mean that devices will be unable to connect as the corresponding client. The password can be updated at any point, but only by a client with the correct access. Devices typically cannot update their own passwords. #### Client ID The client id maps to the client id provided in the CONNECT packet when a device connects. This is an optional attribute. If the client id is empty or not provided, then any device can connect with the username for this client regardless of its client id. This means that multiple devices could connect with the same credentials at the same time, but sharing credentials between devices is not recommended. If the client id is set, then a device can only connect as this client if the triple of username, password, and client id all match those in the client. #### Groups A client can be a member of any number of groups. #### Roles A client can be assigned to any number of roles. A role gives the client access to different topics. #### Text name This is an optional text field to give a human friendly name to this client. #### Text description This is an optional text field to give a human friendly description to this client. #### Disabled A client can be set to be enabled/disabled at any point. Disabling a client means that any devices currently connected using the credentials for that client will be disconnected and unable to reconnect. --- ### Groups Multiple clients can be placed in a group. Groups can have roles assigned to them, so using groups is appropriate where you have a number of clients that need to have the same access. Groups have the following attributes: #### Group name The group name is the primary name for the group. It is used when modifying the group in any way, such as adding a client or a role. #### Roles A group can be assigned to any number of roles. A role gives the group access to different topics. #### Text name This is an optional text field to give a human friendly name to this group. #### Text description This is an optional text field to give a human friendly description to this group. --- ### Roles Roles contain multiple access control lists (ACLs), and can be assigned to clients and/or groups. Roles have the following attributes: #### Role name The role name is the primary name for the role. It is used when modifying the role in any way, such as adding an ACL. #### Access Control Lists ACLs are the feature which allows access to topics to be controlled. Checks are made on different events as they happen: `publishClientSend`, `publishClientReceive`, `subscribe`, and `unsubscribe`. The `publishClientSend` event occurs when a device sends a PUBLISH message to the broker, i.e. "is the device allowed to publish to this topic". The `publishClientReceive` event occurs when a device is due to receive a PUBLISH message from the broker, i.e. it has a valid subscription and a matching message has been published to the broker. The `subscribe` event occurs in response to a device sending a SUBSCRIBE message, and the `unsubscribe` event occurs in response to a device sending an UNSUBSCRIBE packet. The default behaviour of the different events can be set to allow or deny access. The default behaviour applies if no matching ACL is found. The default behaviour for the different events when the plugin has first been configured is: * `publishClientSend`: deny * `publishClientReceive`: allow * `subscribe`: deny * `unsubscribe`: allow There is some overlap between `publishClientReceive` and `subscribe`. In most cases, using a `subscribe` ACL is sufficient to provide the control you need, however by combining the two types it is possible to e.g. allow subscriptions to a wildcard topic like `topic/#`, but deny access for device to receive messages on a specific topic within that hierarchy like 'topic/secret'. The different events have ACL types associated with them, and it is these ACLs that you will add to your roles. Each ACL has a `topic`, a `priority`, and can be set to `allow` or `deny`. The `publishClientSend` and `publishClientReceive` ACL types map directly to the events of the same name. The topic can contain wildcards, so allowing send access to `topic/#` will allow devices to publish to all topics in the `topic/#` hierarchy, including `topic`. The `subscribe` and `unsubscribe` events have two ACL types each: `subscribeLiteral`, `subscribePattern`, `unsubscribeLiteral`, and `unsubscribePattern`. The `*Literal` ACL types make a literal comparison between the topic filter provided for the ACL and the topic filter provided in the SUBSCRIBE or UNSUBSCRIBE message. This means that setting a `subscribeLiteral` ACL with topic filter `#` to deny would prevent matching devices from subscribing the the `#` topic filter only, but still allow them to subscribe to `topic/#`, for example. The `*Pattern` ACL types allow or deny access based on a wildcard comparison of the ACL topic filter and the topic provided in the SUBSCRIBE or UNSUBSCRIBE message. This means that setting a `subscribePattern` ACL with topic filter `#` to deny would prevent matching devices from subscribing to any topic at all. #### Text name This is an optional text field to give a human friendly name to this role. #### Text description This is an optional text field to give a human friendly description to this role. --- ### Priorities If you are working with more than one role per client or group, or more than one group per client, then it is crucial to understand how roles and ACLs are applied. The order in which checks are made is determined in part by the `priority` of groups, roles and ACLs. Each client group has a priority, each client role and group role has a priority, and each ACL within a role has a priority. If not set explicitly, priorities will default to -1. For each of the group, role, and ACL objects, checks are made in priority order from the highest numerical value to the lowest numerical value. If two objects of the same type have the same priority, then they will be checked in lexographical order according to the username/groupname/rolename, but it is advised to use unique priorities per object type. When an event occurs that needs an ACL check, the ACLs for that ACL type are checked in order until there is a matching ACL for the topic in question. Within each role that is checked, the ACLs are checked in priority order. The roles assigned to a client are checked first, in priority order. Each client group is checked in priority order, with all of the roles in a group being checked in priority order before the next group is checked. As an example, let us assume we have the following client, groups, and roles: Client: `sensor` Groups: `temperature` (priority 2), `humidity` (priority 1) Roles: `hallway` Group: `temperature` Roles: `input` (priority 5), `output` (priority 1) Group: `humidity` Roles: `humidity` Role: `hallway` ACLs: `Z` (priority 3), `A` (priority 1) Role: `input` ACLs: `Z` (priority 3), `A` (priority 3) Role: `output` ACLs: `Z` (priority 3), `A` (priority 1) Role: `humidity` ACLs: `Z` (priority 3), `A` (priority 1) We are also assuming we are only looking at single ACL type. If our client `sensor` triggers an ACL check, the ACLs will be checked in this order, and the first matching ACL will be used to allow/reject the event: 1. sensor/hallway Z 2. sensor/hallway A 3. temperature/input A (alphabetical sort) 4. temperature/input Z (alphabetical sort) 5. temperature/output Z 6. temperature/output A 7. humidity/humidity Z 8. humidity/humidity A This is provided as an example that covers all combinations of roles, it is recommended to use as simple a setup as possible for your situation. ### Anonymous access All of the documentation so far assumes that you do not allow anonymous unauthenticated access - meaning devices or users that connect without a username. You may wish to allow anonymous access, but still make use of the dynamic security plugin, and this is supported through the automatic anonymous group. If allowed, anything connecting without a username will be assigned to a group that you define. By assigning roles to that group, you can control what anonymous devices can access. ## Installation To use the Dynamic Security plugin, it must be configured in the broker and an initial plugin configuration must be generated. To configure the broker, add the following to your configuration file. Linux/BSD: ``` plugin path/to/mosquitto_dynamic_security.so plugin_opt_config_file path/to/dynamic-security.json ``` Windows: ``` plugin path\to\mosquitto_dynamic_security.dll plugin_opt_config_file path\to\dynamic-security.json ``` On Linux you would expect the plugin library to be installed to `/usr/lib/x86_64-linux-gnu/mosquitto_dynamic_security.so` or a similar path, but this will vary depending on the particular distribution and hardware in use. It is recommended to use `per_listener_settings false` with this plugin, so all listeners use the same authentication and access control. The `dynamic-security.json` file is where the plugin configuration will be stored. This file will be updated each time you make client/group/role changes, during normal operation the configuration stays in memory. To generate an initial file, use the `mosquitto_ctrl` utility. ``` mosquitto_ctrl dynsec init path/to/dynamic-security.json admin-user ``` Choose your own `admin-user` username. You will be asked for a password for the client. This user will be assigned the `admin` role, which has the following access: * publishClientSend: `$CONTROL/dynamic-security/#` - this allows the client to control the Dynamic security plugin. * publishClientReceive: `$CONTROL/dynamic-security/#` - this allows the client to receive information from the plugin. This is not necessary in the default configuration, but is included in case the default behaviour for `publishClientReceive` is set to `deny`. * subscribePattern: `$CONTROL/dynamic-security/#` - this allows the client to receive information from the plugin. * publishClientReceive: `$SYS/#` - this allows the client to see the broker metrics. * subscribePattern: `$SYS/#` - this allows the client to see the broker metrics. * publishClientReceive: `#` - this allows the client to examine the messages being published by other clients. * subscribePattern: `#` - this allows the client to examine the messages being published by other clients. * unsubscribePattern: `#` - this allows the client to undo previous subscriptions. This is not necessary in the default configuration, but is included in case the default behaviour for `unsubscribe` is set to `deny`. The admin user does not have access to publish to normal application topics in the `#` hierarchy by default. You are strongly encouraged to keep the admin user purely for administering the plugin, and create other clients for your application. ## Usage All control of the plugin after initial installation is through the MQTT topic API at `$CONTROL/dynamic-security/v1`. This allows integrations to be built, but isn't the best choice for people to use directly. The `mosquitto_ctrl` command provided with Mosquitto implements support for the dynamic security plugin API, as described below. Other options include the [Management Center for Mosquitto](https://docs.cedalo.com/latest/) which is an open source web based tool for controlling the plugin and other features. The Management Center is not part of the Mosquitto project. ### Using mosquitto_ctrl with a running broker The initial configuration is the only time that `mosquitto_ctrl` does not connect to a broker to carry out the configuration. All other commands require a connection to a broker, and hence a username, password, and whatever else is required for that particular connection. It is strongly recommended that your broker connection uses encryption so that your configuration, including new passwords, is not transmitted in plain text. The connection options must be given before the `dynsec` part of the command line: ``` mosquitto_ctrl dynsec ... ``` For example: ``` mosquitto_ctrl -u admin -h localhost dynsec ... ``` It is possible to provide the admin password on the command line using `-P password`, but this is not recommended. If you do not provide a password, mosquitto_ctrl will ask you to enter the password when it is needed. ### Using an options file For convenience, mosquitto_ctrl can load an options file which contains a list of options it should use. This means you can set the encryption options, host, admin username and any other options once and not have to add them to the command line every time. mosquitto_ctrl will try to load a configuration file from a default location. For Windows this is at `%USER_PROFILE%\mosquitto_ctrl`. For other systems, it will try `$XDG_CONFIG_HOME/mosquitto_ctrl` or `$HOME/.config/mosquitto_ctrl`. You may override this behaviour by manually specifying an options file with `-o `. The options file should contain a list of options, one per line, exactly as they would be provided on the command line. For example: ``` --cafile /path/to/my/CA.crt --certfile /path/to/my/client.crt --keyfile /path/to/my/client.key -u admin -h mosquitto.example.com ``` ### mosquitto_ctrl options * `-A address` : Bind the outgoing connection to a local ip address/hostname. Use this argument if you need to restrict network communication to a particular interface. * `--cafile path-to-ca.crt` : Define the path to a file containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. See also `--capath` * `--capath` : Define the path to a directory containing PEM encoded CA certificates that are trusted. Used to enable SSL communication. For `--capath` to work correctly, the certificate files must have ".crt" as the file ending and you must run `openssl rehash ` each time you add/remove a certificate. See also `--cafile`. * `--cert path-to-client.crt` : Define the path to a file containing a PEM encoded certificate for this client, if required by the server. See also `--key`. * `--ciphers` : An openssl compatible list of TLS ciphers to support in the client. See ciphers(1) for more information. * `-d` : Enable debug messages. * `--help` : Display usage information. * `-h hostname` : Specify the host to connect to. Defaults to localhost. * `-i client-id` : The id to use for this client. If not given, a client id will be generated depending on the MQTT version being used. For v3.1.1/v3.1, the client generates a client id in the format mosq-XXXXXXXXXXXXXXXXXX, where the X are replaced with random alphanumeric characters. For v5.0, the client sends a zero length client id, and the server will generate a client id for the client. * `--insecure` : When using certificate based encryption, this option disables verification of the server hostname in the server certificate. This can be useful when testing initial server configurations but makes it possible for a malicious third party to impersonate your server through DNS spoofing, for example. Use this option in testing only. If you need to resort to using this option in a production environment, your setup is at fault and there is no point using encryption. * `--key path-to-client.key` : Define the path to a file containing a PEM encoded private key for this client, if required by the server. See also `--cert`. * `-L url` : Specify specify user, password, hostname, port and topic at once as a URL. The URL must be in the form: `mqtt(s)://[username[:password]@]host[:port]`. If the scheme is mqtt:// then the port defaults to 1883. If the scheme is mqtts:// then the port defaults to 8883. * `--nodelay` : Disable Nagle's algorithm for the socket. This means that latency of sent messages is reduced, which is particularly noticable for small, reasonably infrequent messages. Using this option may result in more packets being sent than would normally be necessary. * `-p port` : Connect to the port specified. If not given, the default of 1883 for plain MQTT or 8883 for MQTT over TLS will be used. * `-P password` : Provide a password to be used for authenticating with the broker. Using this argument without also specifying a username is invalid when using MQTT v3.1 or v3.1.1. See also the `-u` option. * `--proxy proxy-url` : Specify a SOCKS5 proxy to connect through. "None" and "username" authentication types are supported. The socks-url must be of the form `socks5h://[username[:password]@]host[:port]`. The protocol prefix socks5h means that hostnames are resolved by the proxy. The symbols %25, %3A and %40 are URL decoded into %, : and @ respectively, if present in the username or password. If username is not given, then no authentication is attempted. If the port is not given, then the default of 1080 is used. * `--psk key` : Provide the hexadecimal (no leading 0x) pre-shared-key matching the one used on the broker to use TLS-PSK encryption support. `--psk-identity` must also be provided to enable TLS-PSK. * `--psk-identity identify` : The client identity to use with TLS-PSK support. This may be used instead of a username if the broker is configured to do so. * `-q qos` : Specify the quality of service to use for messages, from 0, 1 and 2. Defaults to 1. * `--quiet` : If this argument is given, no runtime errors will be printed. This excludes any error messages given in case of invalid user input (e.g. using `-p` without a port). * `--tls-version version` : Choose which TLS protocol version to use when communicating with the broker. Valid options are tlsv1.3, tlsv1.2 and tlsv1.1. The default value is tlsv1.2. Must match the protocol version used by the broker. * `-u username` : Provide a username to be used for authenticating with the broker. See also the `-P` argument. * `--unix path` : Connect to a broker through a local unix domain socket instead of a TCP socket. This is a replacement for `-h` and `-L`. For example: `mosquitto_ctrl --unix /tmp/mosquitto.sock ...`. * `-V protocol-version` : Specify which version of the MQTT protocol should be used when connecting to the remote broker. Can be `5`, `311`, `31`, or the more verbose `mqttv5`, `mqttv311`, or `mqttv31`. Defaults to `311`. ## Configuring default access The initial configuration sets the default ACL type behaviours to: * `publishClientSend`: deny * `publishClientReceive`: allow * `subscribe`: deny * `unsubscribe`: allow If you wish to change these, use `mosquitto_ctrl`. ``` mosquitto_ctrl dynsec setDefaultACLAccess publishClientSend deny mosquitto_ctrl dynsec setDefaultACLAccess publishClientReceive deny mosquitto_ctrl dynsec setDefaultACLAccess subscribe deny mosquitto_ctrl dynsec setDefaultACLAccess unsubscribe deny ``` You can examine the current default access with the `getDefaultACLAccess` command: ``` mosquitto_ctrl dynsec getDefaultACLAccess unsubscribe ``` ## Creating and modifying clients To create a new client: ``` mosquitto_ctrl dynsec createClient ``` This creates a client which does not have a client id associated with it. You will be asked for the password for the new client before you are asked for the admin user password. Pay attention to the messages on the command line. ``` mosquitto_ctrl dynsec createClient -i ``` This creates a client which has a client id associated with it. To delete a client (clients connected with these credentials will be disconnected from the broker): ``` mosquitto_ctrl dynsec deleteClient ``` To disable a client (clients connected with these credentials will be disconnected from the broker): ``` mosquitto_ctrl dynsec disableClient ``` To enable a client (clients will be able to use these credentials to log in again): ``` mosquitto_ctrl dynsec enableClient ``` To set a client password: ``` mosquitto_ctrl dynsec setClientPassword mosquitto_ctrl dynsec setClientPassword ``` To add/remove a role to/from a client: ``` mosquitto_ctrl dynsec addClientRole mosquitto_ctrl dynsec removeClientRole ``` To get information on a client: ``` mosquitto_ctrl dynsec getClient ``` To list all clients: ``` mosquitto_ctrl dynsec listClients ``` The `modifyClient` command also exists in the topic API, but is not currently available in `mosquitto_ctrl`. ## Creating and modifying groups To create a new group: ``` mosquitto_ctrl dynsec createGroup ``` To delete a group: ``` mosquitto_ctrl dynsec deleteGroup ``` To add/remove a client to/from a group: ``` mosquitto_ctrl dynsec addGroupClient mosquitto_ctrl dynsec removeGroupClient ``` In this case the `priority` refers to the priority of the group within the client's list of groups. To add/remove a role to/from a group: ``` mosquitto_ctrl dynsec addGroupRole mosquitto_ctrl dynsec removeGroupRole ``` To set/get the group that anonymous devices are assigned to: ``` mosquitto_ctrl dynsec setAnonymousGroup mosquitto_ctrl dynsec getAnonymousGroup ``` To get information on a group: ``` mosquitto_ctrl dynsec getGroup ``` To list all groups: ``` mosquitto_ctrl dynsec listGroups ``` The `modifyGroup` command also exists in the topic API, but is not currently available in `mosquitto_ctrl`. ## Creating and modifying roles To create a new role: ``` mosquitto_ctrl dynsec createRole ``` To delete a role: ``` mosquitto_ctrl dynsec deleteRole ``` To add an ACL to a role: ``` mosquitto_ctrl dynsec addRoleACL allow|deny ``` Where `acltype` is one of `publishClientSend`, `publishClientReceive`, `subscribeLiteral`, `subscribePattern`, `unsubscribeLiteral`, and `unsubscribePattern`. For example: ``` mosquitto_ctrl dynsec addRoleACL publishClientSend client/topic allow 5 ``` To remove an ACL from a role using the topic filter as the key: ``` mosquitto_ctrl dynsec removeRoleACL ``` For example: ``` mosquitto_ctrl dynsec removeRoleACL publishClientSend client/topic ``` To get information on a role: ``` mosquitto_ctrl dynsec getRole ``` To list all roles: ``` mosquitto_ctrl dynsec listRoles ``` The `modifyRole` command also exists in the topic API, but is not currently available in `mosquitto_ctrl`. mosquitto-2.0.18/www/pages/documentation/migrating-to-2-0.md000066400000000000000000000160021450213760600236640ustar00rootroot00000000000000 [TOC] ## Introduction Mosquitto 2.0 introduces a number of changes to the behaviour of the broker which new users need to be aware of, and which this document explains. If you are packaging Mosquitto for distribution, see the Packaging and Distribution section. If you are a plugin author, see the Plugins section. ## Listener behaviour changes The way in which Mosquitto configures listeners has been changed to help encourage end users to take an informed choice about security, rather than just relying on the previously very forgiving defaults. ### Listener without configuration When Mosquitto is run without a configuration file, or without configuring any listeners, it will now bind to the loopback interfaces 127.0.0.1 and/or ::1. This means that only connections from the local host will be possible. This mode allows automated or manual testing on a local machine without the need for a configuration file. In this mode only, anonymous/unauthenticated users are allowed by default. This applies to you if you run your broker in a similar way to one of these examples: * `mosquitto` * `mosquitto -p 1883`. If you use this mode and wish to have clients connect from a remote machine, then you will need to use a configuration file: ``` listener 1883 # Note that this will not allow anonymous access by default. ``` This configuration binds the listener for port 1883 to the `0.0.0.0` or `::` interface by default, i.e. allows connections on all interfaces. It is still possible to bind to a specific interface manually, e.g. `listener 1883 192.168.1.1`. ### Authentication requires configuration All listeners now require authentication to be configured. This is with the exception of the case where no listener configuration is provided and hence the listener is bound to the loopback interface, as described above. This means that `allow_anonymous` now defaults to false. If you currently have a broker running that has a listener configured in the configuration file, but has no other authentication configured and no explicit `allow_anonymous` setting, then your clients will be unable to connect after upgrading to Mosquitto 2.0. There are three choices : * Configure the in-built `password_file` and `acl_file` options for authentication. * Use an authentication plugin, such as the new [dynamic-security plugin], or the third party [mosquitto-go-auth plugin]. * Set `allow_anonymous true` - this should be done only if you have a specific need for unauthenticated clients. ### Listener TLS protocol version changes The listener `tls_version` option now defines the *minimum* TLS protocol version to be used, rather than the exact version. For example, setting `tls_version tlsv1.2` would allow both TLS v1.2 and TLS v1.3. Support for TLS v1.0 has been disabled. ### Mixing configuration files with -p If you configure a listener in your configuration file *and* use e.g. `-p 1883` on the command line at the same time, you will need to add all listeners to the configuration file because this behaviour is no longer supported - the port provided on the command line will be ignored. If you have a configuration file like this: ``` listener 1883 # ... ``` And run Mosquitto like this: `mosquitto -c mosquitto.conf -p 1884` Then you should instead run Mosquitto as `mosquitto -c mosquitto.conf`, and use a configuration file with both listeners in: ``` listener 1883 # ... listener 1884 # ... ``` ## Use of root/privileged user In versions prior to 2.0, if Mosquitto was run as root it would load TLS certificates, start listeners, and start logging, before dropping to the unprivileged mosquitto user. This behaviour has changed. In 2.0, Mosquitto load the configuration file and immediately drop to the configured unprivileged user, which defaults to `mosquitto`. If the `mosquitto` or manually configured user is not available, the broker will attempt to drop to the `nobody` user. This means that the only files Mosquitto will access as root are the configuration files, and hence any other files that Mosquitto needs to access or write must be accessible by the unprivileged user. In particular those people using TLS certificates from Lets Encrypt will need to do something to allow Mosquitto to access those certificates. An example deploy renewal hook script to help with this is at [misc/letsencrypt/mosquitto-copy.sh]. It is still possible to force Mosquitto to run as root, but this is strongly recommended against. ## Other behaviour The `pid_file` option will now always attempt to write a pid file, regardless of whether the `-d` argument is used when running the broker. The `max_queued_messages` option has been increased from 100 to 1000 by default, and now also applies to QoS 0 messages, when a client is connected. ## Packaging and Distribution The components that Mosquitto provides can be categorised as follows: ### Client libraries C/C++ libraries for creating MQTT clients. * lib/libmosquitto.so.1 * lib/cpp/libmosquittopp.so.1 * include/mosquitto.h * include/mqtt_protocol.h ### Clients General purpose command line MQTT clients. These depend upon libmosquitto.so.1. * client/mosquitto_pub * client/mosquitto_sub * client/mosquitto_rr ### Broker The main offering of the project, the Mosquitto broker, plus associated utilities and plugins. * apps/mosquitto_ctrl/mosquitto_ctrl * apps/mosquitto_passwd/mosquitto_passwd * plugins/dynamic-security/mosquitto_dynamic_security.so * src/mosquitto * include/mosquitto_broker.h * include/mosquitto_plugin.h Changes: * The mosquitto_passwd utility has changed location. * The mosquitto_ctrl utility has been added. * The mosquitto_dynamic_security plugin has been added, this is a Mosquitto specific shared library. * The mosquitto_ctrl utility requires libmosquitto.so.1. * Plugin developers would expect to have the header files from libmosquitto available, as well as those from the broker.. ### Dependencies mosquitto_ctrl and mosquitto_dynamic_secuity.so require the cJSON library. If it is not detected or desired, those features can be disabled. ## Plugins Mosquitto 2.0 introduces a new plugin interface which should be simpler to develop for, and is more easily extendable. A separate document will describe the new plugin interface. If you have an existing plugin that followed the guidelines in `mosquitto_plugin.h`, then it will continue to work with Mosquitto 2.0 *unless* it is compiled using the Mosquitto 2.0 header files, which contained a mistake in the documentation. To modify your plugin so that it will work on both of Mosquitto 1.6 and Mosquitto 2.0, you should change your instance of `mosquitto_auth_plugin_version` so that it returns the version of the plugin interface that you support, i.e. `4`. [misc/letsencrypt/mosquitto-copy.sh]:https://github.com/eclipse/mosquitto/tree/master/misc/letsencrypt [dynamic-security plugin]:/documentation/dynamic-security/ [mosquitto-go-auth plugin]:https://github.com/iegomez/mosquitto-go-auth mosquitto-2.0.18/www/pages/documentation/using-the-snap.md000066400000000000000000000065621450213760600236430ustar00rootroot00000000000000 On Linux systems that have snap support, Mosquitto can be installed from the graphical software installer, or with `snap install mosquitto`. After installing the Mosquitto snap, the Mosquitto broker will be running with the default configuration, which means it is listening for connections on port 1883 on the local computer only. If you want to allow connections from other computers you must configure a listener and an [authentication method]. To test the broker, you can use the `mosquitto_pub` and `mosquitto_sub` command line utilities, which are also provided in the snap. `mosquitto_pub` allows you to publish messages to an MQTT broker, and `mosquitto_sub` allows you to subscribe to messages from an MQTT broker. Both tools have a large number of options to control how they are used and as such are useful for a wide variety of tasks. In this case, we will just use them for some simple testing. To subscribe to all messages being published to the MQTT broker on the `snap/example` topic, use the following command. If your MQTT broker is not running on the same machine as `mosquitto_sub`, you will need to change the `localhost` argument to match your MQTT broker host or IP address. ``` mosquitto_sub -h localhost -t 'snap/example' -v ``` The `-t snap/example` option sets the topic to subscribe to, and can be provided multiple times. The `-v` option means to print both the topic of the message as well as its payload. Now to publish a message to the same topic, use the very similar `mosquitto_pub` command: ``` mosquitto_pub -h localhost -t 'snap/example' -m 'Hello from mosquitto_pub' ``` In this case the `-m` option provides the message payload to be published. If everything works as planned, you should see `mosquitto_sub` print ``` snap/example Hello from mosquitto_pub ``` This is of course a very simple example, but it does allow testing of the broker operation. Other things you may wish to try are subscribing to wildcard topics that include `#` or `+`, or subscribing to the `$SYS/#` topic to see information the broker is publishing about itself. Beware that the command line treats `#` as a special character, and `$SYS` will be expanded as a environment variable if you do not surround them with single quotes. Once you have finished your testing, you will want to configure your broker to have encrypted connections and use authentication, possibly configuring bridges, which allow different brokers to share topics, or many other options. To do this, you need to provide a new configuration file. The snap provides an example configuration file at `/var/snap/mosquitto/common/mosquitto_example.conf`. This file contains all of the broker configuration, in a similar manner to the man page. To create your own configuration, copy the example file to `/var/snap/mosquitto/common/mosquitto.conf` and edit according to your needs. Any additional files required by the configuration, such as TLS certificates and keys, must also be placed in `/var/snap/mosquitto/common/` - in new folders if wanted. This directory is the only place accessible by Mosquitto when running as a snap. All other aspects of running Mosquitto are the same as with any other installation methods. [authentication method]:/documentation/authentication-methods mosquitto-2.0.18/www/pages/download.md000066400000000000000000000046701450213760600177350ustar00rootroot00000000000000 # Source * [mosquitto-2.0.14.tar.gz](https://mosquitto.org/files/source/mosquitto-2.0.14.tar.gz) ([GPG signature](https://mosquitto.org/files/source/mosquitto-2.0.14.tar.gz.asc)) * [Git source code repository](https://github.com/eclipse/mosquitto) (github.com) Older downloads are available at [https://mosquitto.org/files/](../files/) # Binary Installation The binary packages listed below are supported by the Mosquitto project. In many cases Mosquitto is also available directly from official Linux/BSD distributions. ## Windows * [mosquitto-2.0.14-install-windows-x64.exe](https://mosquitto.org/files/binary/win64/mosquitto-2.0.14-install-windows-x64.exe) (64-bit build, Windows Vista and up, built with Visual Studio Community 2019) * [mosquitto-2.0.14-install-windows-x32.exe](https://mosquitto.org/files/binary/win32/mosquitto-2.0.14-install-windows-x86.exe) (32-bit build, Windows Vista and up, built with Visual Studio Community 2019) Older installers can be found at [https://mosquitto.org/files/binary/](https://mosquitto.org/files/binary/). See also README-windows.md after installing. ## Mac Mosquitto can be installed from the homebrew project. See [brew.sh](https://brew.sh/) and then use `brew install mosquitto` ## Linux distributions with snap support * `snap install mosquitto` ## Debian * Mosquitto is now in Debian proper. There will be a short delay between a new release and it appearing in Debian as part of the normal Debian procedures. * There are also Debian repositories provided by the mosquitto project, as described at ## Raspberry Pi Mosquitto is available through the main repository. There are also Debian repositories provided by the mosquitto project, as described at ## Ubuntu Mosquitto is available in the Ubuntu repositories so you can install as with any other package. If you are on an earlier version of Ubuntu or want a more recent version of mosquitto, add the [mosquitto-dev PPA](https://launchpad.net/%7Emosquitto-dev/+archive/mosquitto-ppa/) to your repositories list - see the link for details. mosquitto can then be installed from your package manager. * `sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa` * `sudo apt-get update` mosquitto-2.0.18/www/pages/index.html000066400000000000000000000102641450213760600175750ustar00rootroot00000000000000

Eclipse Mosquitto is an open source (EPL/EDL licensed) message broker that implements the MQTT protocol versions 5.0, 3.1.1 and 3.1. Mosquitto is lightweight and is suitable for use on all devices from low power single board computers to full servers.

The MQTT protocol provides a lightweight method of carrying out messaging using a publish/subscribe model. This makes it suitable for Internet of Things messaging such as with low power sensors or mobile devices such as phones, embedded computers or microcontrollers.

The Mosquitto project also provides a C library for implementing MQTT clients, and the very popular mosquitto_pub and mosquitto_sub command line MQTT clients.

Mosquitto is part of the Eclipse Foundation, and is an iot.eclipse.org project. The development is driven by Cedalo.


Download and Security

Mosquitto is highly portable and available for a wide range of platforms. Go to the dedicated download page to find the source or binaries for your platform.

Read the Change Log to find out about recent releases.

Use the security page to find out how to report vulnerabilities or responses to past security issues.

Test

You can have your own instance of Mosquitto running in minutes, but to make testing even easier, the Mosquitto Project runs a test server at test.mosquitto.org where you can test your clients in a variety of ways: plain MQTT, MQTT over TLS, MQTT over TLS (with client certificate), MQTT over WebSockets and MQTT over WebSockets with TLS.

Community

Support

Support is always available from the community channels on a best effort basis. If you require commercial support, Cedalo can offer support for hosted or on-premise instances, consulting on the use of Mosquitto, and custom development to your needs.

Related Projects

Paho provides MQTT client library implementations in a wide variety of languages.

Streamsheets is an easy to use web based real time spreadsheet interface that can be used to process incoming data from a variety of sources, such as MQTT, OPC-UA, and REST. Developers and non-developers can use Streamsheets to control processes and build dashboards, for example. Mosquitto is a core component of Streamsheets.

mosquitto-2.0.18/www/pages/roadmap.md000066400000000000000000000051251450213760600175450ustar00rootroot00000000000000 # Roadmap ## Version 1.6 The next minor release. The focus of this release is on providing support for version 5 of the MQTT protocol. This release will provide a feature complete implementation, but does not represent the final interface for all features. In particular, functions are being added to libmosquitto to provide support for MQTT 5 features, but these will be consolidated with the API changes planned for version 2.0. ### Deprecation notices #### libmosquittopp libmosquittopp, the C++ wrapper around libmosquitto is now deprecated and will be removed in the next major release (2.0). The wrapper came about by an external request and at the time it was created there were no other C++ solutions for MQTT. This has changed in the past years and this wrapper provides no benefit over true C++ libraries or using the pure C libmosquitto. #### libmosquitto API changes The Mosquitto project has maintained API and ABI compatibility in libmosquitto since version 1.0, and has dealt with the introduction of new specification features by adding new functions which duplicate the behaviour of existing functions, but with additional arguments to support the new features. Particularly with regards to adding support for MQTT version 5, this has lead to a proliferation of functions which offer small variations on a theme. The libmosquitto functions listed below (which includes some new functions included in 1.6) are going to be updated for version 2.0. Functions not listed here should still be considered at risk of being updated. * mosquitto\_will\_set * mosquitto\_connect\* * mosquitto\_reconnect\* * mosquitto\_disconnect * mosquitto\_publish\* * mosquitto\_subscribe\* * mosquitto\_unsubscribe\* * mosquitto\_loop\* * mosquitto\_\*\_callback\_set * All callbacks * mosquitto\_\*\_topic\_check\* ## Version 2.0 This is the next major release and includes breaking changes. Other features planned include: ## Disk persistence improvements A new disk persistence interface will be created to allow persistence to occur immediately, rather than periodically. This will allow queued messages for disconnected clients to be removed from memory, and reduce the periodic pause caused when writing the persistence file. ## Breaking changes ### libmosquitto The libmosquitto API is being consolidated to better support the new MQTT 5 features whilst reducing the number of function variants. ### libmosquittopp The C++ wrapper around libmosquitto will be removed in this release. mosquitto-2.0.18/www/pages/security.md000066400000000000000000000114221450213760600177660ustar00rootroot00000000000000 # Reporting security vulnerabilities If you think you have found a security vulnerability in Mosquitto, please follow the steps on [Eclipse Security] page to report it. # Past vulnerabilities Listed with most recent first. Further information on security related issues can be found in the [security category]. * June 2023: [CVE-2023-28366]: Clients sending unacknowledged QoS 2 messages with duplicate message ids cause a memory leak. Affecting versions **1.3.2** to **2.0.15** inclusive, fixed in **2.0.16**. * August 2022: Deleting the anonymous group in the dynamic security plugin could lead to a crash. Affecting versions **2.0.0** to **2.0.14** inclusive, fixed in **2.0.15**. * August 2021: [CVE-2021-34434] Affecting versions **2.0.0** to **2.0.11** inclusive, fixed in **2.0.12**. * April 2021: [CVE-2021-28166] Affecting versions **2.0.0** to **2.0.9** inclusive, fixed in **2.0.10**. * December 2020: Running mosquitto_passwd with the following arguments only `mosquitto_passwd -b password_file username password` would cause the username to be used as the password. Affecting versions **2.0.0** to **2.0.2** inclusive, fixed in **2.0.3**. * September 2019: [CVE-2019-11779]. Affecting versions **1.5** to **1.6.5** inclusive, fixed in **1.6.6** and **1.5.9**. More details at [version-166-released]. * September 2019: [CVE-2019-11778]. Affecting versions **1.6** to **1.6.4** inclusive, fixed in **1.6.5**. More details at [version-166-released]. * April 2019: No CVE assigned. Affecting versions **1.6** and **1.6.1**, fixed in **1.6.2**. More details at [version-162-released]. * December 2018: [CVE-2018-20145]. Affecting versions **1.5** to **1.5.4** inclusive, fixed in **1.5.5.**. More details at [version-155-released]. * November 2018: No CVE assigned. Affecting versions **1.4** to **1.5.3** inclusive, fixed in **1.5.4**. More details at [version-154-released]. * September 2018: [CVE-2018-12543] affecting versions **1.5** to **1.5.2** inclusive, fixed in **1.5.3**. * April 2018: [CVE-2017-7655] affecting versions **1.0** to **1.4.15** inclusive, fixed in **1.5**. * April 2018: [CVE-2017-7654] affecting versions **1.0** to **1.4.15** inclusive, fixed in **1.5**. [security-advisory-cve-2017-7653-cve-2017-7654]. * April 2018: [CVE-2017-7653] affecting versions **1.0** to **1.4.15** inclusive, fixed in **1.5**. * February 2018: [CVE-2017-7651] affecting versions **0.15** to **1.4.14** inclusive, fixed in **1.4.15**. More details at [security-advisory-cve-2017-7651-cve-2017-7652]. * February 2018: [CVE-2017-7652] affecting versions **1.0** to **1.4.14** inclusive, fixed in **1.4.15**. More details at [security-advisory-cve-2017-7651-cve-2017-7652]. * June 2017: [CVE-2017-9868] affecting versions **0.15** to **1.4.12** inclusive, fixed in **1.4.13**. More details at [security-advisory-cve-2017-9868]. * May 2017: [CVE-2017-7650] affecting versions **0.15** to **1.4.11** inclusive, fixed in **1.4.12**. More details at [security-advisory-cve-2017-7650]. [version-166-released]: /blog/2019/09/version-1-6-6-released/ [version-162-released]: /blog/2019/04/version-1-6-2-released/ [version-155-released]: /blog/2018/11/version-155-released/ [version-154-released]: /blog/2018/11/version-154-released/ [security-advisory-cve-2018-12543]: /blog/2018/09/security-advisory-cve-2018-12543/ [security-advisory-cve-2017-7651-cve-2017-7652]: /blog/2018/02/security-advisory-cve-2017-7651-cve-2017-7652/ [security-advisory-cve-2017-7650]: /blog/2017/05/security-advisory-cve-2017-7650/ [security-advisory-cve-2017-9868]: /blog/2017/06/security-advisory-cve-2017-9868/ [Eclipse Security]: https://www.eclipse.org/security/ [security category]: /blog/categories/security/ [CVE-2021-34434]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-34434 [CVE-2021-28166]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28166 [CVE-2019-11779]: https://nvd.nist.gov/vuln/detail/CVE-2019-11779 [CVE-2019-11778]: https://nvd.nist.gov/vuln/detail/CVE-2019-11778 [CVE-2018-20145]: https://nvd.nist.gov/vuln/detail/CVE-2018-20145 [CVE-2018-12543]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12543 [CVE-2017-9868]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868 [CVE-2017-7655]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7655 [CVE-2017-7654]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7654 [CVE-2017-7653]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7653 [CVE-2017-7652]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652 [CVE-2017-7651]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7651 [CVE-2017-7650]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7650 mosquitto-2.0.18/www/plugins/000077500000000000000000000000001450213760600161575ustar00rootroot00000000000000mosquitto-2.0.18/www/plugins/__init__.py000066400000000000000000000000311450213760600202620ustar00rootroot00000000000000# Plugin modules go here.mosquitto-2.0.18/www/plugins/docbookmanpage/000077500000000000000000000000001450213760600211305ustar00rootroot00000000000000mosquitto-2.0.18/www/plugins/docbookmanpage/docbookmanpage.plugin000066400000000000000000000004731450213760600253250ustar00rootroot00000000000000[Core] Name = docbookmanpage Module = docbookmanpage [Nikola] PluginCategory = PageCompiler [Documentation] Author = Roger Light (asciidoc code by Roberto Alsina) Version = 0.4 Website = https://github.com/ralight/nikola-docbook-manpage Description = Compile Docbook manpages into html, based on asciidoc plugin. mosquitto-2.0.18/www/plugins/docbookmanpage/docbookmanpage.py000066400000000000000000000056651450213760600244670ustar00rootroot00000000000000# -*- coding: utf-8 -*- # Copyright © 2012-2014 Roberto Alsina and others. # Permission is hereby granted, free of charge, to any # person obtaining a copy of this software and associated # documentation files (the "Software"), to deal in the # Software without restriction, including without limitation # the rights to use, copy, modify, merge, publish, # distribute, sublicense, and/or sell copies of the # Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice # shall be included in all copies or substantial portions of # the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY # KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE # WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR # PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS # OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR # OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE # SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. """Implementation of compile_html based on asciidoc. You will need, of course, to install asciidoc """ import codecs import os import subprocess from nikola.plugin_categories import PageCompiler from nikola.utils import makedirs, req_missing, write_metadata try: from collections import OrderedDict except ImportError: OrderedDict = dict # NOQA class CompileDocbookManpage(PageCompiler): """Compile docbookmanpage into HTML.""" name = "docbookmanpage" demote_headers = True def compile(self, source, dest, is_two_file=True, post=None, lang=None): """Compile the source file into HTML and save as dest.""" makedirs(os.path.dirname(dest)) binary = self.site.config.get('XSLTPROC_BINARY', 'xsltproc') xslpath = os.path.join(os.path.split(__file__)[0], 'html.xsl') try: subprocess.check_call((binary, '-o', dest, xslpath, source)) if post is None: if shortcode_deps: self.logger.error( "Cannot save dependencies for post {0} (post unknown)", source) except OSError as e: print(e) req_missing(['xsltproc'], 'build this site (compile with xsltproc)', python=False) def create_post(self, path, content=None, onefile=False, is_page=False, **kw): """Create post file with optional metadata.""" metadata = OrderedDict() metadata.update(self.default_metadata) metadata.update(kw) makedirs(os.path.dirname(path)) if not content.endswith('\n'): content += '\n' with codecs.open(path, "wb+", "utf8") as fd: if onefile: fd.write("////\n") fd.write(write_metadata(metadata)) fd.write("////\n") fd.write(content) mosquitto-2.0.18/www/plugins/docbookmanpage/html.xsl000066400000000000000000000017721450213760600226330ustar00rootroot00000000000000 man.css ansi ansi ansi 1 mosquitto-2.0.18/www/posts/000077500000000000000000000000001450213760600156465ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2009/000077500000000000000000000000001450213760600162405ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2009/12/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2009/12/version-0-2-released.md000066400000000000000000000003561450213760600225530ustar00rootroot00000000000000 Version 0.2 released - please see the [change log](/ChangeLog.txt). mosquitto-2.0.18/www/posts/2009/12/version-0-3-released.md000066400000000000000000000010601450213760600225450ustar00rootroot00000000000000 * Added logging support. * Now restarts much more quickly even when the network socket was in use. * Can now be configured to run on multiple network ports and restricted to specific network addresses. * Added host access control in the form of tcp-wrappers support. See the [change log](/ChangeLog.txt) for full details. Wild card support in topics is coming in the next version. mosquitto-2.0.18/www/posts/2010/000077500000000000000000000000001450213760600162305ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/01/000077500000000000000000000000001450213760600164505ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/01/mailing-list-irc.md000066400000000000000000000012311450213760600221330ustar00rootroot00000000000000 We've created some new support channels for mosquitto - a mailing list and an irc channel. Although they are both intended for providing support for mosquitto itself, general discussion of anything to do with mqtt is strongly encouraged. We want to reduce the barrier to getting started and provide a place where people can share their experiences. The mailing list is at The irc channel is #mqtt on the [freenode network] [freenode network]: http://freenode.net/ mosquitto-2.0.18/www/posts/2010/01/version-0-4-1-released.md000066400000000000000000000004261450213760600226770ustar00rootroot00000000000000 This is a bugfix release: * Fix regex used for finding retained messages to send on new subscription. mosquitto-2.0.18/www/posts/2010/01/version-0-4-released.md000066400000000000000000000014751450213760600225460ustar00rootroot00000000000000 * Added support for wildcard subscriptions using + and #. * All network operations are now non-blocking and can cope with partial packets, meaning that networking should be a lot more reliable. * Total messsages/bytes sent/received are now available in $SYS. * Improved logging information - use client ip address and id instead of socket number. * Keepalive==0 is now correctly treated as "never disconnect". * Default logging destination no longer includes "topics" to prevent possible error logging to the db before it is initialised. * Periodic $SYS messages can now be disabled. See the [changelog] for full details. [changelog]: /ChangeLog.txt mosquitto-2.0.18/www/posts/2010/02/000077500000000000000000000000001450213760600164515ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/02/version-0-4-2-released.md000066400000000000000000000005131450213760600226760ustar00rootroot00000000000000 This is a bugfix release. * Fix segfault on client connect with invalid protocol name/version. Get it at the [download page]. [download page]: /download mosquitto-2.0.18/www/posts/2010/03/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/03/google-powermeter.md000066400000000000000000000051011450213760600224340ustar00rootroot00000000000000 A popular use for mqtt brokers seems to be coupling them with a [CurrentCost] (or similar) energy monitor to then log energy data and produce pretty (and useful!) graphs. Google recently opened up their PowerMeter API which looks to provide very nice graphing of energy data. They are working with utility companies directly with in home monitors, but it's also possible to use it as an individual. Toby Evans got to the bottom of registering a device (see his [explanatory blog post]) which just leaves getting data to Google. If you're already logging energy data to an MQTT broker, it's as simple as adding another subscriber to send the data to Google. You could use the mosquitto_sub client and a script I wrote for posting to google, [google_powermeter_update_mqtt.pl] like so: ``` mosquitto_sub -t sensors/cc128/ch1 | google_powermeter_update_mqtt.pl ``` This assumes that the data appearing on the sensors/cc128/ch1 topic is in the format `<unix timestamp> <power reading in Watts>`. If you're not logging your energy data to a broker, you should probably consider doing so :) There is another script [google_powermeter_update.pl] which may be more suitable and can be used as: ``` google_powermeter_update.pl <unix timestamp> <power in Watts> ``` Both of the scripts need your user details adding and should be easy to modify to match your own particular need. They also assume you're using a single cumulative variable with your device and will need modifying if you're using more than one variable or aren't using cumulative variables. For reference, I use the script [cc128.pl] to read data from my CurrentCost CC128 (Envi). # Update: Google has a limit of 6 API requests per hour, so the above description will only work for a short while (the 6 request limit doesn't appear to be a hard limit when you first exceed it, but becomes increasingly stricter). I'm now logging my CC128 data to a mysql database and sending batch updates every 15 minutes with [google_powermeter_update_mysql.pl]. [CurrentCost]: http://currentcost.com/ [explanatory blog post]: http://2cheap2meter.blogspot.com/2010/03/setting-up-google-powermeter.html [google_powermeter_update_mqtt.pl]: /files/perl/google_powermeter_update_mqtt.pl [google_powermeter_update.pl]: /files/perl/google_powermeter_update.pl [cc128.pl]: /files/perl/cc128.pl">cc128.pl [google_powermeter_update_mysql.pl]: /files/perl/google_powermeter_update_mysql.pl mosquitto-2.0.18/www/posts/2010/03/upgrading-to-0-5-1.md000066400000000000000000000023511450213760600220320ustar00rootroot00000000000000 When upgrading to 0.5.1 from 0.4 or higher, there is an important change in the location of the sqlite3-pcre library used. On Linux, the expected location of this library has changed from /usr/lib/sqlite3-pcre.so to /usr/lib/sqlite3/pcre.so. This is because the library is an extension specifically for sqlite3, not a general use shared library. If you installed sqlite3-pcre manually, or are not using Ubuntu, you should either modify the `ext_sqlite3_regex` option in /etc/mosquitto.conf to point to your library path, or move the library to the new location. If you are using Ubuntu and have installed mosquitto from the launchpad ppa, this will largely be taken care of. However, due to a mistake in the packaging of sqlite3-pcre, you must first remove sqlite3-pcre with your package manager and then reinstall it before updating mosquitto. You will only ever need to do this once. Sorry for the inconvenience caused by this change. If you have any problem or questions, feel free to get in touch on the [mqtt users mailing list]. [mqtt users mailing list]: https://launchpad.net/~mqtt-users mosquitto-2.0.18/www/posts/2010/03/version-0-5-1-released.md000066400000000000000000000031201450213760600226740ustar00rootroot00000000000000 This announcement summarises the changes in both 0.5 and 0.5.1. The interesting changes: * Add mosquitto_sub and mosquitto_pub, simple clients for subscribe/publish. * Change persistence behaviour. The database is now stored in memory even if persistence is enabled. It is written to disk when mosquitto exits and also at periodic intervals as defined by the new `autosave_interval` option. This makes persistence more suitable when being used on devices with a limited number of writes, such as flash. * Default sqlite3-pcre path on Linux is now /usr/lib/sqlite3/pcre.so to match new sqlite3-pcre packages. The less interesting/bug fixes: * No longer store QoS=0 messages for disconnected clients that do not have clean start set. * Rename `msg_timeout` option to `retry_interval` for better rsmb compatibility. * The writing of the persistence database may be forced by sending mosquitto the SIGUSR1 signal. * Clients that do not send CONNECT as their first command are now disconnected. * Boolean configuration values may now be specified with true/false as well as 1/0. * Log message on CONNECT with invalid protocol or protocol version. * Add man pages for clients. * Add general man page on mqtt. * Root privileges are now dropped only after attempting to write a pid file (if configured). This means that the pid file can be written to /var/run/ directly and should fix bug #523183. mosquitto-2.0.18/www/posts/2010/03/version-0-5-2-released.md000066400000000000000000000022041450213760600226770ustar00rootroot00000000000000 This is a bugfix release; it is recommended that you upgrade to this version: * Always update last backup time, so that the backup doesn't run every time through the main loop once `autosave_interval` has been reached. * Report $SYS/broker/uptime in the same format as rsmb. * Make mandatory options obvious in usage output and man page of mosquitto_pub. Fixes bug [#529990]. * Treat subscriptions with a trailing slash correctly. This should fix bugs [#530099] and [#530369]. Mosquitto is now also available for Linux x86 statically compiled against sqlite3, which makes it usable on older distributions such as Ubuntu Hardy that are still supported but do not have a sufficiently new version of sqlite3. To download this package, go to the [download page]. [#529990]: https://bugs.launchpad.net/mosquitto/+bug/529990 [#530099]: https://bugs.launchpad.net/mosquitto/+bug/530099 [#530369]: https://bugs.launchpad.net/mosquitto/+bug/530369 [download page]: /download mosquitto-2.0.18/www/posts/2010/03/version-0-5-3-released.md000066400000000000000000000011541450213760600227030ustar00rootroot00000000000000 This is a bugfix release. * Will messages are now only sent when a client disconnects unexpectedly. * Fix all incoming topics/subscriptions that start with a / or contain multiple / in a row (//). This should finally fix bug [#530099]. * Do actually disconnect client when it sends an empty subscription/topic string. * Add missing $SYS/broker/clients/total to man page. [#530099]: https://bugs.launchpad.net/mosquitto/+bug/530099 mosquitto-2.0.18/www/posts/2010/03/version-0-5-4-released.md000066400000000000000000000010161450213760600227010ustar00rootroot00000000000000 This is a bugfix release. * Fix memory allocation in `mqtt3_fix_sub_topic()` ([bug #531861]). * Remove accidental limit of 100 client connections. * Fix mosquitto_pub handling of messages with QoS>0 ([bug #537061]). [bug #531861]: https://bugs.launchpad.net/mosquitto/+bug/531861 [bug #537061]: https://bugs.launchpad.net/mosquitto/+bug/537061 mosquitto-2.0.18/www/posts/2010/04/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/04/help-wanted-rpm-packaging.md000066400000000000000000000023501450213760600237230ustar00rootroot00000000000000 I'm currently working on the finishing touches of mosquitto 0.6 and will hopefully be releasing it some time this week in time for oggcamp. Mosquitto is pretty usable now so I'm keen on making it as easy as possible for people to get and use. The ultimate goal is of course to get it into the major Linux distros so it appears in the normal package repositories. Until then, other solutions are possible. I can provide Windows executables and have a PPA to support Ubuntu Linux users, but don't have anything for rpm based distros. Can you help? I'm quite happy using the opensuse build service to build and host the final packages, but the creation of the rpm build script isn't something I know how to do at the moment. Given the amount of time I've spent on the Debian style packaging, I thought I'd ask for help with rpms! :) If you've got familiarity with rpm and would like to help, please [get in touch]. If you aren't familiar with creating rpms but want a reason to learn, that would suit me fine as well. Thanks in advance! [get in touch]: /support mosquitto-2.0.18/www/posts/2010/04/mind-control-mqtt.md000066400000000000000000000011751450213760600223710ustar00rootroot00000000000000 If you're in the UK, you may be interested in watching the Wednesday 21st April episode of "Bang Goes the Theory". IBM employees [Nicholas O'Leary] and [Kevin Brown] will be in one of the segments, on controlling remote control cars with their minds - all with MQTT under the covers! * [BBC programme link] [Nicholas O'Leary]: http://twitter.com/knolleary [Kevin Brown]: http://twitter.com/kevinxbrown [BBC programme link]: https://www.bbc.co.uk/programmes/b00s5fvq mosquitto-2.0.18/www/posts/2010/04/oggcamp.md000066400000000000000000000005661450213760600204210ustar00rootroot00000000000000 I'm going to be at [oggcamp] this coming weekend. If you're there, try and find me and say hello! I'm planning a talk on mosquitto/mqtt with Andy Stanford-Clark, so should be relatively easy to find. [oggcamp]: http://oggcamp.org/ mosquitto-2.0.18/www/posts/2010/05/000077500000000000000000000000001450213760600164545ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/05/fedora-packages-available.md000066400000000000000000000013411450213760600237270ustar00rootroot00000000000000 Thanks to help from Chris Procter, there are now rpm packages available for Fedora Linux. As these are the first rpm packages, there may be problems so please report back if you find any. There are details on where to get the packages on the [download page]. Users of other rpm based distributions are currently out of luck - the versions of sqlite that they provide are typically either too old or else don't have support for some required features compiled in. Thanks to everybody else who got in touch about rpms as well! [download page]: /download mosquitto-2.0.18/www/posts/2010/05/gentoo-ebuilds-available.md000066400000000000000000000007341450213760600236400ustar00rootroot00000000000000 Thanks to Neil Bothwick, there are now some Gentoo ebuilds available for mosquitto and sqlite3-pcre. You can grab them from the links below - hopefully they'll be integrated in the near future. * * mosquitto-2.0.18/www/posts/2010/05/mosquitto-org.md000066400000000000000000000005401450213760600216260ustar00rootroot00000000000000 I'm pleased to annouce that the mosquitto website is now available at so please update your bookmarks! I'll be updating the links here in a few days when the change has propagated. mosquitto-2.0.18/www/posts/2010/05/mqtt-push-on-android.md000066400000000000000000000012051450213760600227660ustar00rootroot00000000000000 If you want to use MQTT for push in Android apps, you'll probably want to head over to Anton L's blog post [How to Implement Push Notifications for Android]. He has a sample Android app that uses the IBM Java library to implement push notifications using MQTT, as well as a web page solution to demo pushing notifications to your phone. [How to Implement Push Notifications for Android]: http://tokudu.com/2010/how-to-implement-push-notifications-for-android/ mosquitto-2.0.18/www/posts/2010/05/mqtt-wiki.md000066400000000000000000000005271450213760600207300ustar00rootroot00000000000000 A new wiki has been created, devoted to MQTT. If you want to share what you're doing with MQTT and how to do it, or want to find any of that out, head over to . mosquitto-2.0.18/www/posts/2010/05/version-0-6-1-released.md000066400000000000000000000006321450213760600227040ustar00rootroot00000000000000 This fixes an important bug that didn't get caught for 0.6 that can prevent old database versions being upgraded. From 0.7 onwards, mosquitto will be using release candidates to ensure this kind of problem doesn't occur in the future. mosquitto-2.0.18/www/posts/2010/05/version-0-6-released.md000066400000000000000000000042271450213760600225520ustar00rootroot00000000000000 This is a new features release. It offers quite a bit of change over the previous version. More details of the new features can be found in the [man pages]. The substantial changes are: * Basic support for connecting multiple MQTT brokers together (bridging). * mosquitto_sub can now subscribe to multiple topics (limited to a global QoS). * mosquitto_pub can now send a file as a message. * mosquitto_pub can now read all of stdin and send it as a message. * mosquitto_pub can now read stdin and send each line as a message. * Implement a more efficient database design, so that only one copy of each message is held in the database, rather than one per subscribed client. * Add support for automatic upgrading of the mosquitto DB from v1 to v2. * If a retained message is received with a zero length payload, the retained message for that topic is deleted. * Implement the `max_inflight_messages` and `max_queued_messages` features in the broker. The less visible features and bug fixes are as follows: * Add support for disabling `clean session` for the sub client. * mosquitto will now correctly run VACUUM on the persistent database on exit. * Add the `store_cleanup_interval` config option for dealing with the internal message store. * Add `persistence_file` config option to allow changing the filename of the persistence database. This allows multiple mosquitto DBs to be stored in the same location whilst keeping `persistence_location` compatible with rsmb. * Don't store QoS=0 messages for disconnected clients. Fixes bug #572608. This wasn't correctly fixed in version 0.5. * Don't disconnect clients if they send a PUBLISH with zero length payload (bug #573610). * Send through zero length messages. * Produce a warning on unsupported rsmb options instead of quitting. * Describe clean session flag in the mqtt man page. Get it from the [download page]. Windows and Ubuntu binaries will follow along shortly. [man pages]: /documentation [download page]: /download mosquitto-2.0.18/www/posts/2010/06/000077500000000000000000000000001450213760600164555ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/06/automation-has-the-oven-warmed-up-yet.md000066400000000000000000000107021450213760600261510ustar00rootroot00000000000000 In the Bang Goes the Theory episode [The Human Power Station] a family of four people had their electricity supplied by a large group of cyclists on cycles hooked up to generators, the point being to highlight the amount of energy that is used and wasted on a daily basis. There's a video on [youtube]. One example that seemed to waste a lot of energy was cooking roast dinner. The oven was turned on to preheat (this is often the first instruction in a recipe), but it wasn't actually used until a significant time later, wasting a lot of energy. An example which may be more common is turning the oven on to preheat before cooking frozen food (which requires no preparation), then forgetting to check to see if it has preheated. This used to happen to me, but I've solved the problem by using my electricity monitor (I have an electric oven), mqtt and asterisk. First off, we need to monitor electricity usage. I do this with a [CurrentCost] CC128 (note that EON customers in the UK can apply to get one of these for free in an [Energy Fit] pack) hooked up to a low power computer that is running mosquitto. If you're running Linux, you can use [cc128_read.py] or [cc128_read.pl] to read the data coming from the monitor and publish it to an mqtt broker. A second client, [cc128_parse.pl], takes the data from the monitor and republishes it to the broker in a friendlier format - the Unix timestamp of the reading and the power usage, separated by a single space. To figure out when the oven has warmed up, I look at the electricity usage with the oven heater on - approximately 2.4kW. If the energy usage drops below this value, I know that the oven heater has turned off and so the oven has warmed up. This is of course slightly simplistic - I'm not actually measuring the oven, just the electricity usage so if I turned on the kettle at the same time it could cause my guess to be incorrect. When CurrentCost produce their individual appliance monitors, I'll be able to be certain of how much electricity just the oven is using. This uncertainty means that I only want to turn the oven monitor on when I've actually turned the oven on. Looking for an easy way to start the monitor, I spotted my house phone - a Siemens C460IP - which is a "normal" land line phone and an IP phone all in one. I've got Asterisk running on the same server as mosquitto, so it's an ideal solution for controlling things. Configuring Asterisk is way beyond the scope of this text, so I'm only going to talk about the bits I changed. I added a new extension number 100 which, when called, starts the oven monitor: ``` exten => 100,1,Answer() exten => 100,n,System(echo "/usr/local/bin/oven_pub.pl" | at now) exten => 100,n,Playback(oven-trigger) exten => 100,n,Hangup ``` This answers the call, starts the monitor, plays a sound clip so the person calling knows what has happened and then hangs up. I'm using the "at" command here as a simple way of putting the job into the background, thanks to [Ed] for the suggestion. The script [oven_monitor.pl] looks for the electricity usage to drop beneath 2kW, then runs `oven_warmed_up.sh` and exits. The final step is to do something in [oven_warmed_up.sh] to give feedback. I make use of asterisk once again and initiate a call to the phone - so when the oven has warmed up I get a phone call with a message that tells me so. The outgoing call is initiated by moving [oven.call] to /var/spool/asterisk/outgoing/, as shown in the script. Do you have any suggestions on how to improve this? Or other ways of using asterisk or mqtt like this? Let me know in the comments! [The Human Power Station]: http://www.bbc.co.uk/programmes/b00p8469 [youtube]: http://www.youtube.com/watch?v=C93cL_zDVIM [CurrentCost]: http://www.currentcost.com/ [Energy Fit]: http://www.eon-uk.com/media/energyfit.aspx [cc128_read.py]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.py [cc128_read.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.pl [parse.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_parse.pl [Ed]: http://twitter.com/ribzlike [oven_monitor.pl]: /files/examples/oven-asterisk/oven_monitor.pl [oven_warmed_up.sh]: /files/examples/oven-asterisk/oven_warmed_up.sh [oven.call]: /files/examples/oven-asterisk/oven.call mosquitto-2.0.18/www/posts/2010/06/google-powermeter-step-by-step.attachments.json000066400000000000000000000006731450213760600276650ustar00rootroot00000000000000{"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"]}}mosquitto-2.0.18/www/posts/2010/06/google-powermeter-step-by-step.md000066400000000000000000000162251450213760600250020ustar00rootroot00000000000000 # Note: Google Powermeter is now defunct but this post will remain here for those interested. This is a follow up to my previous [post on using Google Powermeter], but this time I'm going to give a step by step guide to getting your data uploaded. The only assumptions are that you have a CurrentCost monitor (note that CurrentCost monitors are often rebadged by electricity suppliers such as EON in the UK so check yours) and have already connected it to your computer, want to use MQTT and that you're using Linux, or another Unix operating system. # Retrieving the data The first step is to get the data from the CurrentCost into the MQTT broker. This is straightforward - simply read data from the serial port and send it all to the broker. I have scripts to do this with mosquitto in both [perl] and [python]. The data coming from the CurrentCost is in XML format and as well as providing the real time power reading every 6 seconds, will also send historical data periodically. I'm only going to deal with the real time readings here. The next step is to reprocess the incoming data into something more manageable, then republish it. An example of doing that is the script [cc128_parse.pl], which assumes you're only using the main channel from the CurrentCost. If you have multiple monitoring channels, you'll need to modify it to suit. # Logging the data Google limits the number of times we can send data to 6 per hour, so we have to log the data and then send amalgamated updates. I use mysql for this - I'm going to assume that you've got it installed and running. Log into the mysql console using "mysql -u root",  "mysql -u root -p" if you know the password, or possibly "sudo mysql". We're now going to create a database and table to hold the powermeter data, then add a user to access and update the data. To create the database and table enter the following: ``` CREATE DATABASE powermeter; USE 'powermeter'; CREATE TABLE powermeter ( `id` INT NOT NULL auto_increment, `timestamp` INT NOT NULL, `temperature` FLOAT NOT NULL DEFAULT 0.0, `ch1` INT NOT NULL DEFAULT 0, PRIMARY KEY (`id`), UNIQUE KEY `timestamp` (`timestamp`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8; ``` Note that there's a column there for the temperature as well. To add the user and grant access to the database: ``` CREATE USER 'powermeter'@'localhost' IDENTIFIED BY '<your password>'; GRANT ALL ON powermeter.* to 'powermeter'@'localhost'; ``` Finally, you'll need to get data into this database. My script [cc128_log_mysql.pl] subscribes to the data from cc128_parse.pl and logs it into the database. You'll need to edit it to have the correct database details. If you already have your power data published to an MQTT topic, it's quite likely that you won't have it in the same format that I use above. If this is the case, you will need to modify cc128_log_mysql.pl. Assuming your data coming in over MQTT is just the power reading, then you can replace this: ``` @vals = split(/,/, $line); $timestamp = @vals[0]; $temperature = @vals[1]; $ch1 = @vals[2]; ``` with this: ``` $timestamp = time(); $temperature = 0; $ch1 = $line; ``` You can of course leave the temperature column out completely if you prefer. # Registering with Google Powermeter Before you can send any data to Google, you need to register your device with them. This would normally be done automatically by your device, but because we're doing things ourselves we need to do it manually. See [2cheap2meter] and the links it provides for more details. We first need to decide on a few parameters for our device: * Manufacturer (e.g. CurrentCost) * Device model (e.g. CC128 or Envi) * Device id (e.g. Serial number or your own made up string, 1234) * Number of channels to log (e.g. 1) We can then construct an address which you will paste into your web browser: ``` https://www.google.com/powermeter/device/activate?mfg=CurrentCost&model=CC128&did=1234&dvars=1 ``` `dvars` here is the number of channels (or monitors) that we wish to register. If you have more than one channel logging, change the number accordingly - bear in mind that you'll have to modify just about everything else in this post to match. You will need to remember the values you put here for later. Visiting that link will take you to the activation page, which you should complete. After you have done this, you will be presented with authorisation information for your new device. The piece of information we need is the 32 character string contained between "token=" and "&path" (the authorisation token) as well as the 20 digit number after "&path=/user/" (your google powermeter id). # Sending the data I have a script [google_powermeter_update.pl] that will query the database for readings from the past 15 minutes and then send them. You'll need to edit the script to put the correct database details, power meter id, authorisation token and device details. To set it to run every fifteen minutes, I use cron. Either add an entry to your own crontab by running "crontab -e" then entering the following line: ``` */15 * * * * /path/to/google_powermeter_update.pl > /dev/null ``` Or by creating a file containing the line below and copying it to /etc/cron.d/powermeter_update.cron. ``` */15 * * * * nobody /path/to/google_powermeter_update.pl > /dev/null ``` In both cases, you can change the output redirection from "/dev/null" to e.g. "/tmp/powermeter" to allow you to check any error codes in case of a problem. Now go to to check your data! Here's an example of mine: [![powermeter example](/blog/uploads/2010/06/powermeter-example-300x138.png)](/blog/uploads/2010/06/powermeter-example.png) # Possible changes The above description and scripts aren't ideal - if you lose your internet connection then data will still be recorded but won't be sent to google. One possible change would be to add a column to the database to list whether that particular piece of data had been sent or not, which would allow all data to eventually be sent and deleted afterwards if desired. A second way around this would be to make use of the historical data that the CurrentCost monitors use. This could also be a way of reducing the need to log things ourselves. # Conclusion I hope this is of use to you - please let me know if you have any problems with any of the above steps. [post on using Google Powermeter]: /blog/2010/03/google-powermeter/ [perl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.pl [python]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_read.py [cc128_parse.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_parse.pl [cc128_log_mysql.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/cc128_log_mysql.pl [2cheap2meter]: http://2cheap2meter.blogspot.com/2010/03/setting-up-google-powermeter.html [google_powermeter_update.pl]: http://bitbucket.org/oojah/mosquitto/src/tip/misc/currentcost/google_powermeter_update.pl mosquitto-2.0.18/www/posts/2010/06/mosquitto-0-7rc1.md000066400000000000000000000006351450213760600217560ustar00rootroot00000000000000 Mosquitto 0.7 release candidate 1 is available for testing. Please give it a try and report back any problems you find. The source code is available at  and I'll hopefully have various binaries available soon. mosquitto-2.0.18/www/posts/2010/06/version-0-7-released.md000066400000000000000000000030231450213760600225450ustar00rootroot00000000000000 This is a new features release. Note that although the number of changes is relatively small, there is a fairly major change in the network socket handling (to allow >1024 clients) , which is one reason this has been treated as a separate release. Changes: * Use poll() instead of select() to allow >1024 clients. * Implement `max_connections`. * Run VACUUM on in-memory database on receiving SIGUSR2. * mosquitto_pub can now send null (zero length) messages. * Add option to print debug messages in pub and sub clients. * hg revision is now exported via $SYS/broker/changeset * Add compile time option to disable heap memory tracking. Bug fixes: * Don't store QoS=0 messages for disconnected clients with subscriptions of QoS>0. * accept() all available sockets when new clients are connecting, rather than just one (performance advantage) * Send Will when client exceeds keepalive timer and is disconnected. * Check to see if a client has a will before sending it. * Correctly deal with clients connecting with the same id multiple times. * Fix bridge keepalive timeouts and reconnects. * Don't attempt to drop root privileges when running on Windows as this isn't well supported (bug #586231). Source downloads are available at the [download page] Links for binary packages on Ubuntu and Fedora can be found on the same page. [download page]: /download mosquitto-2.0.18/www/posts/2010/07/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/07/mosquitto-on-opensuse-11-3.md000066400000000000000000000010601450213760600236710ustar00rootroot00000000000000 The upcoming release of [openSUSE], version 11.3, includes extension support for sqlite3 which means it now has everything required for mosquitto. I've created packages for this new version of openSUSE and details can be found on the [download page]. You just need to wait three days until the release of 11.3! [openSUSE]: http://www.opensuse.org/ [download page]: /download mosquitto-2.0.18/www/posts/2010/07/mqtt-client-library.md000066400000000000000000000021521450213760600227030ustar00rootroot00000000000000 I have been working on a client library for MQTT for the next release of Mosquitto. It is now at a stage where it is usable and ready for wider testing. There isn't any documentation yet (!) so it's only available in the source repository at . Use the "get source" link in the top right corner of the page to download a snapshot. If you're interested in developing your own open source MQTT clients, it'd be great if you could take a look to make sure the interface is sane before I make a release! The library itself is written in C, with bindings for C++ and Python. I plan to package it up in a more easy to access form in the not too distant future, hopefully with some documentation as well. # Update I've put the start of a man page online, which shows an example of using libmosquitto to subscribe to a topic and print the results: [libmosquitto.3]. [libmosquitto.3]: http://mosquitto.org/man/libmosquitto-3.html mosquitto-2.0.18/www/posts/2010/08/000077500000000000000000000000001450213760600164575ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/08/compiling-mosquitto-on-mac-os-x.md000066400000000000000000000011241450213760600250560ustar00rootroot00000000000000 In a follow up to his screencast [demoing mosquitto on Mac OS X], Andy Piper has written a blog post detailing how he got compiled mosquitto and, more importantly, the dependencies, on the Mac. Take a look over at [OS X mosquitto "bites"...] [demoing mosquitto on Mac OS X]: /blog/2010/08/mosquitto-running-on-mac-os-x/ [OS X mosquitto "bites"...]: http://andypiper.co.uk/2010/08/08/os-x-mosquitto-bites/ mosquitto-2.0.18/www/posts/2010/08/mosquitto-running-on-mac-os-x.md000066400000000000000000000007371450213760600245660ustar00rootroot00000000000000 Andy Piper has put together a 2 minute screencast demoing mosquitto running on Mac OS X and showing publishing and subscribing using the mosquitto command line clients and the IBM java gui client. View the screen cast here: . Thanks Andy! mosquitto-2.0.18/www/posts/2010/08/mqtt-v3-1.md000066400000000000000000000016701450213760600204560ustar00rootroot00000000000000 The MQTT v3 spec has been updated to v3.1. The significant change is the inclusion of the option to send a username and password as part of the connect command. The new spec is available at and is a lot more readable and clear than the original. Mosquitto will support the v3.1 spec in a future release, along with the ability to control both broker and topic access by username. In the meantime, if you need this functionality, the IBM proprietary [RSMB] broker may be suitable for testing purposes. The RSMB package now also includes an MQTT client library, a simple publish client and a simple subscribe client, just like mosquitto. Be sure to check the license terms before using it! [RSMB]: http://www.alphaworks.ibm.com/tech/rsmb mosquitto-2.0.18/www/posts/2010/08/version-0-8-1-released.md000066400000000000000000000032251450213760600227120ustar00rootroot00000000000000 This is a minor release. The primary reason for it is the amount of interest in the Python interface to libmosquitto. This release tidies up the Python interface considerably (it is now more "Pythonic" and easier to use), and significantly, brings the promised packages. This release also provides a few fixes, including to the packaging and installation scripts. Unfortunately, it does also include a known bug that was fixed prior to release, but accidentally left unmerged. This affects mosquitto_pub client when using the -l option (publish line by line input from stdin), causing it to exhibit high cpu load. I'll make a new bug fix release in a few days with this and any other fixes that come up. This release also provides improved packaging options. All of the available options are now packaged for Ubuntu, including the libmosquitto0-python package. Because there are now multiple packages, it is possible to provide some mosquitto functionality on distributions where the version of sqlite3 is too old. The packages available on these systems are listed as "clients only": * Fedora 12, 13 (full support) * openSUSE 11.3 (full support) * openSUSE 11.1, 11.2 (clients only) * Redhat Enterprise Linux 5 (clients only) * CentOS 5 (clients only) Details are available on the [download page]. Please note that some distributions have different naming schemes, so the Python module can be called both python-mosquitto and libmosquitto0-mosquitto for example. [download page]: /download mosquitto-2.0.18/www/posts/2010/08/version-0-8-2.md000066400000000000000000000014141450213760600211270ustar00rootroot00000000000000 This is a bugfix release. * Fix default loop() timeout value in mosquitto.py. Previous value was 0, causing high cpu load. * Fix message handling problem in client library when more than one message was in the client queue. * Fix the logic used to determine whether a QoS>0 message needs to be retried. * Fix the Python sub.py example so that it quits on error. See the [download page]. Includes Windows 32-bit binaries for the broker compiled with Cygwin, and the client library and clients compiled natively with Visual Studio to allow developing native Windows MQTT clients. [download page]: /download mosquitto-2.0.18/www/posts/2010/08/version-0-8-released.md000066400000000000000000000063301450213760600225540ustar00rootroot00000000000000 This is the library release. There are a few bug fixes and changes of behaviour for the mosquitto and the clients, but the significant part of this release is the new mosquitto MQTT client library. The library comes in three flavours: the C library, which is the main library, and C++ and Python bindings. If you're interested in helping add bindings for your favourite language, please get in touch. The library interface (API) is to be considered experimental, although I believe the C and C++ APIs to be complete and sane. The Python bindings are a naïve attempt by a C programmer and will definitely be changing in the future to something more pythonic. I'd be extremely grateful for help from experienced python programmers to this end. The documentation of the library is currently ongoing... There is an overview of most of the function calls and an example in the [libmosquitto.3] man page, but complete coverage can be found in the mosquitto.h man page. This, combined with the class details in mosquittopp.h can be used to help use the C++ library. The python module isn't documented due to its extremely changeable state, but there is an example in the python directory. Other changes: * Topics starting with a / are treated as distinct to those not starting with a /. For example, /topic/path is different to topic/path. This matches the behaviour of rsmb. * Correctly calculate the will QoS on a new client connection (bug #597451). * Add "addresses" configuration file variable as an alias of "address", for better rsmb compatibility. * Bridge `clean_session` setting is now false, to give more sensible behaviour and be more compatible with rsmb. * Add `cleansession` variable for configuring bridges. * Add `keepalive_interval` variable for bridges. * Remove default topic subscription for mosquitto_sub because the old behaviour was too confusing. * Added a C client library, which the pub and sub clients now use. * Added a C++ client library (bound to the C library). * Added a Python client library (bound to the C library). * Added CMake build scripts to allow the library and clients (not the broker) to be compiled natively on Windows. Get it from the [download page]. The change to using a library means that packaging mosquitto for distros is a lot more complex. This is stretching my packaging experience, so please bear with me on that front! Mosquitto will now likely consist of a number of different packages on Ubuntu at least: * mosquitto (the broker) * mosquitto-clients (mosquitto_sub, mosquitto_pub) * libmosquitto0 (C library) * libmosquitto0-dev (C library development files) * libmosquittopp0 (C++ library) * libmosquittopp0-dev (C++ library development files) * libmosquitto-python (Python binding) # Update I've been getting a few questions about the python interface. This isn't currently packaged for Ubuntu, but hopefully will be soon. There are basic python examples in the downloads at lib/python/sub.py and misc/currentcost/gnome-panel/CurrentCostMQTT.py [libmosquitto.3]: /man/libmosquitto-3.html [download page]: /download mosquitto-2.0.18/www/posts/2010/09/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/09/debian-packages.md000066400000000000000000000011451450213760600220010ustar00rootroot00000000000000 I've created some packages for Debian on i386 and amd64. They can be found at . They are almost identical to the Ubuntu packages (Debian doesn't use upstart, so there is a different init script), but compiled against Debian testing (Squeeze) instead. This is because Debian 5 (Lenny) doesn't include a recent enough version of sqlite3. Please let me know if you have any problems with the packages. mosquitto-2.0.18/www/posts/2010/09/mqtt-with-php.md000066400000000000000000000014221450213760600215240ustar00rootroot00000000000000 Using MQTT in PHP has been possible for a long time using the [Simple Asynchronous Messaging] MQTT class. Unfortunately this is an imperfect solution due to unclear licensing, some slightly dubious design decisions and bugs. Thankfully, [Andrew Milstead] has started creating an alternative implementation. It is MIT licensed and available on [github]. It's very new, so if you have problems check back to see if there have been updates and then let Andrew know. [Simple Asynchronous Messaging]: http://project-sam.awardspace.com/ [Andrew Milstead]: http://twitter.com/bluerhinos [github]: http://github.com/bluerhinos/phpMQTT mosquitto-2.0.18/www/posts/2010/10/000077500000000000000000000000001450213760600164505ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/10/man-page-translations.md000066400000000000000000000021261450213760600231770ustar00rootroot00000000000000 Something that is very much in the back of my mind whilst developing mosquitto is that it should have support for language translations. I've been reluctant to start this effort until development was a little bit more settled, to avoid wasting translators time. I think it's now time to start the ball rolling. I'm going to approach this in two stages - the man pages and the programs. Man pages are first. I've imported the translation templates to launchpad, so if your native tongue is anything other than English and you'd like to translate them - please go ahead. I'll be putting any translated man pages (with credits :) into the upcoming 0.9 release. I think I've finished making changes but if I haven't, the one most likely to change is for mosquitto.conf The translation page can be found at Thanks in advance, and please get in touch if you find any strings that don't make sense. mosquitto-2.0.18/www/posts/2010/10/one-year-old.md000066400000000000000000000021511450213760600212640ustar00rootroot00000000000000 On the 25th October 2009, at 21:40:51 (just five hours and forty minutes after the first [oggcamp] ended :), I made the first commit to what would become the mosquitto source code repository. Although there was no code committed until the next day, and the first release wasn't until almost six weeks later, I consider this to be when mosquitto was born. It's been a good year. Thanks to everybody who has helped out and been in touch! I had hoped to release version 0.9 today, but it isn't to be. Nevertheless, I hope you'll join me in hoping for an even more successful year ahead. I'm aiming for a 1.0 release before this time next year with full MQTT 3.1 support, full rsmb feature set (except where inappropriate), IPv6 support, SSL support, language translation for the programs and man pages, full API documentation and examples, and whatever I think of in the meantime. Have a feature you're particularly interested in? Leave a comment! :) [oggcamp]: http://oggcamp.org/ mosquitto-2.0.18/www/posts/2010/10/version-0-8-3-released.md000066400000000000000000000007241450213760600227060ustar00rootroot00000000000000 This is a bugfix release. * Fix compliance with the MQTT protocol for messages published at QoS 2. This means that messages that time out are dealt with correctly and duplicate messages are also dealt with correctly. See the [download page] for the update. [download page]: /download mosquitto-2.0.18/www/posts/2010/11/000077500000000000000000000000001450213760600164515ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/11/distro-packaging.md000066400000000000000000000030211450213760600222150ustar00rootroot00000000000000 Starting with version 0.9, I plan on getting mosquitto packaged in the major Linux distributions. By this I mean Debian, Fedora, openSUSE and Ubuntu.  This is my understanding of the current state of play of those distributions. Feel free to correct me! * Debian is currently in freeze for the Squeeze release. Mosquitto will have to go into Squeeze+1, although it can still be uploaded to Unstable. * Fedora 14 has just been released, Fedora 15 will have feature freeze on the 8th of February. * openSUSE 11.4 will have feature freeze at the start of December. * Ubuntu 11.04 has its Debian import freeze on the 30th of December and feature freeze on 24th of February. The plan is therefore to release 0.9 around the 14th of November and aim to be packaged for Debian unstable before 30th of December and openSUSE before the start of December, with packaging for Fedora 15 coming at some point later. If packaging for Debian unstable isn't possible before the Ubuntu import freeze, then package for Ubuntu separately. If you can help out with the packaging process for any of the above, I'd love to hear from you. If your distribution isn't included and you'd like it to be, get in touch as well and we'll see what's possible. Finally, this won't stop me producing Ubuntu PPA or openSUSE build service packaged binaries for those that prefer to stay at the cutting edge. mosquitto-2.0.18/www/posts/2010/11/mosquitto-0-9test2.md000066400000000000000000000036651450213760600223360ustar00rootroot00000000000000 Mosquitto 0.9, which I hope to release mid November, represents the most significant change to mosquitto to date - the removal of sqlite as an absolute dependency. In addition, this removes the dependency on the sqlite3-pcre extension and on pcre. This gives a definite performance improvement, reduces the amount of object code that needs loading by around 95%, reduces memory usage and also makes it lots easier to compile on more unusual systems. It's quite a substantial change though, so I've made a test release to hopefully get some external testing. If you could give it a try and report back that'd be great. The source is at . (use the highest numbered version available). There are also Ubuntu packages available at the [mosquitto-expt ppa] and binaries for Fedora, Mandriva,  SLES and openSUSE at the [openSUSE build service]. If you'd like binaries for other systems, please get in touch. Note that this is a test release, not a release candidate - there are definitely things that still need changing. The following list shows the points I'm currently aware of: * Old style sqlite will be imported when the option is compiled in (enabled by default). This import currently only imports retained messages and durable subscriptions, but not queued messages. * ~~The `max_inflight_messages` and `max_queued_messages` config options are ignored and no maximum is applied.~~ * ~~The CMake compilation scripts aren't updated.~~ # Update I've uploaded test3 with a python fix, updated CMake scripts and fixed `max_inflight_messages` and `max_queued_mesages`. [mosquitto-expt ppa]: https://launchpad.net/~mosquitto-dev/+archive/mosquitto-expt [openSUSE build service]: https://build.opensuse.org/project/show?project=home%3Aoojah%3Amqtt_expt mosquitto-2.0.18/www/posts/2010/11/version-0-9-released.md000066400000000000000000000102331450213760600225440ustar00rootroot00000000000000 This is a features release. It is probably the most significant change for mosquitto so far. The important change is the removal of the sqlite dependency, along with the associated pcre and sqlite3-pcre dependencies. This results in better performance both in terms of messages handled per second and memory usage. Optional support for importing existing persistent databases in sqlite3 format is provided, with the option compiled in by default. This will be set to not be compiled by default in 0.10 and then removed in 0.11. This release also provides support for the recently updated MQTT v3.1 spec - most notably offering username/password authentication support. The client library and clients have full v3.1 support. The broker is fully compatible with v3.1, but doesn't provide any mechanism for controlling username/password control. This will come in 0.10. One goal of mosquitto is to be a drop in replacement for the IBM rsmb broker. Another goal is to do more than rsmb :) I'm still working on the first goal, but this release helps with the second as mosquitto now has IPv6 support, which isn't supported in rsmb. A detailed list of changes is given below: * Client and message data is now stored in memory with custom routines rather than a sqlite database. This removes the dependencies on sqlite, pcre and sqlite3-pcre. It also means that the persistent database format has had to be reimplemented in a custom format. Optional support for importing old sqlite databases is provided. * Added IPv6 support for mosquitto and the clients. * Provide username and password support for the clients and client libraries. This is part of the new MQTT v3.1 spec. * The broker supports the username and password connection flags, but will not do anything with the username and password. * Python callback functions now optionally take an extra argument which will return the user object passed to the `Mosquitto()` constructor, or the calling python object itself if nothing was given to `Mosquitto()`. * Remove the mosquitto command line option `-i interface`. * Remove the mosquitto.conf "interface" variable. * Add support for the listener config variable (replaces the interface variable) * Add support for the `bind_address` config variable. * Change the port config variable behaviour to match that of rsmb (applies to the default listener only, can be given just once). * Fix QoS 2 protocol compliance - stop sending duplicate messages and handle timeouts correctly. Fixes bug #598290. * Set retain flag correctly for outgoing messages. It should only be set for messages sent in response to a subscribe command (ie. stale data). * Fix bug in returning correct CONNACK result to `on_connect` client callback. * Don't send client will if it is disconnected for exceeding its keepalive timer. * Fix client library unsubscribe function incorrectly sending a SUBSCRIBE command when it should be UNSUBSCRIBE. * Fix `max_inflight_messages` and `max_queued_messages` operation. These parameters now apply only to QoS 1 and 2 messages and are used regardless of the client connection state. * mosquitto.conf now installed to /etc/mosquitto/mosquitto.conf instead of /etc/mosquitto.conf. The /etc/mosquitto/ directory will be used for password and access control files in the future. * Give the compile time option of using 32-bit integers for the database IDs instead of 64-bit integers. This is useful where htobe64()/be64toh() are not available or for embedded systems for example. * The DUP bit is now set correctly when resending PUBREL messages. * A port to Windows native has been partially completed. This currently drops a number of features, including the ability to change configuration parameters and persistent storage. See the [download page] for the update. Debian and Ubuntu users should note that the package libmosquitto0-python has been renamed python-mosquitto to comply with Debian naming policies. The Debian packages aren't yet ready. [download page]: /download mosquitto-2.0.18/www/posts/2010/12/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2010/12/version-0-9-1-released.md000066400000000000000000000010071450213760600227020ustar00rootroot00000000000000 This is a bugfix release. * Add missing code for parsing the `bind_address` configuration option. * Fix missing include when compiling with tcp-wrappers support. * Add linker version script for C library to control exported functions. Source code is on the [download page], binary packages will follow on later. [download page]: /download mosquitto-2.0.18/www/posts/2011/000077500000000000000000000000001450213760600162315ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/01/000077500000000000000000000000001450213760600164515ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/01/mosquitto-for-slackware.md000066400000000000000000000010041450213760600235700ustar00rootroot00000000000000 Chris Willing of the University of Queensland has very kindly put together some mosquitto packages for Slackware 13.0 and 13.1 as well as instruction on how to build your own packages. The packages and instructions are here: I've also put the link on the downloads page. Thanks Chris! mosquitto-2.0.18/www/posts/2011/01/mqtt-news.md000066400000000000000000000010541450213760600207320ustar00rootroot00000000000000 Here are some MQTT updates from out there on the internet: A new perl client implementation by Mark Hindess * A [homebrew] recipe for installing mosquitto on Mac by Adam Rudd * MQTT implemented for the mbed processor by Yiluin Fan * [homebrew]: http://brew.sh/ mosquitto-2.0.18/www/posts/2011/02/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/02/lightweight-messaging-and-linux.md000066400000000000000000000010761450213760600251670ustar00rootroot00000000000000 Andy Piper was at [Linux Conf Australia] this year and gave a talk on MQTT. His blog post Lightweight Messaging and Linux] gives a few details and has a link to the slides. The video can be seen at [Linux Conf Australia]: http://linux.conf.au/ [Lightweight Messaging and Linux]: http://andypiper.co.uk/2011/01/28/lightweight-messaging-and-linux/ mosquitto-2.0.18/www/posts/2011/02/mosquitto-on-maemo.md000066400000000000000000000006401450213760600225460ustar00rootroot00000000000000 Yuvraaj Kelkar got in touch to say he's packaged up mosquitto and the client libraries for Maemo. If you want to use MQTT on your Maemo device then take a look at the details on Thanks Yuvraaj! mosquitto-2.0.18/www/posts/2011/02/mqtt-on-android.md000066400000000000000000000010451450213760600220110ustar00rootroot00000000000000 Dale Lane has written an enormous blog post [Using MQTT in Android Mobile Applications in which he talks about a lot of the points you are likely to want to consider if you're writing MQTT applications for Android. There's lots of useful information and he even includes a complete source code implementation. [Using MQTT in Android Mobile Applications]: http://dalelane.co.uk/blog/?p=1599 mosquitto-2.0.18/www/posts/2011/02/version-0-9-2-released.md000066400000000000000000000016171450213760600227120ustar00rootroot00000000000000 This is a bugfix release: * Only send a single DISCONNECT command when using -l in the pub client. * Set QoS=1 on PUBREL commands to meet protocol spec. * Don't leak sockets on connection failure in the library. * Install man pages when building under cmake. * Fix crash bug on malformed CONNECT message. * Clients are now rejected if their socket peer name cannot be obtained on connection. * Fix a number of potential problems caused when a client with a duplicate id connects. * Install mosquitto.conf under cmake. Thanks to Mark Hindess, Joshua Lock, Adam Rudd and Ben Davenport for their help. The source code is available as always on the [download page]. Binaries will appear shortly. [download page]: /download mosquitto-2.0.18/www/posts/2011/03/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/03/api-documentation.md000066400000000000000000000010021450213760600224060ustar00rootroot00000000000000 I've rewritten the API documentation for the C library using the [NaturalDocs] format. This covers the whole C library and so should give enough information for anybody using the C++ or Python wrappers as well. The documentation generated from mosquitto.h is available at [NaturalDocs]: http://www.naturaldocs.org/ mosquitto-2.0.18/www/posts/2011/03/mosquitto-in-mac-homebrew.md000066400000000000000000000006311450213760600240110ustar00rootroot00000000000000 Thanks to work done by Adam Rudd, mosquitto is now available in the Mac [homebrew](https://brew.sh) package manager. Once you've installed homebrew (see the link), you can install mosquitto with: ``` brew install mosquitto ``` mosquitto-2.0.18/www/posts/2011/03/version-0-9-3-released.md000066400000000000000000000013251450213760600227100ustar00rootroot00000000000000 This is a bugfix release: * Set retained message status for QoS 2 messages (bug #726535). * 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. * Don't clean queued messages when a non clean session client reconnects. * Make mosquitto.py compatible with Python <2.6. * Fix mosquitto.h header includes for Windows. Please see the [download page]. Thanks to Joe B, David Monro,  Yuvraaj Kelkar and Colin Jones. [download page]: /download mosquitto-2.0.18/www/posts/2011/04/000077500000000000000000000000001450213760600164545ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/04/version-0-10-released.md000066400000000000000000000024171450213760600226240ustar00rootroot00000000000000 This release brings the new MQTT v3.1 features to the broker - client authentication and topic access control. See [mosquitto.conf(5)] or the included example password and ACL files. * Implement support for the `password_file` option and accompanying authentication requirements in the broker. * Implement topic Access Control Lists. * `mosquitto_will_set()` and `mosquitto_publish()` now return `MOSQ_ERR_PAYLOAD_SIZE` if the payload is too large (>268,435,455 bytes). * Bridge support can now be disabled at compile time. * Group together network writes for outgoing packets - don't send single byte writes! * Add support for `clientid_prefixes` variable. * Add support for the `clientid` config variable for controlling bridge client ids. * Remove 32-bit database ID support because htobe64() no longer used. * Multiple client subscriptions to the same topic result in only a single subscription. Bug #744077. Please see the [download page]. Thanks to Adam Rudd, Joshua Lock,  Sang Kyeong Nam and Yuvraaj Kelkar. [mosquitto.conf(5)]: /man/mosquitto-conf-5.html [download page]: /download mosquitto-2.0.18/www/posts/2011/05/000077500000000000000000000000001450213760600164555ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/05/mqtt-ontology.md000066400000000000000000000012461450213760600216370ustar00rootroot00000000000000 Mark Hindess has written a blog post titled [Home Automation Protocols: MQTT], where he asks for suggestions on how to go forward making "a specification for topic usage and semantics". I think this kind of work is really valuable to make it easy to have different MQTT systems that can interoperate. If you've got any suggestions you can make, please go and leave a comment there. [Home Automation Protocols: MQTT]: http://www.temporalanomaly.com/blog/2011/05/02/home-automation-protocols:-mqtt mosquitto-2.0.18/www/posts/2011/05/version-0-10-1-released.md000066400000000000000000000007701450213760600227630ustar00rootroot00000000000000 This is a bugfix release primarily for Windows users. * Fix Windows compilation. * Fix mosquitto.py on Windows - call lib init/cleanup. * Don't abort when connecting if given an unknown address type (assuming an IPv4 or IPv6 address is given). Please see the [download page]. Thanks to Karl Palsson. [download page]: /download mosquitto-2.0.18/www/posts/2011/06/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/06/nanode-a-cheap-networked-arduino-clone.md000066400000000000000000000015471450213760600263040ustar00rootroot00000000000000 The arduino, the open source microcontroller board, has had MQTT support for a long time in the form of [Nick O'Leary's arduino client]. It does however require networking support which has traditionally provided by an add on shield, which increases the cost of the system. The [Nanode] is an arduino compatible board which includes network support and can be built for approximately the same cost as a normal arduino board. It's still a work in progress, but is definitely worth a look if you want to use low power MQTT capable sensors/controllers. [Nick O'Leary's arduino client]: http://knolleary.net/arduino-client-for-mqtt/ [Nanode]: http://nanode.eu/ mosquitto-2.0.18/www/posts/2011/06/version-0-10-2-released.md000066400000000000000000000013141450213760600227600ustar00rootroot00000000000000 This is a bugfix release. * Don't abort when connecting if the first connection fails. This is important on e.g. Windows 7, where IPV6 is offered as the first choice but may not be available. * Deal with long logging messages properly (bug #785882). * Fix library compilation on Symbian - no pselect() available. * Don't stop processing subscriptions on received messages after a subscription with # matches. (bug #791206). Please see the [download page]. Thanks again to Karl Palsson and Yuvraaj Kelkar. [download page]: /download mosquitto-2.0.18/www/posts/2011/06/version-0-11-1-released.md000066400000000000000000000006761450213760600227720ustar00rootroot00000000000000 This is an important bugfix release. It fixes a buffer overrun that affects 0.11 only. Users of 0.11 should upgrade immediately. * Fix buffer overrun when checking for + and # in topics (bug #799688). * Pub client now quits if publish fails. Thanks to Karl Palsson. mosquitto-2.0.18/www/posts/2011/06/version-0-11-2-released.md000066400000000000000000000005251450213760600227640ustar00rootroot00000000000000 This is a bugfix release. * Don't free contexts in mqtt3_context_disconnect() (bug #799688 / #801678). * Only free will if present when freeing a client context. mosquitto-2.0.18/www/posts/2011/06/version-0-11-released.md000066400000000000000000000035341450213760600226300ustar00rootroot00000000000000 This is an update with some fairly minor changes and some bug fixes. I had planned on more exciting features but my time has been occupied getting ready for the 25th, when I'm getting married. Those changes will just have to wait until 0.12! * Removed all old sqlite code. * Remove client id limit in clients. * Implemented $SYS/broker/heap/maximum size * Implemented $SYS/broker/clients/inactive to show the number of disconnected non-clean session clients. * $SYS/broker/heap/current size and maximum size messages now include "bytes" to match rsmb message format. * Implemented the `retained_persistence` config file option - a synonym of the `persistence` option. * Added security_external.c to broker source to make it easier for third parties to add support for their existing username/password and ACL database for security checks. See external_security_checks.txt. * $SYS messages are now only republished when their value changes. * Windows native broker now responds to command line arguments. * Simplify client disconnecting so wills gets sent in all cases (bug #792468). * Clients now have a `--quiet` option. * The on_disconnect() callback will always be called now, even if the client has disconnected unexpectedly. * Always close persistent DB file after restoring. * Return error code when exiting the clients. * mosquitto_publish() now returns `MOSQ_ERR_INVAL` if the topic contains + or # * mosquitto now silently rejects published messages with + or # in the topic. * `max_connections` is now a per-listener setting instead of global. * Connection count is now reduced when clients disconnect (bug #797983). Thanks to Sebastian Kroll and Karl Palsson. mosquitto-2.0.18/www/posts/2011/07/000077500000000000000000000000001450213760600164575ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/07/debian-and-ubuntu-packaging.md000066400000000000000000000016411450213760600242270ustar00rootroot00000000000000 I'm very pleased to say that Mosquitto is very nearly packaged in Debian and Ubuntu. In truth, 0.10 is packaged and uploaded for both Debian testing (Wheezy) and Ubuntu Oneiric Ocelot, but there is a problem with the config that means it won't restart properly. That is fixed with the 0.11.3 upload which is now in unstable. That means after 10 days and it will be in Debian testing for all to use. I've also submitted a sync request with Ubuntu ([bug #808530]) to ensure it makes it across. I'll still be maintaining the Launchpad PPA for older versions of Ubuntu. Thanks to the Debian developer Michael Tautschnig for reviewing my package and doing the upload. [bug #808530]: https://bugs.launchpad.net/ubuntu/+source/mosquitto/+bug/808530 mosquitto-2.0.18/www/posts/2011/07/lua-mqtt-client.md000066400000000000000000000010701450213760600220170ustar00rootroot00000000000000 Andy Gelme [reports] that his Lua MQTT client is ready for use. The downloads, installation and usage instructions, example code and api information are all available at . I particularly like the image of it running on a PSP. Well done Andy! I wonder what the next language to get MQTT support will be? [reports]: https://twitter.com/#%21/geekscape/status/96710950979256323 mosquitto-2.0.18/www/posts/2011/07/mosquitto-on-qnx.md000066400000000000000000000015411450213760600222640ustar00rootroot00000000000000 Andrea asked a [question on launchpad] about problems compiling Mosquitto on QNX. I've now managed to get an evaluation version of QNX and fix the compilation problems. These fixes will be in 0.12, but you can get them in the current snapshot if it's urgent. I've also put compiled binaries in the [downloads directory] but they are completely untested, so use at your own risk. Although I've provided these binaries I don't intend to keep doing so for each version of Mosquitto. I will endeavour to fix any other problems that arise in the future though. [question on launchpad]: https://answers.launchpad.net/mosquitto/+question/164154 [downloads directory]: http://mosquitto.org/files/binary/qnx/ mosquitto-2.0.18/www/posts/2011/07/version-0-11-3-released.md000066400000000000000000000014071450213760600227660ustar00rootroot00000000000000 This is a bugfix release. * Don't complain and quit if `persistence_file` option is given (bug #802423). * Initialise listeners correctly when clients with duplicate client ids connect. Bug #801678. * Memory tracking is now disabled for Symbian builds due to lack of malloc.h. * Fix memory tracking compilation for kFreeBSD. * Python callbacks can now be used with class member functions. * Fix persistent database writing of client message chunks which caused errors when restoring (bug #798164) Thanks to Neil Bothwick, Yuvraaj Kelkar, Craig Hollabaugh, Karl Palsson and Andy Piper. mosquitto-2.0.18/www/posts/2011/07/version-0-12-released.md000066400000000000000000000032361450213760600226310ustar00rootroot00000000000000 This is an update with some features and bug fixes. The most significant change is configuration reloading support. This will be improved to include bridge reloading in the future. * Reload (most) configuration on SIGHUP. * Memory tracking is no longer compiled in the client library. * Add `--help` option to mosquitto to display usage. * Add `--id-prefix` option to clients to allow easier use with brokers that are using the `clientid_prefix` option. * Fix compilation on QNX. * Add `-P` as a synonym argument for `--pw` in the clients. * Fix python MosquittoMessage payload parameter. This is now returned as a pointer to an array of c_uint8 values so binary data is handled correctly. If a string is needed, use msg.payload_str * Fix memory leaks on client authentication. * If `password_file` is not defined then clients can now connect even if they  use a username/password. * Add mosquitto_reconnect() to the client library. * Add option for compiling with liberal protocol compliance support (enabled by default). * Fix problems with clients reconnecting and old messages remaining in the message store. * Display both ip and client id in the log message when a client connects. * Change the socket connection message to make it more obvious that it is just a socket connection being made (bug #801135). * Fix retained message delivery where a subscription contains a +. * Be more lenient when reloading persistent database to reduce errors with empty retained messages. mosquitto-2.0.18/www/posts/2011/07/wireshark-mqtt-decoder.md000066400000000000000000000007151450213760600233710ustar00rootroot00000000000000 If you're trying to debug your MQTT connection, you may be interested in something Karl P has written - an MQTT decoder/dissector for Wireshark. It doesn't have complete protocol support yet, but is a good start. * mosquitto-2.0.18/www/posts/2011/08/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/08/arch-linux-package.md000066400000000000000000000005271450213760600224510ustar00rootroot00000000000000 Gordon Pearce has packaged Mosquitto on Arch Linux through an Arch User Repository. The package details are at Thanks Gordon! mosquitto-2.0.18/www/posts/2011/08/facebook-using-mqtt.attachments.json000066400000000000000000000006161450213760600255470ustar00rootroot00000000000000{"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"]}}mosquitto-2.0.18/www/posts/2011/08/facebook-using-mqtt.md000066400000000000000000000024121450213760600226600ustar00rootroot00000000000000 Something else that has happened recently is the announcement by Facebook that they're using MQTT in their new [Facebook Messenger app] They've posted some details in a [facebook-engineering blogpost] and cite the low bandwidth and battery usage as important considerations. This is very exciting as an application that is potentially huge and very user oriented (rather than "internet of things" oriented), but the really exciting bit is if you use an iPhone under Settings and Licenses (apparently it's quite hard to find): Thanks to Michael Rowe for getting me the screenshot and Andy Piper for pestering Michael on my behalf. You should note that if you're in the UK, the Facebook Messenger app isn't currently available. [Facebook Messenger app]: https://www.facebook.com/mobile/messenger [facebook-engineering blogpost]: http://www.facebook.com/notes/facebook-engineering/building-facebook-messenger/10150259350998920 mosquitto-2.0.18/www/posts/2011/08/mosquitto-on-openwrt.md000066400000000000000000000015561450213760600231630ustar00rootroot00000000000000 Thanks to work done by Karl Palsson, Mosquitto is now available on [OpenWrt], the embedded Linux distribution frequently used on wireless routers. This is exciting if you want a really low power way of running an MQTT broker. It also includes the mosquitto clients and development libraries. It's only in the source tree at the moment, so if you want to install it I believe you'll have to download everything and compile it yourself. Update: Karl tells me that if you're running a binary snapshot from trunk then you can do: ``` opkg update opkg install mosquitto mosquitto-client libmosquitto ``` You only need to build it yourself if you're running a stable binary. [OpenWrt]: https://openwrt.org/ mosquitto-2.0.18/www/posts/2011/08/mqtt-standardisation.md000066400000000000000000000006071450213760600231570ustar00rootroot00000000000000 IBM have announced that MQTT is to be formally standardised. If you're interested in taking part in the process, there are full details at mosquitto-2.0.18/www/posts/2011/09/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/09/version-0-13-released.md000066400000000000000000000035171450213760600226360ustar00rootroot00000000000000 This release brings some new features and fixes. Although there are no real "killer features", this release does include some fairly significant updates. Of particular note are the fixes to subscription wildcard matching, which now meets the spec in all cases, the Python payload parameter being a Python string which should make life lots easier for Python developers, the non clean-session client fixes and related persistent database fixes. * Implement bridge state notification messages. * Save client last used mid in persistent database (DB version number bumped). * Expose message id in Python MosquittoMessage. * It is now possible to set the topic QoS level for bridges. * Python MosquittoMessage payload parameter is now a Python string, not a ctypes object which makes it much easier to use. * Fix queueing of messages for disconnected clients. The `max_queued_messages` option is now obeyed. * C++ library is now in its own namespace, mosquittopp. * Add support for adding log message timestamps in the broker. * Fix missing mosquitto_username_pw_set() python binding. * Fix keepalive timeout for reconnecting non clean-session clients. Prevents immediate disconnection on reconnection. * Fix subscription wildcard matching - a subscription of +/+ will now match against /foo * Fix subscription wildcard matching - a subscription of foo/# will now match * against foo * When restoring persistent database, clients should be set to non clean-session or their subscriptions will be immediately removed. * Fix SUBACK payload for multiple topic subscriptions. * Don't send retained messages when a client subscribes to a topic it is already subscribed to. mosquitto-2.0.18/www/posts/2011/10/000077500000000000000000000000001450213760600164515ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/10/mqtt-power-usage-on-android.md000066400000000000000000000005651450213760600242520ustar00rootroot00000000000000 Stephen Nicholas has carried out some power usage analysis of MQTT on Android. Details are at and the conclusion is that it doesn't use much power. mosquitto-2.0.18/www/posts/2011/10/two.md000066400000000000000000000033001450213760600176000ustar00rootroot00000000000000 Today (just) marks the 2nd birthday of the mosquitto project. In the past year mosquitto has undergone pretty substantial changes and improvements. Some highlights from the year: * Move away from using sqlite to store data in memory and on disk, resulting in a much more compact, better performing and *more elegant* broker * Windows native port * MQTT 3.1 support * Greatly improved Python module * Getting really close to being feature complete with respect to RSMB * Being packaged in Debian... * ... and Ubuntu * The mosquitto client code being used by Facebook in their iphone app * The numerous bugs reported, bugfixes, suggestions and general interest displayed by people. Thanks everyone! Mosquitto has gone from version 0.8.3 to 0.13 - so what about next year? This will be the year when 1.0 is released. The bar I'm setting is complete RSMB features, with the exception of some of the more esoteric ones. At the moment this means there are still some of the bridge features to implement and complete configuration reloading. I'm also going to have a much improved Windows port so there will be no need for a separate Cygwin version. At the same time I'm making a Windows installer and allowing mosquitto to be installed as a proper Windows service. This work should all be in 0.14. Another point for improvement is the Python module - it could be more Pythonic than it is now. My current plan is to have it throw exceptions rather than return integer error values but I could do with the help of a Python expert really. All in all I think it should be a good year. mosquitto-2.0.18/www/posts/2011/11/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/11/android-mqtt-example-project.md000066400000000000000000000030441450213760600244750ustar00rootroot00000000000000 To celebrate the news that the IBM Java MQTT client implementation will be released as open source, I've put together a simple Android example based on the [MQTT service code written by Dale Lane]. I'm a beginner at both Java and Android, so expect it to be a bit rough. The example displays incoming payload text on a text label. It's a complete project that you can build and install on your phone with only a few small changes - search for "CHANGE ME" in src/org/mosquitto/android/mqtt/MQTTDemo.java. To get the project working, assuming you've already installed the android sdk, first get the IBM Java library (see ) and put it in <project dir>/lib then do the following: ``` android update project -p <path to project> # If the update complains about build.xml - delete it and run again cd <path to project> ant debug sudo adb start-server ant installd ``` I'll not be at all surprised if there are problems in the project due to different sdk or tool versions. Please comment if you find a problem. The project is available from . Until the IBM Java implementation is open source please be aware of the licence attached to it. Thanks to Dale for the core Android MQTT service implementation. [MQTT service code written by Dale Lane]: http://dalelane.co.uk/blog/?p=1599 mosquitto-2.0.18/www/posts/2011/11/ibm-java-and-c-clients-to-be-open-source.md000066400000000000000000000030051450213760600263400ustar00rootroot00000000000000 The web is currently buzzing with the announcement yesterday that IBM and Eurotech are donating MQTT to the Eclipse Foundation. One part of this is the new [Machine to Machine Working Group], again part of Eclipse. Another much more significant part has been released as part of a new Eclipse open source project [Paho], which is in the proposal stage. The exciting part is the "Initial contributions" section which states "The initial code contribution to Paho will include  Java and C client-side implementations the MQTT protocol, contributed by IBM. " It looks like the code will be licensed under the EPL (Eclipse Public License). This is particularly exciting because there is currently no solid freely available and usable implementation of MQTT in Java. Well done everyone at IBM for making this happen. Roll on the end of November! Update: This post on mqtt.org explains what the various announcements mean more clearly: Update 2: Andy Piper's blog post covers things even better, along with clearing up some of the confusions from the news releases: [Machine to Machine Working Group]: http://wiki.eclipse.org/M2MIWG_charter_draft [Paho]: http://www.eclipse.org/proposals/technology.paho/ mosquitto-2.0.18/www/posts/2011/11/new-linux-repositories.md000066400000000000000000000005661450213760600234560ustar00rootroot00000000000000 I've just added some more Linux repositories to the download page for Fedora 16 and SLE 10, 11 and 11 SP1. Note that mosquitto-python isn't available on SLE 10. See the [download page](/download). mosquitto-2.0.18/www/posts/2011/11/version-0-14-1-released.md000066400000000000000000000003661450213760600227650ustar00rootroot00000000000000 This is a bugfix release. * Fix Python syntax errors (bug #891673). mosquitto-2.0.18/www/posts/2011/11/version-0-14-2-released.md000066400000000000000000000004401450213760600227570ustar00rootroot00000000000000 This is a bugfix release: * Add uninstall target for libs. * Don't try to write packet whilst in a callback. mosquitto-2.0.18/www/posts/2011/11/version-0-14-released.md000066400000000000000000000033011450213760600226170ustar00rootroot00000000000000 This is a fairly minor feature release. The major changes are the pattern matching ACL support, the support for running directly as a Windows service and the change to the network code to attempt to send packets immediately. The Windows binary is now supplied as an installer rather than a zip file. * Add support for matching ACLs based on client id and username. * Add a Windows installer file (NSIS based). * Add native support for running the broker as a Windows service. This is the default when installed using the new installer. * Fix client count for listeners. When clients disconnect, decrement the count. Allow `max_connections` to work again. * Attempt to send all packets immediately upon being queued. This will result in more immediate network communication in many cases. * Log IP address when reporting CONNACK packets if the client id isn't yet known. * Fix payload length calculation in python `will_set` function. * Fix Python publish and `will_set` functions for payload=None. * Fix keepalive value being lost when reconnecting a client (bug #880863). * Persistence file writing now uses portable file functions, so the Cygwin broker build should no longer be necessary. * Duplicate code between the client and broker side has been reduced. * Queued messages for clients reconnecting with `clean_session=false` set were not being sent until the next message for that client was received. This has been fixed (bug #890724). * Fix subscriptions to # incorrectly matching against topics beginning with / mosquitto-2.0.18/www/posts/2011/12/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2011/12/mqtt-on-nanode.md000066400000000000000000000017311450213760600216400ustar00rootroot00000000000000 [Nanode], the popular arduino-with-ethernet board started early in 2011 is ideal for small MQTT based projects but has so far lacked an implementation of MQTT. Nick O'Leary, the author of the original Arduino MQTT client, [has created a Nanode implementation], but it [isn't quite ready for the public]. Nicholas Humfrey has made public some code at that he says [still needs some work] but supports publishing QoS 0 messages of up to 127 bytes long and subscribing to topics with QoS 0. [Nanode]: http://nanode.eu/ [has created a Nanode implementation]: https://twitter.com/#!/knolleary/status/151057575775965184 [isn't quite ready for the public]: https://twitter.com/#!/knolleary/status/151059089881960448 [still needs some work]: https://twitter.com/#!/njh/status/152913104446038018 mosquitto-2.0.18/www/posts/2011/12/version-0-14-3-released.md000066400000000000000000000011451450213760600227640ustar00rootroot00000000000000 This is a bugfix release. * Fix potential crash when client connects with an invalid CONNECT packet. * Fix incorrect invalid socket comparison on Windows. * Server shouldn't crash when a message is published to foo/ when a subscription to foo/# exists (bug #901697). * SO_REUSEADDR doesn't work the same on Windows, so don't use it. * Cygwin builds now support Windows service features. * Fix $SYS/broker/bytes/sent reporting. mosquitto-2.0.18/www/posts/2012/000077500000000000000000000000001450213760600162325ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/01/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/01/challenge-web-based-mqtt-graphing.md000066400000000000000000000026771450213760600253410ustar00rootroot00000000000000 Thanks to a data feed courtesy of an IBM broker, [test.mosquitto.org] now publishes information on energy generation and demand in the UK (in the energy/ topic tree). I think this could be used as a great demonstration for coupling MQTT and the web. # The challenge Create a web based report that takes energy data from the broker over MQTT and displays it in interesting and useful ways. Alternatively, an Android/iPhone app would be ok, but web based is the preferred option. # The rules There are no rules really. Having said that, I'd be most pleased if the end result was something that other people could learn from. There are bonus points for solutions that work where a web proxy is the only internet access. If you want to use new or unusual technologies that's fine. # The prize I'm afraid there is no tangible prize - I hope you'll be content with your work being shown here and the respect of your peers. # Some suggestions Google charts is definitely worth looking at for generating the actual graphs. Some examples of what you might show are: * Pie chart of generation source * Gauge of current mains frequency * Historical graph of electricity export amount I look forward to any and all responses! [test.mosquitto.org]: http://test.mosquitto.org/ mosquitto-2.0.18/www/posts/2012/01/do-you-use-mqtt.md000066400000000000000000000014101450213760600217610ustar00rootroot00000000000000 I saw this in the nanode irc channel: > I've never seen any real world projects with MQTT... it looks good though. So I'm looking for real world projects that use MQTT. If you've got a project it'd be great if you could mention it in the comments. A short sentence on what it does and how many clients you run on it - really anything you can say. If it's a secret please still comment if you can, just be very very vague. If you've got a blog post describing it, link that instead. I'm interested in everything from a single temperature sensor reporting to a computer up to millions of mobile users. Thanks! mosquitto-2.0.18/www/posts/2012/01/mosquitto-test-server.md000066400000000000000000000005101450213760600233150ustar00rootroot00000000000000 A publicly accessible Mosquitto server is now available to use. Details are at [test.mosquitto.org] [test.mosquitto.org]: http:/test.mosquitto.org/ mosquitto-2.0.18/www/posts/2012/01/version-0-14-4-released.md000066400000000000000000000006251450213760600227660ustar00rootroot00000000000000 This is a bugfix release: * Fix local bridge notification messages. * Fix return values for more internal library calls. * Fix incorrect out of memory checks in library and broker. * Never time out local bridge connections. mosquitto-2.0.18/www/posts/2012/02/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/02/mqtt2pachube.md000066400000000000000000000030171450213760600213750ustar00rootroot00000000000000 I've written a tool to help get data from mqtt to [pachube]. Existing pachube libraries offer good support for updating feeds that have a single datastream or updating all feeds in a datastream, but seem to offer limited support for updating an arbitrary datastream on its own. This can make life difficult when your data is coming in from sensors as individual messages. [mqtt2pachube] allows you to choose what mqtt subscriptions to make and then match incoming messages by their topics to a pachube feed and datastream id. At the moment it is still experimental, but seems to work. It has highlighted a shortcoming in the mosquitto client library, so requires version 0.15.90 (ie. the in-progress work for the next release). There is no Windows support for the moment and no binary packages either. If you are interested in giving it a try, you will have to compile it yourself. If you need help, please get in touch. There are two examples of feeds created through mqtt2pachube using data from [test.mosquitto.org] * [test.mosquitto.org details] * [UK energy data - generation source percentage] [pachube]: http://pachube.com/ [mqtt2pachube]: http://bitbucket.org/oojah/mqtt2pachube [test.mosquitto.org details]: https://pachube.com/feeds/43810 [UK energy data - generation source percentage]: https://pachube.com/feeds/47080 [test.mosquitto.org]: http://test.mosquitto.org/ mosquitto-2.0.18/www/posts/2012/02/version-0-15-released.md000066400000000000000000000023771450213760600226350ustar00rootroot00000000000000 This is a feature and bugfix release. * Implement "once" and "lazy" bridge start types. * Add support for $SYS/broker/clients/maximum and $SYS/broker/clients/active topics. * Add support for $SYS messages/byte per second received/sent topics. * Updated mosquitto man page - $SYS hierarchy and signal support were out of date. * Auto generated pub/sub client ids now include the hostname. * Tool for dumping persistent DB contents is available in src/db_dump. It isn't installed by default. * Enforce topic length checks in client library. * Add new return type `MOSQ_ERR_ERRNO` to indicate that the errno variable should be checked for the real error code. * Add support for `connection_messages` config option. * mosquitto_sub will now refuse to run if the -c option (disable clean session) is given and no client id is provided. * mosquitto_pub now gives more useful error messages on invalid input or other error conditions. * Fix Python `will_set()` true/True typo. * Fix messages to topic `a/b` incorrectly matching on a subscription `a` if another subscription `a/#` exists. mosquitto-2.0.18/www/posts/2012/03/000077500000000000000000000000001450213760600164545ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/03/quick-start-guide-for-mqtt-with-pachube.md000066400000000000000000000114521450213760600264700ustar00rootroot00000000000000 Pachube (now Cosm) has recently announced beta support for publishing and receiving data to their service using MQTT. This is great news and something I know that a lot of people have been hoping for. Well done Pachube! Their documentation is at and provides enough information to get going if you're already familiar with MQTT. If you aren't familiar with MQTT, here's a few examples of how you can use the new service. First off, I'm going to use the command line MQTT clients I've created to publish and receive data. You can get these clients as part of the [mosquitto download].   # Command Line Examples ## Publishing Data ``` mosquitto_pub -h api.xively.com -u <your xively api-key> -t /v2/feeds/504.csv -m "0,29" ``` In this example we're connecting to host api.xively.com, using our xively api-key as the username, publishing to feed /v2/feeds/504 using the csv format and are updating datastream 0 with the value 29. Another way to achieve the same thing would be to do: ``` mosquitto_pub -h api.xively.com -u <your xively api-key> -t /v2/feeds/504/datastreams/0.csv -m 29 ``` mosquitto_pub can read data from stdin and publish it, so on Unix type systems the following arrangement is possible: ``` sensor_read | mosquitto_pub -h api.xively.com -u <api-key> -t /v2/feeds/504/datastreams/0.csv -l ``` The `-l` option reads messages from stdin, sending a separate message for each line. This means that our imaginary executable sensor_read that is reading data from a sensor must be printing each reading as a text line. ## Retrieving Data In the MQTT world, retrieving data is done through subscriptions: ``` mosquitto_sub -h api.xively.com -u <api-key> -t /v2/feeds/504/datastreams/0.csv ``` In this example, mosquitto_sub will print a text line containing the csv data for datastream 0 of feed 504 every time it is updated. ## Last Will and Testament The last will and testament or just "will" is a very nice feature of MQTT. When your client connects to the MQTT broker/server, it can give the broker this will, which consists of a topic and a message. If the client is disconnected from the broker unexpectedly, that is to say without sending a disconnect message, then the broker publishes the will message on the will topic. This provides a very simple mechanism for client connection monitoring. When your client connects it could publish a message "1" to a topic. If it also set a will to send a message "0" to the same topic on unexpected disconnect, then it would be possible to determine whether that client was connected by monitoring the topic. In the context of Xively, the same approach is possible, but using a trigger to indicate that the client had disconnected. The mosquitto_sub client provides support for wills as shown in the example below: ``` mosquitto_sub -h api.xively.com -u <api-key> -t /v2/feeds/504/datastreams/0.csv --will-topic /v2/feeds/12345/datastreams/0.csv --will-payload "0" ``` In this example, the Xively broker would publish the value "0" to datastream 0 of feed 12345  if mosquitto_sub disconnects unexpectedly. This isn't the most useful example because of the limitations of what mosquitto_sub provides. # Writing Your Own Clients In practice, to get the full benefit of the advantages that MQTT provides you will probably want to write your own MQTT client to connect to Xively for your specific application. The page lists client implementations for lots of different programming languages including the mosquitto client libraries in C/C++, libraries in Java, Python and also device specific implementations for Arduino and other low power devices. # MQTT Beyond Xively The Xively offering is a slightly restricted MQTT offering. "Full" MQTT offers a bit more scope for doing fun things using topic wildcards for example, something that wouldn't really make sense for Xively. There is an overview of MQTT at [mqtt man page] and examples of some applications at . If you'd like to play on an MQTT broker, try looking at [test.mosquitto.org]. If you want some help there are mailing lists and irc channels listed on . [mosquitto download]: /download [mqtt man page]: /man/mqtt-7.html [test.mosquitto.org]: http://test.mosquitto.org/ mosquitto-2.0.18/www/posts/2012/03/upcoming-incompatible-library-changes.md000066400000000000000000000010321450213760600263270ustar00rootroot00000000000000 Version 0.16 of the mosquitto client libraries will have some binary incompatible changes to their APIs. This means that it is a good time to make other changes that are incompatible. If you think any part of the interface (see ) is crazy or could be improved in any way, please get in touch or add a comment below. mosquitto-2.0.18/www/posts/2012/05/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/05/python-client-module-available-for-testing.md000066400000000000000000000027631450213760600272450ustar00rootroot00000000000000 As part of the ongoing work on mosquitto 0.16, the libmosquitto C client library has been ported to Python. It provides complete MQTTv3.1 support and will eventually remove the need for the current Python wrapper around the C library and will allow it to be used more easily and in more situations. The interface is largely the same as the existing Python wrapper. The differences are that it uses the current development interface which differs slightly from that in 0.15 (see the [Python documentation]), not all of the new interface is implemented - there is no threading support and finally some datatypes may be more Python like (e.g. lists in `on_subscribe()` callback rather than an integer). The conversion from ~4000 lines C to ~1000 lines Python took just two evenings and is now ready for testing. It is available in the 0.16 branch in the [bitbucket repository], or as a single file at Please give it a try and report any bugs you find using any of the methods on the [Support page]. Please note that the new Python module does not currently support Python 3. [Python documentation]: /documentation/python [bitbucket repository]: https://bitbucket.org/oojah/mosquitto/src/b9e04ef2a762/lib/python/mosquitto.py [Support page]: /support mosquitto-2.0.18/www/posts/2012/06/000077500000000000000000000000001450213760600164575ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/06/ipv6-on-test-server.md000066400000000000000000000007531450213760600225650ustar00rootroot00000000000000 The public Mosquitto test server, [test.mosquitto.org] has supported IPv6 since it was originally put online but the required DNS record was missing. This has now been fixed so once the record has propagated across the internet you should be able to test your IPv6 clients. [test.mosquitto.org]: http://test.msoquitto.org/ mosquitto-2.0.18/www/posts/2012/06/ssl-support-on-test-server.md000066400000000000000000000027421450213760600242140ustar00rootroot00000000000000 The next version of Mosquitto will provide SSL support for network encryption and authentication. This work is still in development, but is sufficiently advanced to make available for testing on [test.mosquitto.org]. In addition to the existing unencrypted access via port 1883, connections are now possible on ports 8883 and 8884. Port 8883 provides simple encrypted access. Your client should verify the server certificate using the CA certificate available at Port 8884 uses the same server certificate, but requires that your client provide a valid certificate signed by the mosquitto.org CA key. If you wish to obtain a client certificate for testing purposes, please get in touch. The development Python module provides client SSL support. The latest version is available at [mosquitto.py] with a simple example at [ssub.py]. You will need to place the mosquitto.org CA certificate linked above in the same directory. All versions of Python from 2.7 upwards (including Python 3) are supported. Please get in touch if you have any problems. Update: All clients in the development version now support SSL. [test.mosquitto.org]: http://test.mosquitto.org/ [mosquitto.py]: http://test.mosquitto.org/ssl/mosquitto.py [ssub.py]: http://test.mosquitto.org/ssl/ssub.py mosquitto-2.0.18/www/posts/2012/07/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/07/upcoming-release.md000066400000000000000000000040101450213760600222340ustar00rootroot00000000000000 The next release of mosquitto is approaching. There is currently only one feature left on the todo list to complete and I've pencilled in the end of the month as the release date. The date may slip a week or two after that depending on any bugs reported. Despite the development being carried out in the 0.16 branch and the current in-development version numbers being 0.15.90, this will be version 1.0 of mosquitto. There has been significant API changes (now a lot more sane hopefully) which means the client library interface version has been incremented, and the number of changes involved in this release far outreach any previous release, including SSL support, a pure Python client implementation, a healthy start on tests and an associated improvement in protocol compliance, and threaded client support. I think it is well worthy of the version number. I am, however, very keen that this be as bug free a release as possible. To this end, if you're a mosquitto user I'd be very appreciative if you'd download the current source code and give it a try. Maybe read through the documentation and check it makes sense The source for the current version is at either of these links (ignore the "0.16", that is just the branch name): * * If you want to test but with a minimum amount of effort, please download the source, run "make test" and report back any problems. This would be particularly  useful if you are using something other than a Debian/Ubuntu/openSUSE based Linux. If you have any problems, bugs can be reported at , by leaving a comment or by getting in touch directly. I'm interested in anything, but would be especially keen to hear from you if you think something to do with the client API needs changing. Thanks in advance! mosquitto-2.0.18/www/posts/2012/08/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/08/baby.attachments.json000066400000000000000000000010071450213760600226010ustar00rootroot00000000000000{"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"]}}mosquitto-2.0.18/www/posts/2012/08/baby.md000066400000000000000000000005701450213760600177220ustar00rootroot00000000000000 [![baby](/blog/uploads/2012/08/IMAG0006-300x199.jpg)](/blog/uploads/2012/08/IMAG0006.jpg) I've recently become a father, so please don't be offended if I take a while to respond to any mosquitto related queries. mosquitto-2.0.18/www/posts/2012/08/bugfix-coming-soon.md000066400000000000000000000011751450213760600225210ustar00rootroot00000000000000 A few bugs have been identified with the 1.0 release; thanks to everyone who has got in touch about it. They're mostly documentation/build script mistakes (see [ChangeLog.txt]), but there is a Python bug that makes it worthwhile making a quick bugfix release. I intend to make the release this evening (in around 8 hours from this post), so if you have anything you think needs fixing please try and get in touch before then. [ChangeLog.txt]: /ChangeLog.txt mosquitto-2.0.18/www/posts/2012/08/version-1-0-1-released.md000066400000000000000000000026251450213760600227100ustar00rootroot00000000000000 This is a bugfix release. The important changes are fixing the `on_log()` callback in the Python module and the `log_dest` option when running as a Windows service. The rest of the fixes are documentation and build script fixes. Downloads are available on the [download page] and include all supported binaries (except for Ubuntu packages which are still waiting to build due to Launchpad maintenance). The Python module has been uploaded to [Python Package Index]. # Broker * Fix default `log_dest` when running as a Windows service. # Client library   * Fix incorrect parameters in Python `on_log()` callback call. Fixes bug #1036818. # Clients * Clients now don't display TLS/TLS-PSK usage help if they don't support it. # Build scripts * Fix TLS-PSK support in the CMake build files. * Fix man page installation in the CMake build files. * Fix SYSCONFDIR in cmake on \*nix when installing to /usr. Fixes bug #1036908. # Documentation   * Fix mqtt/MQTT capitalisation in man pages. * Update compiling.txt. * Fix incorrect callback docs in mosquitto.py. Fixes bug #1036607. * Fix various doc typos and remove obsolete script. Fixes bug #1037088. [download page]: /download [Python Package Index]: http://pypi.python.org/pypi mosquitto-2.0.18/www/posts/2012/08/version-1-0-2-released.md000066400000000000000000000014741450213760600227120ustar00rootroot00000000000000 This is a bugfix release. # Broker * If the broker was configured for persistence, a durable client had a subscription to topics in $SYS/# and had messages in its queue when the broker restarted, then the persistent database would have messages missing and so the broker would not restart properly. This has been fixed. # Library * Fix threading problem on some systems. # Tests * Close socket after 08-ssl-connect-no-auth-wrong-ca.py test to prevent * subsequent tests having problems. # Build scripts * Install pskfile.example in CMake. Fixes bug #1037504. # Other * Fix db_dump parameter printing message store and sub chunks. mosquitto-2.0.18/www/posts/2012/08/version-1-0-released.md000066400000000000000000000212001450213760600225400ustar00rootroot00000000000000 This is a feature and bugfix release. This is the most significant release for the mosquitto project so far. It encompasses >20% of the total commits for the project and has an increase in source tarball size of 95%, mostly down to the new bundled tests and new man pages. It introduces lots of new features for the broker and improves the API of the client libraries, although this does mean that the libraries are incompatible with previous releases. I apologise for this and hope you'll agree that the changes are worth it. I've been overwhelmed with the amount of feedback that I've received recently, thanks to everyone that has got in touch to let me know where something could be improved. I'd particularly like to thank Nicholas Humfrey for setting me on the continuous integration path. On a slightly different note, my wife was expecting our first child two days ago so it's quite likely I'll be less responsive to support requests for a little while. # Significant changes These are what I think are the exciting changes for this release. * SSL/TLS support across the board - the broker, client libraries and pub/sub clients. This provides certificate based network encryption in a very similar manner to SSL in a web browser where the client verifies that the server is valid. It is also possible to use client certificates to authenticate the clients with the server. * TLS-PSK support (not on Python). This is "pre-shared-key" network encryption and represents a simpler encryption interface than certificate based encryption which makes it much more suitable for embedded/constrained devices. * The Python client library is now written in pure Python so is much easier to use. It supports Python 2.6, 2.7 and 3.\* (no SSL support for 2.6). * All client libraries have had their interface overhauled and should now be much saner and straightforward to use. * The client libraries have thread support. * Passwords files for the broker are stored hashed and salted and a utility for maintaining them has been provided. * It is now possible to write access and authentication plugins for the broker for providing custom support for authentication against e.g. a SQL database. * Implementation of a good test suite which has lead to improved protocol compliance amongst other bug fixes. * Masses of bug fixes. # Downloads Source is available on the [download page], the binary packages will follow as soon as possible. Windows and Ubuntu packages are currently available, more to follow. # Changes The complete list of changes is below: # The broker * Add SSL/TLS support. * Add TLS-PSK support, providing a simpler encryption method for constrained devices. * Passwords are now salted+hashed if compiled with WITH_TLS (recommended). * Add mosquitto_passwd for handling password files. * Add $SYS/broker/publish/messages/{sent|received} to show the number of PUBLISH messages sent/received. * Add $SYS/broker/publish/bytes/{sent|received} to show the number of PUBLISH bytes sent/received. * Add reload parameter for security init/cleanup functions. * Add option for expiring disconnected persistent clients. * Add option for queueing of QoS 0 messages when persistent clients are disconnected. * Enforce client id limits in the broker (only when WITH_STRICT_PROTOCOL is defined). * Fix reloading of log configuration. * Add support for `try_private` config option for bridge connections. * Add support for `autosave_on_changes` config option. * Add support for `include_dir` config option. * Add support for topic remapping. * Usernames were being lost when a non clean-session client reconnected, potentially causing problems with ACLs. This has been fixed. * Significant improvement to memory handling on Windows. * Bridges with outgoing topics will now set the retain flag correctly so that messages will be retained on the remote broker. * Incoming bridge connections are now detected by checking if bit 8 of the protocol version number is set. This requires support from the remote broker. * Add support for `notification_topic` option. * Add $SYS/broker/subscriptions/count and $SYS/broker/retained messages/count. * Add `restart_timeout` to control the amount of time an automatic bridge will wait before reconnecting. * Overlapping subscriptions are now handled properly. Fixes bug #928538. * Fix reloading of `persistence_file` and `persistence_location`. * Fix broker crash on incorrect protocol number. * Fix missing COMPAT_ECONNRESET define on Windows. * Clients that had disconnected were not always being detected immediately on Linux. This has been fixed. * Don't save $SYS messages to the on-disk persistent db. All $SYS messages should be reconstructed on a restart. This means bridge connection notifications will now be correct on a restart. * Fix reloading of bridge clients from the persistent db. This means that outgoing bridged topics should always work. * Local bridges are now no longer restricted by local ACLs. * Discard publish messages with zero length topics. * Drop to "mosquitto" user even if no config file specified. * Don't incorrectly allow topic access if ACL patterns but no normal ACL rules are defined. ## The client libraries * Add SSL/TLS support. * Add TLS-PSK support, providing a simpler encryption method for constrained devices. * Add javascript/websockets client library. * Add `struct mosquitto *mosq` parameter for all callbacks in the client library. This is a binary incompatible change so the soversion of the libraries has been incremented. The new parameter should make it easier to use callbacks in practice. * Add `mosquitto_want_write()` for use when using own select() loop with `mosquitto_socket()`. * Add `mosquitto_connect_async()` to provide a non-blocking connect client call. * Add `mosquitto_user_data_set()` to allow user data pointer to be updated. * Add "int rc" parameter to disconnect callback to indicate whether disconnect was unexpected or the result of calling `mosquitto_disconnect()`. * Add `mosquitto_strerror()` for obtaining a string description of error numbers. * Add `mosquitto_connack_string()` for obtaining a string description of MQTT connection results. * Add `mosquitto_will_clear()` and change `mosquitto_will_set()` to only set the will. * Add `mosquitto_sub_topic_tokenise()` and `mosquitto_sub_topic_tokens_free()` utility functions to tokenise a subscription/topic string into a string array. * Add `mosquitto_topic_matches_sub()` to check whether a topic matches a subscription. * Replaced `mosquitto_log_init()` with `mosquitto_log_callback_set()` to allow clients to decide what to do with log messages. * Client will now disconnect itself from the broker if it doesn't receive a PINGRESP in the keepalive period after sending a PINGREQ. * Client will now send a PINGREQ if it has not received a message from the broker in keepalive seconds. * `mosquitto_new()` will now generate a random client id if the id parameter is NULL. * Added `max_packets` to `mosquitto_loop()`, `mosquitto_loop_read()` and `mosquitto_loop_write()` to control the maximum number of packets that are handled per call. * Payload parameters are now void * instead of uint8\_t \*. * The `clean_session` parameter has been moved from `mosquitto_connect()` to `mosquitto_new()` because it is a client parameter rather than a connection parameter. * Functions now use int instead of uint\*\_t where possible. * `mosquitto_new()` now sets errno to indicate failure type. * Return `MOSQ_ERR_INVAL` on zero length topic. * Fix automatic client id generation on Windows. * `mosquitto_loop_misq()` can now return `MOSQ_ERR_NO_CONN`. * Compile static library as well as dynamic library with default makefiles. * Rename C++ namespace from mosquittopp to mosqpp to remove ambiguity. * C++ `lib_init()`, `lib_version()` and `lib_cleanup()` are now in the mosqpp namespace directly, not mosquittopp class members. * The Python library is now written in pure Python and so no longer depends on libmosquitto. * The Python library includes SSL/TLS support. * The Python library should now be compatible with Python 3. ## Other * Fix db_dump reading of retained messages. * Add example of logging all messages to mysql. * Add C++ client example. * Fix potential buffer overflow in pub/sub clients. * Add "make binary" target that doesn't make documents. * Add `--help` arguments to pub/sub clients. * Fix building on Solaris. [download page]: /download mosquitto-2.0.18/www/posts/2012/09/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/09/updating-password-files.md000066400000000000000000000010051450213760600235530ustar00rootroot00000000000000 Mosquitto 1.0 introduced the use of password files with hashed passwords but had no way to convert from the old plain text password files. This feature will be available in version 1.1 but if it is important to you then you can already get the updated code for the mosquitto_passwd utility at mosquitto-2.0.18/www/posts/2012/09/version-1-0-3-released.md000066400000000000000000000023471450213760600227140ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix loading of psk files. * Don't return an error when reloading config if an ACL file isn't defined.  This was preventing psk files being reloaded. * Clarify meaning of $SYS/broker/clients/total in mosquitto(8) man page. * Clarify meaning of $SYS/broker/messages/stored in mosquitto(8) man page. * Fix non-retained message delivery when subscribing to #. * Fix retained message delivery for subs to foo/# with retained messages at foo. * Include the filename in password/acl file loading errors. # Library * Fix possible AttributeError when `self._sock == None` in Python module. * Fix reconnecting after a timeout in Python module. * Fix reconnecting when there were outgoing packets in the queue in the Python module. * Fix problem with mutex initialisation causing crashes on some Windows installations. Source is available on the [download page], the binary packages for Windows are available now and Linux builds will be available as soon as the various build servers complete their tasks. [download page]: /download mosquitto-2.0.18/www/posts/2012/10/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/10/version-1-0-4-released.md000066400000000000000000000011731450213760600227010ustar00rootroot00000000000000 This is a bugfix release. # Broker * Deal with poll() POLLIN/POLLOUT before POLL[RD]HUP to correctly handle the case where a client sends data and immediately closes its socket. # Library * Fix memory leak with messages of QoS=2. Fixes bug #1064981. * Fix potential thread synchronisation problem with outgoing packets in the Python module. Fixes bug #1064977. # Clients * Fix `mosquitto_sub -l` incorrectly only sending one message per second. mosquitto-2.0.18/www/posts/2012/11/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/11/making-mosquitto-packages-for-debian-yourself.md000066400000000000000000000025601450213760600277360ustar00rootroot00000000000000 As Debian has been in feature freeze since before Mosquitto 1.0 was released, it will be a long time until there is an updated version of Mosquitto in Debian. It is, however, fairly straightforward to do the packaging yourself. Here's how to do that from the command line. Download and unpack the mosquitto source tarball: ``` wget http://mosquitto.org/files/source/mosquitto-1.1.2.tar.gz tar -zxf mosquitto-1.1.2.tar.gz ``` Rename the tarball to match Debian requirements: ``` mv mosquitto-1.1.2.tar.gz mosquitto_1.1.2.orig.tar.gz ``` The current mosquitto packaging files are available at - you want the .debian.tar.xz. The next step is to build the package, but you may find that you need to install some packages first: ``` sudo apt-get install build-essential python quilt libwrap0-dev libssl-dev devscripts python-setuptools ``` To build the packages do ``` cd mosquitto-1.1.2/ debuild ``` You should now have a list of .deb files in the parent directory which you can install with: ``` sudo dpkg -i <deb file> ``` Please leave comments if you find this useful or have any problems. mosquitto-2.0.18/www/posts/2012/11/version-1-0-5-released.md000066400000000000000000000011051450213760600226760ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix crash when the broker has `use_identity_as_username` set to true but a client connects without a certificate. * mosquitto_passwd should only be installed if `WITH_TLS=yes`. # Library * Use symbolic errno values rather than numbers in Python module to avoid cross platform issues (incorrect errno on Mac OS). # Other * Build script fixes for FreeBSD. mosquitto-2.0.18/www/posts/2012/12/000077500000000000000000000000001450213760600164545ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2012/12/libmosquitto-go-bindings.md000066400000000000000000000007661450213760600237400ustar00rootroot00000000000000 I just discovered that Shane Hanna has written a Go language binding for libmosquitto available at . Good work Shane! Note that the readme file states: > Doesn't expose all of libmosquitto, just what I've needed so far. so you shouldn't necessarily expect everything to work. mosquitto-2.0.18/www/posts/2012/12/version-1-1-released.md000066400000000000000000000071501450213760600225440ustar00rootroot00000000000000 This is a feature and bugfix release. # Broker * Add $SYS/broker/messages/dropped * Add $SYS/broker/clients/expired * Replace $SYS/broker/+/per second/+ with moving average versions published at $SYS/broker/load/# * Add $SYS/broker/load/sockets/+ and $SYS/broker/load/connections/+ * Documentation on password file format has been fixed. * Disable SSL compression. This reduces memory usage significantly and removes the possibility of CRIME type attacks. * Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further. * Add allow_duplicate_messages option. * ACL files can now have comment lines with # as the first character. * Display message on startup about which config is being loaded. * Fix `max_inflight_messages` and `max_queued_messages` not being applied. * Fix documentation error in mosquitto.conf. * Ensure that QoS 2 queued messages are sent out in a timely manner. * Local bridges now act on `clean_session` correctly. * Local bridges with `clean_session==false` now remove unused subscriptions on broker restart. * The $SYS/broker/heap/# messages now no longer include "bytes" as part of the string for ease of use. # Client library * Free memory used by OpenSSL in `mosquitto_lib_cleanup()` where possible. * Change WebSocket subprotocol name to mqttv3.1 to make future changes easier and for compatibility with other implementations. * `mosquitto_loop_read()` and `mosquitto_loop_write()` now handle errors themselves rather than having `mosquitto_loop()` handle their errors. This makes using them in a separate event loop more straightforward. * Add `mosquitto_loop_forever()` / `loop_forever()` function call to make simple clients easier. * Disable SSL compression. This reduces memory usage significantly and removes the possibility of CRIME type attacks. * Enable SSL_MODE_RELEASE_BUFFERS mode to reduce SSL memory usage further. * `mosquitto_tls_set()` will now return an error or raise an exception immediately if the CA certificate or client certificate/key cannot be accessed. * Fix potential memory leaks on connection failures. * Don't produce return error from `mosquitto_loop()` if a system call is interrupted. This prevents disconnects/reconnects in threaded mode and simplifies non-threaded client handling. * Ignore SIGPIPE to prevent unnecessary client quits in threaded mode. * Fix document error for `mosquitto_message_retry_set()`. * Fix `mosquitto_topic_matches_sub()` for subscriptions with + as the final character. Fixes bug #1085797. * Rename all "obj" parameters to "userdata" for consistency with other libraries. * Reset errno before network read/write to ensure EAGAIN isn't mistakenly returned. * The message queue length is now tracked and used to determine the maximum number of packets to process at once. This removes the need for the `max_packets` parameter which is now unused. * Fix incorrect error value in Python `error_string()` function. Fixes bug #1086777. * Reset last message in/out timer in Python module when we send a PINGREQ. Fixes too-early disconnects. # Clients * Clients now display their own version number and library version number in their help messages. * Fix `mosquitto_pub -l -q 2` disconnecting before all messages were transmitted. * Fix potential out-of-bounds array access with client ids. Fixes bug #1083182. # Other * mosquitto_passwd can now convert password files with plain text files to hashed versions. mosquitto-2.0.18/www/posts/2013/000077500000000000000000000000001450213760600162335ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/01/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/01/mosquitto-debian-repository.md000066400000000000000000000030011450213760600244700ustar00rootroot00000000000000 On a previous post I described [how to make mosquitto debian packages]. This turned out to be a bit problematic, so I've now put up an experimental debian repository for mosquitto. It includes packages for the i386, amd64, armel and raspberry pi (raspbian armhf ) architectures. It's worth repeating that this is experimental - there are package changes that haven't been vetted by a Debian developer so it's possible something will break. I've tested myself and had no problems so far. To use the new repository you should first import the repository package signing key: ``` wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key sudo apt-key add mosquitto-repo.gpg.key ``` Then make the repository available to apt: ``` cd /etc/apt/sources.list.d/ ``` Then one of the following, depending on which version of debian you are using: ``` sudo wget http://repo.mosquitto.org/debian/mosquitto-jessie.list sudo wget http://repo.mosquitto.org/debian/mosquitto-stretch.list sudo wget http://repo.mosquitto.org/debian/mosquitto-buster.list ``` Then update apt information: ``` apt-get update ``` And discover what mosquitto packages are available: ``` apt-cache search mosquitto ``` Or just install: ``` apt-get install mosquitto ``` [how to make mosquitto debian packages]: /blog/2012/11/making-mosquitto-packages-for-debian-yourself/ mosquitto-2.0.18/www/posts/2013/01/version-1-1-1-released.md000066400000000000000000000006011450213760600226730ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix crash on reload if using acl patterns. # Client library * Fix static C++ functions not being exported on Windows. Fixes bug #1098256. Binaries should be available shortly. mosquitto-2.0.18/www/posts/2013/01/version-1-1-2-released.md000066400000000000000000000010101450213760600226670ustar00rootroot00000000000000 This is a bugfix release. # Client library * Fix `tls_cert_reqs` not being set to `SSL_VERIFY_PEER` by default. This meant that clients were not verifying the server certificate when connecting over TLS. This affects the C, C++ and Python libraries. Source and binaries are available on the [download page]. [download page]: /download mosquitto-2.0.18/www/posts/2013/02/000077500000000000000000000000001450213760600164545ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/02/mqtt-standardisation-oasis-call-for-participation.md000066400000000000000000000013761450213760600306340ustar00rootroot00000000000000 The MQTT protocol is going for standardisation at OASIS. A technical committee is being formed and there is a call for participation for interested parties. There are details at the link below: The plan seems to be to take the 3.1 spec as it is for standardisation and see about changes in the future. If you are interested in taking part see the link above, but note that you need to be a paid up member of OASIS. mosquitto-2.0.18/www/posts/2013/02/version-1-1-3-released.md000066400000000000000000000010111450213760600226720ustar00rootroot00000000000000 This is a minor bugfix release that addresses some problems identified during Debian packaging. # Broker * mosquitto_passwd utility now uses tmpfile() to generate its temporary data storage file. It also creates a backup file that can be used to recover data if an errors occur.

Other

* Build script fixes to help packaging on Debian. mosquitto-2.0.18/www/posts/2013/04/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/04/some-interesting-mqtt-things.md000066400000000000000000000015761450213760600245620ustar00rootroot00000000000000 It's been a while since there has been an update here, so in lieu of one here are some interesting links I've come across recently. Add a comment to the post if you've done something cool not mentioned here! Work progresses on mosquitto 1.2. Initial release of an MQTT-S gateway, written in ruby: * And some MQTT-S tools: * A Pinoccio/MQTT/sensor powered Theramin: * Voice controlled MQTT LED: * An MQTT notification plugin for Jenkins/Hudson: * mosquitto-2.0.18/www/posts/2013/05/000077500000000000000000000000001450213760600164575ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/05/mosquitto-javascript-client-deprecated.md000066400000000000000000000013621450213760600265650ustar00rootroot00000000000000 The [Paho] project recently made a new Javascript client available: The mosquitto Javascript client, mosquitto.js, is neither as functional nor as well written as the Paho client, so is being deprecated. If you are using mosquitto.js I strongly recommend that you look to the Paho client for the future. I will be carrying out minor bug fixes but no other development will take place. There are no plans to remove the existing files. [Paho]: http://www.eclipse.org/paho/ mosquitto-2.0.18/www/posts/2013/07/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/07/authentication-plugins.md000066400000000000000000000017261450213760600235070ustar00rootroot00000000000000 There has been some interest in authentication plugins for mosquitto recently. Some examples have appeared: Authentication based on md5 hashes: [mosquitto_auth_plugin_md5] Authentication based on md5 hashed passwords in postgresql: [mosquitto_auth_plugin_pg_md5] Authentication and topic ACL with redis and a PBKDF2 hash: [mosquitto-redis-auth] I particularly like the redis based plugin for the interesting additions like the "superuser" that is exempt from ACL checks. If you've written an auth plugin and think it might be useful to others, let me know. [mosquitto_auth_plugin_md5]: https://github.com/sskaje/mosquitto_auth_plugin_md5 [mosquitto_auth_plugin_pg_md5]: https://github.com/sebaroesch/mosquitto_auth_plugin_pg_md5 [mosquitto-redis-auth]: https://github.com/jpmens/mosquitto-redis-auth mosquitto-2.0.18/www/posts/2013/07/version-1-2-near-complete.md000066400000000000000000000017261450213760600235240ustar00rootroot00000000000000 With the most recent commit, "Implement TLSv1.2 and TLSv1.1 support," everything that is planned for version 1.2 has been completed. If you haven't tried it out yet, now would be a good time to take a look. Before the release is finalised, there still needs to be more testing done, particularly on Windows. If you use another platform than Windows or Linux, I'd be interested to hear if you have any problems with the 1.2 code. I will also be updating the packaging for all of the binaries that I build or contribute to directly, so there is still time for bug reports. You can get a copy of the source at one of the links below, or through the mercurial repository directly on the 1.2 branch. * * mosquitto-2.0.18/www/posts/2013/08/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/08/mosquitto-on-fedora.md000066400000000000000000000004731450213760600227240ustar00rootroot00000000000000 Mosquitto has been packaged for Fedora thanks to Rich Mattes. Fedora 19 users will be able to install with "yum install mosquitto". Thanks Rich! mosquitto-2.0.18/www/posts/2013/08/mqtt-watchdir.md000066400000000000000000000013551450213760600216000ustar00rootroot00000000000000 Recursively watch a directory for modifications and publish file content to an MQTT broker `mqtt-watchdir` is a Python program by [Jan-Piet Mens] to watch a directory and publish new or modified files in that directory hierarchy to an MQTT broker, using a matching topic. Source and instructions are available at and it is also available via pypi. It is a similar idea to my [mqttfs] fuse filesystem, but ultimately implemented in a better (and portable) manner. [Jan-Piet Mens]: https://twitter.com/jpmens [mqttfs]: https://bitbucket.org/oojah/mqttfs mosquitto-2.0.18/www/posts/2013/08/version-1-2-released.md000066400000000000000000000137471450213760600225640ustar00rootroot00000000000000 This is a (long overdue) feature release. There is a potential gotcha when upgrading to this release because the default version of TLS used has changed from 1.0 to 1.2. Python does not yet have support for TLS>1.0 so Python clients will be unable to communicate with brokers using the default TLS settings. The source is available at the [download page] and binaries will become available in the near future. # Broker * Replace O(n) username lookup on CONNECT with a roughly O(1) hashtable version. * It is now possible to disable $SYS at compile time. * Add dropped publish messages to load tree in $SYS. Closes bug #1183318. * Add support for logging SUBSCRIBE/UNSUBSCRIBE events. * Add `log_dest file` logging support. * Auth plugin ACL check function now passes the client id as well as username and password. * The `queue_qos0_messages` option wasn't working correctly, this has now been fixed. Closes bug #1125200. * Don't drop all messages for disconnected durable clients when `max_queued_messages=0`. * Add support for `log_type all`. * Add support for `-v` option on the command line to provide the equivalent of `log_type all` without needing a config file. * Add the `upgrade_outgoing_qos` option, a non-standard feature. * Persistence data is now written to a temporary file which is atomically renamed on completion, so a crash during writing will not produce a corrupt file. * mosquitto.conf is now installed as mosquitto.conf.example * Configuration file errors are now reported with filename and line number. * The broker now uses a monotonic clock if available, to avoid changes in time causing client disconnections or message retries. * Clean session and keepalive status are now display the log when a client connects. * Add support for TLSv1.2 and TLSv1.1. * Clients that connect with zero length will topics are now rejected. * Add the ability to set a maximum allowed PUBLISH payload size. * Fix an ACL with topic `#` incorrectly granting access to $SYS. * Fix retained messages incorrectly being set on wildcard topics, leading to duplicate retained messages being sent on subscription. Closes bug #1116233. * Don't discard listener values when no "port" option given. Closes bug #1131406. * Client password check was always failing when security was being reapplied after a config reload. This meant that all clients were being disconnected. This has been fixed. * Fix build when `WITH_TLS=no`. Closes bug #1174971. * Fix single outgoing packets not being sent in a timely fashion if they were not sent in one call to write(). Closes bug #1176796. * Fix remapping of messages for clients connected to a listener with `mount_point` set. Closes bug #1180765. * Fix duplicate retained messages being sent for some wildcard patterns. * If a client connects with a will topic to which they do not have write access, they are now disconnected with CONNACK "not authorised". * Fix retained messages on topic foo being incorrectly delivered to subscriptions of /# * Fix handling of SSL errors on SSL_accept(). * Fix handling of QoS 2 messages on client reconnect. * Drop privileges now sets supplementary groups correctly. * Fix load reporting interval (is now 60s). * Be strict with malformed PUBLISH packets - clients are now disconnected rather than the packet discarded. This goes inline with future OASIS spec changes and makes other changes more straightforward. * Process incoming messages denied by ACL properly so that clients don't keep resending them. * Add support for `round_robin` bridge option. * Add bridge support for verifying remote server certificate subject against the remote hostname. * Fix problem with out of order calls to free() when restarting a lazy bridge. * The broker now attempts to resolve `bind_address` and bridge addresses immediately when parsing the config file in order to detect invalid hosts. * Bridges now set their notification state before attempting to connect, so if they fail to connect the state can still be seen. * Fix bridge notification payload length - no need to send a null byte. * mosquitto_passwd utility now reports errors more clearly. * Fix `mosquitto_passwd -U`.   # Client library * Add support for TLSv1.2 and TLSv1.1, except for on the Python module. * Add support for verifying remote server certificate subject against the remote hostname. * Add mosquitto_reconnect_async() support and make asynchronous connections truely asynchronous rather than simply deferred. DNS lookups are still blocking, so asynchronous connections require an IP address instead of hostname. * Allow control of reconnection timeouts in mosquitto_loop_forever() and after mosquitto_loop_start() by using mosquitto_reconnect_delay_set(). * Fix building on Android NDK. * Re-raise unhandled errors in Python so as not to provide confusing error messages later on. * Python module supports IPv6 connections. * mosquitto_sub_topic_tokenise() was behaving incorrectly if the last topic hierarchy had only a single character. This has been fixed. Closes bug #1163348. * Fix possible crash after disconnects when using the threaded interface with TLS. * Allow build/install without Python. Closes bug #1174972. * Add support for binding connection to a local interface. * Implement maximum inflight messages handling. * Fix Python client not handling `will_payload==None`. * Fix potential memory leak when setting username/password. * Fix handling of QoS 2 messages on reconnect. * Improve handling of mosquitto_disconnect() with threaded mode. # Clients * Add support for TLSv1.2 and TLSv1.1. * Sub client can now suppress printing of messages with the retain bit set. * Add support for binding connection to a local interface. * Implement maximum inflight messages handling for the pub client. [download page]: /download mosquitto-2.0.18/www/posts/2013/09/000077500000000000000000000000001450213760600164635ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/09/version-1-2-1-released.md000066400000000000000000000023361450213760600227130ustar00rootroot00000000000000 This is a bugfix release. # Broker: * The broker no longer ignores the `auth_plugin_init()` return value. Closes bug #1215084. * Use `RTLD_GLOBAL` when opening authentication plugins on posix systems. Fixes resolving of symbols in libraries used by authentication plugins. * Add/fix some config documentation. * Fix ACLs for topics with $SYS. * Clients loaded from the persistence file on startup were not being added to the client hash, causing subtle problems when the client reconnected, including ACLs failing. This has been fixed. * Add note to mosquitto-tls man page stating that certificates need to be unique. Closes bug #1221285. * Fix incorrect retained message delivery when using wildcard subs in some circumstances. Fixes bug #1226040. # Client library * Fix support for Python 2.6, 3.0, 3.1. * Fix TLS subjectAltName verification and segfaults. * Handle EAGAIN in Python on Windows. Closes bug #1220004. * Fix compilation when using `WITH_TLS=no`. * Don't fail reconnecting in Python when broker is temporarily unavailable. mosquitto-2.0.18/www/posts/2013/10/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/10/version-1-2-2-released.md000066400000000000000000000013601450213760600227000ustar00rootroot00000000000000 This is a bugfix release: # Broker * Fix compliance with `max_inflight_messages` when a non-clean session client reconnects. Closes one of the issues on bug #1237389. # Client library * Fix incorrect inflight message accounting, which caused messages to go * unsent. Partial fix for bug #1237351. * Fix potential memory corruption when sending QoS>0 messages at a high rate using the threaded interface. Further fix for #1237351. * Fix incorrect delay scaling when exponential_backoff=true in mosquitto_reconnect_delay_set(). * Some pep8 fixes for Python. mosquitto-2.0.18/www/posts/2013/12/000077500000000000000000000000001450213760600164555ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2013/12/paho-mqtt-python-client.md000066400000000000000000000024431450213760600235070ustar00rootroot00000000000000 The Mosquitto Python client was donated to the Eclipse Paho project in June of this year. As mosquitto.py has been very popular, I have been maintaining both code bases together. With the Mosquitto project also moving to Eclipse it is now even more redundant to keep maintaining mosquitto.py so I would like to recommend that everybody currently using mosquitto.py move over to using the Paho Python client. The current state of the Paho client is now available on [pypi] and can be installed using `pip install paho-mqtt`. To port code from mosquitto.py, you should change: ``` import mosquitto mqttc = mosquitto.Mosquitto() ``` to: ``` import paho.mqtt.client as paho mqttc = paho.Client() ``` All error codes e.g. `MOSQ_ERR_SUCCESS` change to `MQTT_ERR_SUCCESS`. The Paho module has a compatibility Mosquitto class that means a very simple (but not recommended for the long term) port can be achieved with the following line, assuming none of the error codes are used: ``` import paho.mqtt.client as mosquitto ``` I will keep applying updates to mosquitto.py until the Paho 1.0 release. [pypi]: https://pypi.python.org/pypi/paho-mqtt mosquitto-2.0.18/www/posts/2013/12/version-1-2-3-released.md000066400000000000000000000024621450213760600227070ustar00rootroot00000000000000 In time for the second day of [Thingmonk], which I regret not being able to go to, version 1.2.3 of mosquitto is released. This is a bugfix release. # All components * Various fixes caught by [Coverity Scan]. # Broker * Don't always attempt to call read() for SSL clients, irrespective of whether they were ready to read or not. Reduces syscalls significantly. * Possible memory leak fixes. * Further fix for bug #1226040: multiple retained messages being delivered for subscriptions ending in #. * Fix bridge reconnections when using multiple bridge addresses. # Client library * Fix possible memory leak in C/C++ library when communicating with a broker that doesn't follow the spec. * Block in Python `loop_stop()` until all messages are sent, as the documentation states should happen. * Fix for asynchronous connections on Windows. Closes bug #1249202. * Module version is now available in mosquitto.py. # Clients * mosquitto_sub now uses fwrite() instead of printf() to output messages, so messages with NULL characters aren't truncated. [Thingmonk]: http://redmonk.com/thingmonk/ [Coverity Scan]: https://scan.coverity.com/ mosquitto-2.0.18/www/posts/2014/000077500000000000000000000000001450213760600162345ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2014/03/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2014/03/version-1-3-1-released.md000066400000000000000000000017171450213760600227110ustar00rootroot00000000000000 This is a bugfix release: # Broker * Prevent possible crash on client reconnect. Closes bug #1294108. * Don't accept zero length unsubscription strings (MQTT v3.1.1 fix) * Don't accept QoS 3 (MQTT v3.1.1 fix) * Don't disconnect clients immediately on HUP to give chance for all data to be read. * Reject invalid un/subscriptions e.g. `foo/+bar` `#/bar`. * Take more care not to disconnect clients that are sending large messages. # Client library * Fix socketpair code on the Mac. * Fix compilation for `WITH_THREADING=no`. * Break out of select() when calling `mosquitto_loop_stop()`. * Reject invalid un/subscriptions e.g. `foo/+bar` `#/bar`. # Clients * Fix keepalive value on mosquitto_pub. * Fix possibility of mosquitto_pub not exiting after sending messages when using -l. mosquitto-2.0.18/www/posts/2014/03/version-1-3-released.md000066400000000000000000000062351450213760600225530ustar00rootroot00000000000000 # Broker * The broker no longer ignores the `auth_plugin_init()` return value. * Accept SSLv2/SSLv3 HELLOs when using TLSv1, whilst keeping SSLv2 and SSLv3 disabled. This increases client compatibility without sacrificing security. * The $SYS tree can now be disabled at runtime as well as at compile time. * When remapping bridged topics, only check for matches when the message direction is correct. This allows two identical topics to be remapped differently for both in and out. * Change `$SYS/broker/heap/current size` to `$SYS/broker/heap/current` for easier parsing. * Change `$SYS/broker/heap/maximum size` to `$SYS/broker/heap/maximum` for easier parsing. * Topics are no longer normalised from e.g `a///topic` to `a/topic`. This matches the behaviour as clarified by the Oasis MQTT spec. This will lead to unexpected behaviour if you were using topics of this form. * Log when outgoing messages for a client begin to drop off the end of the queue. * Bridge clients are recognised as bridges even after reloading from persistence. * Basic support for MQTT v3.1.1. This does not include being able to bridge to an MQTT v3.1.1 broker. * Username is displayed in log if present when a client connects. * Support for 0 length client ids (v3.1.1 only) that result in automatically generated client ids on the broker (see option `allow_zero_length_clientid`). * Ability to set the prefix of automatically generated client ids (see option `auto_id_prefix`). * Add support for TLS session resumption. * When using TLS, the server now chooses the cipher to use when negotiating with the client. * Weak TLS ciphers are now disabled by default. # Client library * Fix support for Python 2.6, 3.0, 3.1. * Add support for un/subscribing to multiple topics at once in un/subscribe(). * Clients now close their socket after sending DISCONNECT. * Python client now contains its version number. * C library `mosquitto_want_write()` now supports TLS clients. * Fix possible memory leak in C/C++ library when communicating with a broker that doesn't follow the spec. * Return strerror() through `mosquitto_strerror()` to make error printing easier. * Topics are no longer normalised from e.g `a///topic` to `a/topic`. This matches the behaviour as clarified by the Oasis MQTT spec. This will lead to unexpected behaviour if you were using topics of this form. * Add support for SRV lookups. * Break out of select() on publish(), subscribe() etc. when using the threaded interface. Fixes bug #1270062. * Handle incoming and outgoing messages separately. Fixes bug #1263172. * Don't terminate threads on `mosquitto_destroy()` when a client is not using the threaded interface but does use their own thread. Fixes bug #1291473. # Clients * Add `--ciphers` to allow specifying which TLS ciphers to support. * Add support for SRV lookups. * Add `-N` to sub client to suppress printing of EOL after the payload. * Add `-T` to sub client to suppress printing of a topic hierarchy. mosquitto-2.0.18/www/posts/2014/05/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2014/05/new-arrival.attachments.json000066400000000000000000000007241450213760600241170ustar00rootroot00000000000000{"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"]}}mosquitto-2.0.18/www/posts/2014/05/new-arrival.md000066400000000000000000000010331450213760600212260ustar00rootroot00000000000000 I'm pleased to say that I'm a new father again. My 7lb 12 (3.57kg) boy arrived today and is quite happy, as is his mother. Apologies to anybody who has emailed me recently and I've not yet replied - this is the main reason! [![baby][baby]](/blog/uploads/2014/05/14098345978_c15d12f19a_z.jpg) [baby]:/blog/uploads/2014/05/14098345978_c15d12f19a_z-300x200.jpg mosquitto-2.0.18/www/posts/2014/07/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2014/07/version-1-3-2-released.md000066400000000000000000000041311450213760600227070ustar00rootroot00000000000000 This is a security and bugfix release. # Security A bug in the way that mosquitto handles authentication plugins has been identified. When using a plugin for authentication purposes, if the plugin returns `MOSQ_ERR_UNKNOWN` when making an authentication check, as might happen if a database was unavailable for example, then mosquitto incorrectly treats this as a successful authentication. This has the potential for unauthorised clients to access the running mosquitto broker and gain access to information to which they are not authorised. This is an important update for users of authentication plugins in mosquitto. # Broker * Don't allow access to clients when authenticating if a security plugin returns an application error. Fixes bug [#1340782]. * Ensure that bridges verify certificates by default when using TLS. * Fix possible crash when using pattern ACLs that do not include a %u and clients that connect without a username. * Fix subscriptions being deleted when clients subscribed to a topic beginning with a $ but that is not $SYS. * When a durable client reconnects, its queued messages are now checked against ACLs in case of a change in username/ACL state since it last connected. * Anonymous clients are no longer accidentally disconnected from the broker after a SIGHUP. * Fix bug [#1324411], which could have had unexpected consequences for delayed messages in rare circumstances. # Client library * Fix topic matching edge case. * Fix callback deadlocks after calling `mosquitto_disconnect()`, when using the threaded interfaces. Closes bug [#1313725]. * Fix SRV support when building with CMake. # General * Use $(STRIP) for stripping binaries when installing, to allow easier cross compilation. [#1313725]: https://bugs.launchpad.net/mosquitto/+bug/1313725 [#1324411]: https://bugs.launchpad.net/mosquitto/+bug/1324411 [#1340782]: https://bugs.launchpad.net/mosquitto/+bug/1340782 mosquitto-2.0.18/www/posts/2014/08/000077500000000000000000000000001450213760600164635ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2014/08/version-1-3-3-released.md000066400000000000000000000004661450213760600227200ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix incorrect handling of anonymous bridges on the local broker. Binaries will follow shortly. mosquitto-2.0.18/www/posts/2014/08/version-1-3-4-released.md000066400000000000000000000011351450213760600227130ustar00rootroot00000000000000 This is a bugfix release. The reason for the rapid release of the past two versions is down to a Debian developer reviewing the mosquitto package. This is a good opportunity to ensure that as bug free a version as possible is present in Debian. # Broker * Don't ask client for certificate when `require_certificate` is **false**. * Backout incomplete functionality that was incorrectly included in 1.3.2. Binaries will follow shortly. mosquitto-2.0.18/www/posts/2014/10/000077500000000000000000000000001450213760600164545ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2014/10/mosquitto-and-poodle.md000066400000000000000000000006141450213760600230630ustar00rootroot00000000000000 Details of the POODLE attack that targets SSLv3 have been released recently. Mosquitto has never provided support for SSLv3 (or SSLv2) so should not be vulnerable to this attack and does not require any configuration changes. mosquitto-2.0.18/www/posts/2014/10/unintended-change-of-behaviour-in-1-3-4.md000066400000000000000000000017721450213760600260140ustar00rootroot00000000000000 Version 1.3.4 introduced the change that when using TLS with `require_certificate` set to false, the client is no longer asked for a client certificate. This seemed to be causing problems in some situations, particularly with embedded devices. If `use_identity_as_username` is set to true when `require_certificate` is set to false, then the client will not be asked for a certificate, even if it has one configured. This means that the client will be refused access with connack code 4, "bad username or password", because if `use_identity_as_username` currently requires that a certificate is present, even if `allow_anonymous` is set to true. This change may cause unexpected results, but does not represent a security flaw because the change results in more clients being rejected than would otherwise have been. mosquitto-2.0.18/www/posts/2014/10/version-1-3-5-released.md000066400000000000000000000015371450213760600227130ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix possible memory leak when using a topic that has a leading slash. Fixes bug #1360985. * Fix saving persistent database on Windows. * Temporarily disable ACL checks on subscriptions when using MQTT v3.1.1. This is due to the complexity of checking wildcard ACLs against wildcard subscriptions. This does not have a negative impact on security because checks are still made before a message is sent to a client. Fixes bug #1374291. * When using -v and the broker receives a SIGHUP, verbose logging was being disabled. This has been fixed. # Client library * Fix mutex being incorrectly passed by value. Fixes bug #1373785. mosquitto-2.0.18/www/posts/2015/000077500000000000000000000000001450213760600162355ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/01/000077500000000000000000000000001450213760600164555ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/01/seeking-sponsorship.md000066400000000000000000000036531450213760600230200ustar00rootroot00000000000000 The mosquitto project has, or can get, access to a wide variety of different systems to help with development. One important platform for which this is not true is Mac OS X. There are sufficient differences between Macs and other systems that this makes life difficult. To this end, I would like to reach out to the mosquitto community to ask for help with obtaining either * A remote login on a Mac system * Donation of hardware * Donation of money to buy some hardware I have been offered a remote account by a few individuals in the past, for which I'm very grateful, but only on a short term basis and, understandably, with limited control. Something on a longer term, with the ability to install packages would be much more useful. Unfortunately I realise this is relatively difficult to offer. On the hardware side of things, there isn't a need for a modern, powerful computer. A second hand Mac Mini of Core2Duo vintage with 1GB RAM and a reasonably modern version of Mac OS X would be quite sufficient, and ideal for me in terms of the space it takes up. Regrettably I feel I would have to turn down offers of an old iMac or Mac Pro. 2007-era Mac Minis go on Ebay UK for around £100. I'm hopeful that there is a company out there using mosquitto, likes Macs and for whom £100 would be a drop in the ocean. If so, or any individuals want to help out with a small donation towards this, please get in touch directly to roger@atchoo.org or head over to the downloads page to see the paypal donation link, and thanks very much in advance.
Update: I have now awaiting delivery of a Mac mini. Thanks very much to all of you that have contributed, it is very much appreciated. If you would still like to support mosquitto development please don't let this put you off... mosquitto-2.0.18/www/posts/2015/02/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/02/version-1-4-released.md000066400000000000000000000132301450213760600225450ustar00rootroot00000000000000 This is a feature release and is also the first release of the mosquitto project from the Eclipse Foundation umbrella. The code is now dual licenced under the [EPL]. The EDL and BSD 3 clause license are essentially identical so if you were happy with the BSD license then you should be happy with the EDL. Files distributed will remain in the same place but will in some cases also be available on the Eclipse download servers. # Important changes * Websockets support in the broker. * Bridge behaviour on the local broker has changed due to the introduction of the `local_*` options. This may affect you if you are using authentication  and/or ACLs with bridges. * The default TLS behaviour has changed to accept all of TLS v1.2, v1.1 and v1.0, rather than only one version of the protocol. It is still possible to restrict a listener to a single version of TLS. * The Python client has been removed now that the Eclipse Paho Python client has had a release. * When a durable client reconnects, its queued messages are now checked against ACLs in case of a change in username/ACL state since it last connected. * New `use_username_as_clientid` option on the broker, for preventing hijacking of a client id. * The client library and clients now have experimental SOCKS5 support. * Wildcard TLS certificates are now supported for bridges and clients. * The clients have support for config files with default options. * Client and client libraries have support for MQTT v3.1.1. * Bridge support for MQTT v3.1.1. # Broker * Websockets support in the broker. * Add `local_clientid`, `local_username`, `local_password` for bridge connections to authenticate to the local broker. * Default TLS mode now accepts TLS v1.2, v1.1 and v1.0. * Support for ECDHE-ECDSA family ciphers. * Fix bug #1324411, which could have had unexpected consequences for delayed messages in rare circumstances. * Add support for `session present` in CONNACK messages for MQTT v3.1.1. * Remove strict protocol #ifdefs. * Change $SYS/broker/clients/active -> $SYS/broker/clients/connected * Change $SYS/broker/clients/inactive -> $SYS/broker/clients/disconnected * When a durable client reconnects, its queued messages are now checked against ACLs in case of a change in username/ACL state since it last connected. * libuuid is used to generate client ids, where it is available, when an MQTT v3.1.1 client connects with a zero length client id. * Anonymous clients are no longer accidently disconnected from the broker after a SIGHUP. * mosquitto_passwd now supports `-b` (batch mode) to allow the password to be provided at the command line. * Removed $SYS/broker/changeset. This was intended for use with debugging, but in practice is of no use. * Add support for `use_username_as_clientid` which can be used with authentication to restrict ownership of client ids and hence prevent one client disconnecting another by using the same client id. * When `require_certificate` was false, the broker was incorrectly asking for a certificate (but not checking it). This caused problems with some clients and has been fixed so the broker no longer asks. * When using syslog logging on non-Windows OSs, it is now possible to specify the logging facility to one of local0-7 instead of the default "daemon". * The `bridge_attempt_unsubscribe` option has been added, to allow the sending of UNSUBSCRIBE requests to be disabled for topics with "out" direction. Closes bug #456899. * Wildcard TLS certificates are now supported for bridges. * Support for "hour" client expiration lengths for the `persistent_client_expiration` option. Closes bug #425835. * Bridge support for MQTT v3.1.1. * Root privileges are now dropped after starting listeners and loading certificates/private keys, to allow private keys to have their permissions restricted to the root user only. Closes bug #452914. * Usernames and topics given in ACL files can now include a space. Closes bug #431780. * Fix hang if pattern acl contains a %u but an anonymous client connect. Closes bug #455402. * Fix man page installation with cmake. Closes bug #458843. * When using `log_dest file` the output file is now flushed periodically. # Clients * Both clients can now load default configuration options from a file. * Add `-C` option to mosquitto_sub to allow the client to quit after receiving a certain count of messages. Closes bug #453850. * Add `--proxy` SOCKS5 support for both clients. * Pub client supports setting its keepalive. Closes bug #454852. * Add support for config files with default options. * Add support for MQTT v3.1.1. # Client library * Add experimental SOCKS5 support. * mosquitto_loop_forever now quits after a fatal error, rather than blindly retrying. * SRV support is now not compiled in by default. * Wildcard TLS certificates are now supported. * mosquittopp now has a virtual destructor. Closes bug #452915. * Add support for MQTT v3.1.1. * Don't quit mosquitto_loop_forever() if broker not available on first connect. Closes bug #453293, but requires more work. # Dependencies This release introduces two new dependencies, libwebsockets and libuuid. Both are optional. libuuid comes from the e2fsprogs project and allows the broker to generate random client ids for MQTT v.3.1.1. The libwebsockets dependency can use either libwebsockets 1.3 or 1.2.x, with 1.3 being the preferred choice. [EPL]: https://www.eclipse.org/legal/epl-v10.html [EDL]: https://eclipse.org/org/documents/edl-v10.php mosquitto-2.0.18/www/posts/2015/04/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/04/version-1-4-1-released.md000066400000000000000000000033631450213760600227130ustar00rootroot00000000000000 This is a bugfix and security release. Users of mosquitto 1.4 are strongly advised to upgrade. Upgrading from earlier versions is recommended but not as important. # Broker * Fix possible crash under heavy network load. Closes [#463241]. This bug only affects version 1.4. * Fix possible crash when using pattern ACLs. * Fix problems parsing config strings with multiple leading spaces. Closes [#462154]. * Websockets clients are now periodically disconnected if they have not maintained their keepalive timer. Closes [#461619]. * Fix possible minor memory leak on acl parsing. # Client library * Inflight limits should only apply to outgoing messages. Closes [#461620]. * Fix reconnect bug on Windows. Closes [#463000]. * Return -1 on error from `mosquitto_socket()`. Closes [#461705]. * Fix crash on multiple calls to `mosquitto_lib_init`/`mosquitto_lib_cleanup`. Closes [#462780]. * Allow longer paths on Windows. Closes [#462781]. * Make `_mosquitto_mid_generate()` thread safe. Closes [#463479]. [#463241]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463241 [#462154]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=462154 [#461619]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=461619 [#461620]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=461620 [#463000]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463000 [#461705]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=461705 [#462780]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=462780 [#462781]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=462781 [#463479]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463479 mosquitto-2.0.18/www/posts/2015/05/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/05/mosquitto-and-current-unreleased-libwebsockets-branch.md000066400000000000000000000013721450213760600315100ustar00rootroot00000000000000 The current unreleased libwebsockets master branch defines the VERSION macro in its header files. I believe this to be a bug in libwebsockets. This bug causes compilation of mosquitto with websockets support to fail. Please use a released version of libwebsockets, either 1.2, 1.3 or 1.4. Mosquitto will compile with all of these versions. I do not recommend using an unreleased version of libwebsockets, the project is not shy about making ABI/API incompatible changes between releases so it is impractical to provide support for. mosquitto-2.0.18/www/posts/2015/05/version-1-4-2-released.md000066400000000000000000000031351450213760600227120ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix bridge prefixes only working for the first outgoing message. Closes [#464437]. * Fix incorrect bridge connection notifications on local broker. * Fix persistent db writing on Windows. Closes [#464779]. * ACLs are now checked before sending a will message. * Fix possible crash when using bridges on Windows. Closes [#465384]. * Fix parsing of `auth_opt_` arguments with extra spaces/tabs. * Broker will return CONNACK rc=5 when a username/password is not authorised. This was being incorrectly set as rc=4. * Fix handling of payload lengths>4096 with websockets. # Client library * Inflight message count wasn't being decreased for outgoing messages using QoS 2, meaning that only up to 20 QoS 2 messages could be sent. This has been fixed. Closes [#464436]. * Fix CMake dependencies for C++ wrapper building. Closes [#463884]. * Fix possibility of select() being called with a socket that is >FD_SETSIZE. This is a fix for [#464632]. * Fix calls to `mosquitto_connect*_async()` not completing. [#464437]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464437 [#464779]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464779 [#465384]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=465384 [#463884]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=463884 [#464436]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464436 [#464632]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=464632 mosquitto-2.0.18/www/posts/2015/08/000077500000000000000000000000001450213760600164645ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/08/version-1-4-3-released.md000066400000000000000000000030311450213760600227110ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix incorrect bridge notification on initial connection. Closes [#467096]. * Build fixes for OpenBSD. * Fix incorrect behaviour for `autosave_interval`, most noticable for `autosave_interval=1`. Closes [#465438]. * Fix handling of outgoing QoS>0 messages for bridges that could not be sent because the bridge connection was down. * Free unused topic tree elements. Closes [#468987]. * Fix some potential memory leaks. Closes [#470253]. * Fix potential crash on libwebsockets error. # Client library * Add missing error strings to `mosquitto_strerror`. * Handle fragmented TLS packets without a delay. Closes [#470660]. * Fix incorrect loop timeout being chosen when using threaded interface and keepalive = 0. Closes [#471334]. * Increment inflight messages count correctly. Closes [#474935]. # Clients * Report error string on connection failure rather than error code. [#467096]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=467096 [#465438]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=465438 [#468987]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=468987 [#470253]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=470253 [#470660]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=470660 [#471334]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=471334 [#474935]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=474935 mosquitto-2.0.18/www/posts/2015/09/000077500000000000000000000000001450213760600164655ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/09/version-1-4-4-released.md000066400000000000000000000012351450213760600227170ustar00rootroot00000000000000 This is a bugfix release. * Don't leak sockets when outgoing bridge with multiple addresses cannot * connect. Closes [#477571]. * Fix cross compiling of websockets. Closes [#475807]. * Fix memory free related crashes on openwrt and FreeBSD. Closes [#475707]. * Fix excessive calls to message retry check. [#477571]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=477571 [#475707]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=475707 [#475807]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=475807 mosquitto-2.0.18/www/posts/2015/11/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/11/version-1-4-5-released.md000066400000000000000000000011441450213760600227100ustar00rootroot00000000000000 This is a bugfix release: # Broker * Fix possible memory leak if bridge using SSL attempts to connect to a host that is not up. * Free unused topic tree elements (fix in 1.4.3 was incomplete). Closes [#468987]. # Clients * `mosquitto_pub -l` now no longer limited to 1024 byte lines. Closes [#478917]. [#468987]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=468987 [#478917]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=478917 mosquitto-2.0.18/www/posts/2015/12/000077500000000000000000000000001450213760600164575ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2015/12/using-lets-encrypt-certificates-with-mosquitto.md000066400000000000000000000016521450213760600302370ustar00rootroot00000000000000 If you want to use TLS certificates you've generated using the [Let's Encrypt] service, this is how you should configure your listener (replace "example.com" with your own domain of course): Then use the following for your mosquitto.conf: ``` listener 8883 cafile /etc/ssl/certs/ISRG_Root_X1.pem certfile /etc/letsencrypt/live/example.com/fullchain.pem keyfile /etc/letsencrypt/live/example.com/privkey.pem ``` Since version 2.0 of Mosquitto, you can send a SIGHUP to the broker to cause it to reload certificates. Prior to this version, mosquitto would never update listener settings when running, so you will need to completely restart the broker. [Let's Encrypt]: https://letsencrypt.org/ mosquitto-2.0.18/www/posts/2015/12/version-1-4-7-released.md000066400000000000000000000012421450213760600227120ustar00rootroot00000000000000 This is a bugfix release. The changes below include the changes for 1.4.6, which wasn't announced. # Broker * Add support for libwebsockets 1.6. # Client library * Fix `_mosquitto_socketpair()` on Windows, reducing the chance of delays when * publishing. Closes [#483979]. # Clients * Fix `mosquitto_pub -l` stripping the final character on a line. Closes [#483981]. [#483979]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=483979 [#483981]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=483981 mosquitto-2.0.18/www/posts/2016/000077500000000000000000000000001450213760600162365ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/01/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/01/test6-mosquitto-org.md000066400000000000000000000007161450213760600227000ustar00rootroot00000000000000 Thanks to a short discussion on irc, test6.mosquitto.org now exists. This is a DNS entry that points to the same address as test.mosquitto.org, but only with an AAAA record. This means that test6.mosquitto.org can be used to test clients using IPv6 and to be sure that IPv6 is actually being used. mosquitto-2.0.18/www/posts/2016/02/000077500000000000000000000000001450213760600164575ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/02/version-1-4-8-released.md000066400000000000000000000025431450213760600227200ustar00rootroot00000000000000 This is a security bugfix release. Any users of the `mount_point` feature are strongly advised to upgrade because versions prior to 1.4.8 allow clients to inject messages outside of their `mount_point` through the use of a Will. # Broker * Wills published by clients connected to a listener with `mount_point` defined now correctly obey the mount point. This was a potential security risk because it allowed clients to publish messages outside of their restricted mount point. This is only affects brokers where the `mount_point` option is in use. Closes [#487178]. * Fix detection of broken connections on Windows. Closes [#485143]. * Close stdin etc. when daemonised. Closes [#485589]. * Fix incorrect detection of FreeBSD and OpenBSD. Closes [#485131]. # Client library * `mosq->want_write` should be cleared immediately before a call to `SSL_write`, to allow clients using `mosquitto_want_write()` to get accurate results. [#487178]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=487178 [#485143]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=485143 [#485589]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=485589 [#485131]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=485131 mosquitto-2.0.18/www/posts/2016/03/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/03/logo-contest-results-for-shortlisting.md000066400000000000000000000013401450213760600264270ustar00rootroot00000000000000 The first round of the logo contest has closed and we now need to shortlist 6 designers. A selection of 20 logos have been chosen out of the 100 entrants and you are invited to vote on them and make comments. If you like a particular logo but not the colour, or like an idea behind the logo but not another element then please say so. The links for voting (please do look at them all) are: mosquitto-2.0.18/www/posts/2016/03/logo-contest.md000066400000000000000000000007501450213760600214210ustar00rootroot00000000000000 We have initiated a paid contest to create a new logo for the Mosquitto project. If you have graphics design skills or know someone who has,  please head over to the link below to see the design brief and submit your idea. mosquitto-2.0.18/www/posts/2016/03/repository-moved-to-github.md000066400000000000000000000014571450213760600242400ustar00rootroot00000000000000 The mosquitto repository is now hosted on github: This is now the canonical location for mosquitto development work. Bug reports should also be made on github and the existing bug reports will be migrated over shortly. The documentation still needs updating with the new location and processes, so please do be patient with regards that. Contributions can now be made through a github pull request. If you want to contribute a bug fix, please base your work off the "fixes" branch, if you are developing a new feature please use the "develop" branch. Here's to a new stage in the mosquitto project! mosquitto-2.0.18/www/posts/2016/05/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/05/stickers.attachments.json000066400000000000000000000010451450213760600235160ustar00rootroot00000000000000{"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"]}}mosquitto-2.0.18/www/posts/2016/05/stickers.md000066400000000000000000000040221450213760600206310ustar00rootroot00000000000000 To celebrate the new mosquitto logo, stickers are now available: [![stickers](/blog/uploads/2016/05/stickers-300x225.jpg)](/blog/uploads/2016/05/stickers.jpg) If you would like to obtain some stickers for yourself you have two options. The first is to get in touch and I'll send you some for a small contribution. This contribution is to cover the cost of the stickers plus postage: (cost of postage)+N\*£0.45, where N is the number of sheets of 6 stickers that you want. Cost of postage for a letter can be calculated using the [Royal Mail price finder], but should be £1.05 for destinations outside of the UK. Please also consider Paypal fees using a [fees calculator] to calculate the final sum. So for a single sheet of stickers posted internationally, the cost would be £1.76 including paypal fees. Two sheets would be £2.23. The second option is to buy a full sticker book through moo.com. This can be done very easily by navigating to This allows you to easily order a sticker book of 90 stickers with either the colour or blue monochrome stickers, or a mix of both. There is a third option - get in touch to say why you deserve some stickers and maybe we'll send you some. We're looking for things that make us say "wow!" If you will be sending your sticker to space, getting mosquitto on television or using MQTT in your Formula 1 technology, these are all things that would exciting to see with a mosquitto sticker in place. If you want to give out stickers at a local IoT related event or similar that's great, but we'd ask that you make a small donation. It's only a small cost for you, but there are many people in your situation and it becomes a noticeable cost for the project. Please do post links of your kit sporting any stickers you use! [Royal Mail price finder]: http://www.royalmail.com/price-finder [fees calculator]: http://www.clothnappytree.com/ppcalculator/ mosquitto-2.0.18/www/posts/2016/06/000077500000000000000000000000001450213760600164635ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/06/version-1-4-9-released.md000066400000000000000000000056171450213760600227320ustar00rootroot00000000000000 This is a bugfix release. # Broker * Ensure websockets clients that previously connected with clean session set to false have their queued messages delivered immediately on reconnecting. Closes [#5]. * Reconnecting client with clean session set to false doesn't start with mid=1 again. * Will topic isn't truncated by one byte when using a `mount_point` any more. * Network errors are printed correctly on Windows. * Fix incorrect $SYS heap memory reporting when using ACLs. * Bridge config parameters couldn't contain a space, this has been fixed. Closes [#150]. * Fix saving of persistence messages that start with a '/'. Closes [#151]. * Fix reconnecting for bridges that use TLS on Windows. Closes [#154]. * Broker and bridges can now cope with unknown incoming PUBACK, PUBREC, PUBREL, PUBCOMP without disconnecting. Closes [#57]. * Fix websockets listeners not being able to bind to an IP address. Closes [#170]. * mosquitto_passwd utility now correctly deals with unknown command line arguments in all cases. Closes [#169]. * Fix publishing of $SYS/broker/clients/maximum * Fix order of #includes in lib/send_mosq.c to ensure struct mosquitto doesn't differ between source files when websockets is being used. Closes [#180]. * Fix possible rare crash when writing out persistence file and a client has incomplete messages inflight that it has been denied the right to publish. # Client library * Fix the case where a message received just before the keepalive timer expired would cause the client to miss the keepalive timer. * Return value of pthread_create is now checked. * _mosquitto_destroy should not cancel threads that weren't created by libmosquitto. Closes [#166]. * Clients can now cope with unknown incoming PUBACK, PUBREC, PUBREL, PUBCOMP without disconnecting. Closes [#57]. * Fix mosquitto_topic_matches_sub() reporting matches on some invalid subscriptions. # Clients * Handle some unchecked malloc() calls. Closes [#1]. # Build * Fix string quoting in CMakeLists.txt. Closes [#4]. * Fix building on Visual Studio 2015. Closes [#136]. [#1]: https://github.com/eclipse/mosquitto/issues/1 [#4]: https://github.com/eclipse/mosquitto/issues/4 [#5]: https://github.com/eclipse/mosquitto/issues/5 [#57]: https://github.com/eclipse/mosquitto/issues/57 [#136]: https://github.com/eclipse/mosquitto/issues/136 [#150]: https://github.com/eclipse/mosquitto/issues/150 [#151]: https://github.com/eclipse/mosquitto/issues/151 [#154]: https://github.com/eclipse/mosquitto/issues/154 [#166]: https://github.com/eclipse/mosquitto/issues/166 [#169]: https://github.com/eclipse/mosquitto/issues/169 [#170]: https://github.com/eclipse/mosquitto/issues/170 [#180]: https://github.com/eclipse/mosquitto/issues/180 mosquitto-2.0.18/www/posts/2016/08/000077500000000000000000000000001450213760600164655ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/08/mqtt-v5-draft-features.md000066400000000000000000000136061450213760600232440ustar00rootroot00000000000000 The [MQTT Technical Committee] at OASIS continue to work on improvements to MQTT. The next version looks set to be MQTT version 5 and has reached the "working draft" stage. This post lists some of the changes that are in the working draft 02 and so gives at least a flavour of the improvements coming up. Take this with a pinch of salt, I may have missed some changes and there is no commitment that any of these features will remain in the final specification as they are described here. # Session management In MQTT v3.1.1 and earlier, a client could control how the server treats its session with the clean session flag. If set to 1, the server would delete any existing session for that client and would not persist the session after disconnecting. If set to 0, the server would restore any existing session for a client when it reconnected, and persist the session when the client disconnected. A session here means the subscriptions for a client and any queued messages. The new spec changes this behaviour. The clean session flag has been renamed to clean start (this was actually the name of the flag in the old MQTT v3 spec) and now only affects how the broker handles a client session when the client connects. If set to 1, the server discards any previous session information, otherwise session information is kept. To deal with removing of sessions at any other time, a new identifier/value pair has been introduced. These identifier/value pairs are an addition to the variable header part of some MQTT packets and allow configuring of different behaviour. In the case of the CONNECT packet, a Session Expiry interval can be specified which is a 4 byte integer that gives the number of seconds after a client has disconnected that the server should remove session information for that client. If the Session Expiry interval is absent from the CONNECT packet, then the session will never expire. If it is set to 0, then the session is removed as soon as the client disconnects. The new clean start flag and session expiry interval allow the existing clean session behaviour to be duplicated but also allow client sessions to be expired based on time. # Updated Connect Return codes The return codes passed to the client in a CONNACK packet have been expanded to include: * 6: Connection Refused, reason unspecified * 7: Connection Refused, implementation specific * 8: Connection Refused, CONNECT packet was malformed # Repeated topics when publishing When publishing data to a single topic, a new feature will help reduce bandwidth use. A client or server can set the topic in a PUBLISH message to be a zero length string. This tells the client/server being published to, to use the previous topic instead. This goes some way to reducing the current overhead associated with publishing - a shame it isn't quite as good as the registered topics available in MQTT-SN. # Payload Format Indicator Another identifier/value pair is available for use when sending a PUBLISH message. This is the Payload Format indicator. If present and set to 1, this indicates that the PUBLISH payload is UTF-8 encoded data. If set to 0, or if the indicator is not present then the payload is an unspecified byte format, exactly as with MQTT v3.1.1. # Publication Expiry interval This is an identifier/value pair for use when publishing. If present, this value is a 4 byte integer which gives the number of seconds for which the server will attempt to deliver this message to a subscriber. This means that an offline client with messages being queued may not receive all of the messages when it reconnects, due to some of them expiring. Interestingly, when the server does deliver a message that had a Publication Expiry set, it sets the Publication Expiry on the outgoing message to the client but with the amount of time that there is left until the message expires. This means that the true time to expiry will propagate through bridges or similar. # Publish Return Codes The PUBACK and PUBREC packets have a new entry in their variable header which is the Publish Return Code. This can be used to tell the client a message has been refused for various reasons, accepted, or accepted with no matching subscribers.  For the PUBREC packet, if the message is refused or accepted with no matching subscribers then there is no expectation for the PUBREL/PUBCOMP messages to be sent for that message. The PUBCOMP packet also has a similar entry which has the same set of return codes and an additional one for the case when a message had expired. This is for the case when a client reconnects with clean start set to 0 and it has a QoS 2 message part way through its handshake, but the server has already expired the message. There is still no way to tell a client that its QoS 0 message was refused. # Disconnect notification In MQTT v3.1.1 and before, only the client sends a DISCONNECT packet. In the draft spec, either the client or the server can send DISCONNECT and it is used to indicate a reason for disconnection. The disconnect return codes are: * 0: Connection disconnected by application (sent by client) * 1: Server temporarily unavailable (server) * 2: Server unavailable (server) * 3: Malformed UNSUBSCRIBE packet received (server) * 4: Session taken over (server - for when another client connects with the same ID) * 5: Malformed packet received It is clear that there is some duplication there, so I think this is a likely place that changes will be made. # Disconnect expiry notification The DISCONNECT packet can also include a Session Expiry interval value, as with CONNECT. This allows a client to clean up when it disconnects, or to set a long expiry time, even if these were not specified at the initial connect. [MQTT Technical Committee]: https://www.oasis-open.org/committees/tc_home.php?wg_abbrev=mqtt mosquitto-2.0.18/www/posts/2016/08/version-1-4-10-released.md000066400000000000000000000030721450213760600227750ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix TLS operation with websockets listeners and libwebsockets 2.x. Closes [#186]. * Don't disconnect client on HUP before reading the pending data. Closes [#7]. * Fix some $SYS messages being incorrectly persisted. Closes [#191]. * Support OpenSSL 1.1.0. * Call fsync after persisting data to ensure it is correctly written. Closes [#189]. * Fix persistence saving of subscription QoS on big-endian machines. * Fix will retained flag handling on Windows. Closes [#222]. * Broker now displays an error if it is unable to open the log file. Closes [#234]. # Client library * Support OpenSSL 1.1.0. * Fixed the C++ library not allowing SOCKS support to be used. Closes [#198]. * Fix memory leak when verifying a server certificate with a subjectAltName section. Closes [#237]. # Build * Don't attempt to install docs when `WITH_DOCS=no`. Closes [#184]. [#7]: https://github.com/eclipse/mosquitto/issues/7 [#184]: https://github.com/eclipse/mosquitto/issues/184 [#186]: https://github.com/eclipse/mosquitto/issues/186 [#189]: https://github.com/eclipse/mosquitto/issues/189 [#191]: https://github.com/eclipse/mosquitto/issues/191 [#198]: https://github.com/eclipse/mosquitto/issues/198 [#222]: https://github.com/eclipse/mosquitto/issues/222 [#234]: https://github.com/eclipse/mosquitto/issues/234 [#237]: https://github.com/eclipse/mosquitto/issues/237 mosquitto-2.0.18/www/posts/2016/12/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2016/12/pre-christmas-update.md000066400000000000000000000042031450213760600230420ustar00rootroot00000000000000 I have taken a bit of a break from Mosquitto for the past few months, partly because I needed a break but also to work on another unrelated project. I'm now back and working on Mosquitto again, primarily implementing support for the upcoming MQTT v5 spec which has added even more features since I mentioned last wrote about it. Once that is in a state that is reasonably compliant if incomplete, I will be looking for testers. There are a few fixes in the repository waiting for release, I anticipate releasing 1.4.11 before the end of the year. There have been some changes to test.mosquitto.org. On its original host I was seeing lots of bandwidth being used by lots of clients, but in particular lots and lots of tiny connections being made which not showing up on my bandwidth monitoring, but were consuming bandwidth and causing problems at my provider. My provider got in touch to say that at times approximately half of the network flows for their network were related to test.mosquitto.org, and could would I please have a chat with the transit provider to discuss how best to manage this service. In the face of that and the risk of exceeding 2TB bandwidth usage per month, test.mosquitto.org has been moved to a lower spec host with smaller pipes and "automatic DDOS protection". This means I now get half a dozen emails per day to say that test.mosquitto.org is under attack. If you find you can't connect to test.mosquitto.org, it might be because you have been blocked by this DDOS protection - if so, maybe think about how you are using the service. The final thought for this post - if you are part of a company that uses mosquitto and it adds value to your company, please consider making a [donation] to the project that reflects that value. If it is difficult for your company to make donations but you would still like to contribute back, please get in touch and maybe we can arrange something. [donation]: https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=J66JWQ3N76L5A mosquitto-2.0.18/www/posts/2017/000077500000000000000000000000001450213760600162375ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2017/02/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2017/02/version-1-4-11-released.md000066400000000000000000000031061450213760600227670ustar00rootroot00000000000000 This is a bugfix release. # Broker * Fix crash when "lazy" type bridge attempts to reconnect. Closes [#259]. * `maximum_connections` now applies to websockets listeners. Closes [#271]. * Allow bridges to use TLS with IPv6. * Don't error on zero length persistence files. Closes [#316]. * For http only websockets clients, close files served over http in all cases when the client disconnects. Closes [#354]. * Fix error message when websockets `http_dir` directory does not exist. * Improve password utility error message. Closes [#379]. * Bridges can use asynchronous DNS lookups on systems with glibc. This can be enabled at compile time using `WITH_ADNS=yes`. # Clients * Use of `--ciphers` no longer requires you to also pass `--tls-version`. Closes [#380]. # Client library * Clients can now use TLS with IPv6. * Fix potential socket leakage when reconnecting. Closes [#304]. * Fix potential negative timeout being passed to pselect. Closes [#329]. [#259]: https://github.com/eclipse/mosquitto/issues/259 [#271]: https://github.com/eclipse/mosquitto/issues/271 [#304]: https://github.com/eclipse/mosquitto/issues/304 [#316]: https://github.com/eclipse/mosquitto/issues/316 [#329]: https://github.com/eclipse/mosquitto/issues/329 [#354]: https://github.com/eclipse/mosquitto/issues/354 [#379]: https://github.com/eclipse/mosquitto/issues/379 [#380]: https://github.com/eclipse/mosquitto/issues/380 mosquitto-2.0.18/www/posts/2017/03/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2017/03/for-the-final-time.attachments.json000066400000000000000000000016471450213760600252650ustar00rootroot00000000000000{"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"]}}mosquitto-2.0.18/www/posts/2017/03/for-the-final-time.md000066400000000000000000000007411450213760600223740ustar00rootroot00000000000000 This guy arrived on Tuesday, two weeks early and weighing 9lb 6oz / 4.26kg. Apologies if I'm a bit out of touch for a while. [![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) mosquitto-2.0.18/www/posts/2017/05/000077500000000000000000000000001450213760600164635ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2017/05/security-advisory-cve-2017-7650.md000066400000000000000000000045641450213760600242640ustar00rootroot00000000000000 A vulnerability exists in Mosquitto versions 0.15 to 1.4.11 inclusive known as [CVE-2017-7650]. Pattern based ACLs can be bypassed by clients that set their username/client id to '#' or '+'. This allows locally or remotely connected clients to access MQTT topics that they do have the rights to. The same issue may be present in third party authentication/access control plugins for Mosquitto. The vulnerability only comes into effect where pattern based ACLs are in use, or potentially where third party plugins are in use. The issue is fixed in Mosquitto 1.4.12, which has just been released. Patches for older versions are available at The fix addresses the problem by restricting access for clients with a '#', '+', or '/' in their username or client id. '/' has been included in the list of characters disallowed because it also has a special meaning in a topic and may represent an additional risk. The restriction placed on clients is that they may not receive or send messages that are subject to a pattern based ACL check, nor any message that is subject to a plugin check. Thanks to Artem Zinenko from HackerDom CTF team for finding this vulnerability and responsibly reporting it. Complete list of fixes addressed in version 1.4.12: # Broker * Fix mosquitto.db from becoming corrupted due to client messages being persisted with no stored message. Closes [#424]. * Fix bridge not restarting properly. Closes [#428]. * Fix unitialized memory in `gets_quiet` on Windows. Closes [#426]. * Fix building with `WITH_ADNS=no` for systems that don't use glibc. Closes [#415]. * Fixes to readme.md. * Fix deprecation warning for OpenSSL 1.1. PR [#416]. * Don't segfault on duplicate bridge names. Closes [#446]. * Fix [CVE-2017-7650]. [CVE-2017-7650]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7650 [#415]: https://github.com/eclipse/mosquitto/issues/415 [#416]: https://github.com/eclipse/mosquitto/issues/416 [#424]: https://github.com/eclipse/mosquitto/issues/424 [#428]: https://github.com/eclipse/mosquitto/issues/428 [#426]: https://github.com/eclipse/mosquitto/issues/426 [#446]: https://github.com/eclipse/mosquitto/issues/446 mosquitto-2.0.18/www/posts/2017/06/000077500000000000000000000000001450213760600164645ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2017/06/citing-eclipse-mosquitto.md000066400000000000000000000014711450213760600237520ustar00rootroot00000000000000 A short paper has been published on Mosquitto in [The Journal of Open Source Software] If you use Mosquitto in your academic work, please now use this paper as your citation. > R. A. Light, "Mosquitto: server and client implementation of the MQTT > protocol," *The Journal of Open Source Software*, vol. 2, no. 13, May 2017, > DOI: [10.21105/joss.00265] The paper link is A [bibtex] entry is available. [The Journal of Open Source Software]: http://joss.theoj.org [10.21105/joss.00265]: http://dx.doi.org/10.21105/joss.00265 [bibtek]: http://www.doi2bib.org/#/doi/10.21105/joss.00265 mosquitto-2.0.18/www/posts/2017/06/security-advisory-cve-2017-9868.md000066400000000000000000000016611450213760600242750ustar00rootroot00000000000000 A vulnerability exists in Mosquitto versions 0.15 to 1.4.12 inclusive known as [CVE-2017-9868]. If persistence is enabled, then the persistence file is created world readable, which has the potential to make sensitive information available to any local user. Patches are available to fix this for Unix like operating systems (i.e. not Windows): This will be fixed in version 1.4.13, due to be released shortly. This can also be fixed administratively by removing world read permissions for the directory that the persistence file is stored in. In many systems this can be achieved with: ``` chmod 700 /var/lib/mosquitto ``` [CVE-2017-9868]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868 mosquitto-2.0.18/www/posts/2017/07/000077500000000000000000000000001450213760600164655ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2017/07/version-1-4-13-released.md000066400000000000000000000033211450213760600227750ustar00rootroot00000000000000 This is a bugfix and security release. # Security * Fix [CVE-2017-9868]. The persistence file was readable by all local users, potentially allowing sensitive information to be leaked. This can also be fixed administratively, by restricting access to the directory in which the persistence file is stored. # Broker * Fix for poor websockets performance. * Fix lazy bridges not timing out for `idle_timeout`. Closes [#417]. * Fix problems with large retained messages over websockets. Closes [#427]. * Set persistence file to only be readable by owner, except on Windows. Closes [#468]. * Fix CONNECT check for reserved=0, as per MQTT v3.1.1 check MQTT-3.1.2-3. * When the broker stop, wills for any connected clients are now "sent". Closes [#477]. * Auth plugins can be configured to disable the check for +# in usernames/client ids with the `auth_plugin_deny_special_chars` option. Partially closes [#462]. * Restrictions for [CVE-2017-7650] have been relaxed - '/' is allowed in usernames/client ids. * Remainder of fix for [#462]. # Clients * Don't use / in auto-generated client ids. [CVE-2017-7650]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7650 [CVE-2017-9868]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-9868 [#417]: https://github.com/eclipse/mosquitto/issues/417 [#427]: https://github.com/eclipse/mosquitto/issues/427 [#462]: https://github.com/eclipse/mosquitto/issues/462 [#468]: https://github.com/eclipse/mosquitto/issues/468 [#477]: https://github.com/eclipse/mosquitto/issues/477 mosquitto-2.0.18/www/posts/2017/07/version-1-4-14-released.md000066400000000000000000000007141450213760600230010ustar00rootroot00000000000000 This is a bugfix release. Version 1.4.13 contained a regression that meant persistence data was only being saved after client information had been freed. This release fixes that. If you use persistence then it is strongly recommended to avoid 1.4.13 so you do not suffer data loss. mosquitto-2.0.18/www/posts/2018/000077500000000000000000000000001450213760600162405ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/01/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/01/mosquitto-debian-repo-key-updated.md000066400000000000000000000010651450213760600254450ustar00rootroot00000000000000 If you are using the [debian repository] at repo.mosquitto.org you may have noticed that the repository signing key expired at the end of 2017. To get the updated key use the following commands: ``` wget http://repo.mosquitto.org/debian/mosquitto-repo.gpg.key sudo apt-key add mosquitto-repo.gpg.key ``` [debian repository]:/blog/2013/01/mosquitto-debian-repository mosquitto-2.0.18/www/posts/2018/02/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/02/security-advisory-cve-2017-7651-cve-2017-7652.md000066400000000000000000000111321450213760600260130ustar00rootroot00000000000000 Mosquitto 1.4.15 has been released to address two security vulnerabilities. # CVE-2017-7651 A vulnerability exists in all Mosquitto versions up to and including 1.4.14 known as [CVE-2017-7651]. Unauthenticated clients can send a crafted CONNECT packet which causes large amounts of memory use in the broker. If multiple clients do this, an out of memory situation can occur and the system may become unresponsive or the broker will be killed by the operating system. The issue is fixed in Mosquitto 1.4.15. Patches for older versions are available at The fix addresses the problem by limiting the permissible size for CONNECT packet, and by adding a `memory_limit` configuration option that allows the broker to self limit the amount of memory it uses. Thanks to Felipe Balabanian for finding this vulnerability and responsibly reporting it. # CVE-2017-7652 A vulnerability exists in Mosquitto versions 1.0 to 1.4.14 inclusive known as [CVE-2017-7652]. If the broker has exhausted all of its free sockets/file descriptors and then a SIGHUP signal is received to trigger reloading of the configuration, then the reloading will fail. This results in many of the configuration options, including security options, being set to their default value. This means that authorisation and access control may no longer be in place. The issue is fixed in Mosquitto 1.4.15. Patches for older versions are available at The fix addresses the problem by only copying the new configuration options to the in use configuration after a successful reload has taken place. # Version 1.4.15 Changes The complete list of fixes addressed in version 1.4.15 is: ## Security * Fix [CVE-2017-7652]. If a SIGHUP is sent to the broker when there are no more file descriptors, then opening the configuration file will fail and security settings will be set back to their default values. * Fix [CVE-2017-7651]. Unauthenticated clients can cause excessive memory use by setting "remaining length" to be a large value. This is now mitigated by limiting the size of remaining length to valid values. A `memory_limit` configuration option has also been added to allow the overall memory used by the broker to be limited. ## Broker * Use constant time memcmp for password comparisons. * Fix incorrect PSK key being used if it had leading zeroes. * Fix memory leak if a client provided a username/password for a listener with `use_identity_as_username` configured. * Fix `use_identity_as_username` not working on websockets clients. * Don't crash if an auth plugin returns `MOSQ_ERR_AUTH` for a username check on a websockets client. Closes [#490]. * Fix 08-ssl-bridge.py test when using async dns lookups. Closes [#507]. * Lines in the config file are no longer limited to 1024 characters long. Closes [#652]. * Fix $SYS counters of messages and bytes sent when message is sent over a Websockets. Closes [#250]. * Fix `upgrade_outgoing_qos` for retained message. Closes [#534]. * Fix CONNACK message not being sent for unauthorised connect on websockets. Closes [#8]. ## Client library * Fix incorrect PSK key being used if it had leading zeroes. * Initialise "result" variable as soon as possible in `mosquitto_topic_matches_sub`. Closes [#654]. * No need to close socket again if setting non-blocking failed. Closes [#649]. * Fix `mosquitto_topic_matches_sub()` not correctly matching `foo/bar` against `foo/+/#`. Closes [#670]. ## Clients * Correctly handle empty files with `mosquitto_pub -l`. Closes [#676]. ## Build * Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes [#636]. [CVE-2017-7651]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7651 [CVE-2017-7652]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2017-7652 [#8]: https://github.com/eclipse/mosquitto/issues/8 [#250]: https://github.com/eclipse/mosquitto/issues/250 [#490]: https://github.com/eclipse/mosquitto/issues/490 [#507]: https://github.com/eclipse/mosquitto/issues/507 [#534]: https://github.com/eclipse/mosquitto/issues/534 [#636]: https://github.com/eclipse/mosquitto/issues/636 [#649]: https://github.com/eclipse/mosquitto/issues/649 [#652]: https://github.com/eclipse/mosquitto/issues/652 [#654]: https://github.com/eclipse/mosquitto/issues/654 [#670]: https://github.com/eclipse/mosquitto/issues/670 [#676]: https://github.com/eclipse/mosquitto/issues/676 mosquitto-2.0.18/www/posts/2018/05/000077500000000000000000000000001450213760600164645ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/05/press-release.md000066400000000000000000000042601450213760600215620ustar00rootroot00000000000000 I am pleased to announce that I am being paid to work on Mosquitto by Cedalo AG. I will be leaving my current job at the end of June this year, but have already started work for Cedalo on a limited basis. The press release for this change is below: # New 1.5 release, MQTT 5.0 roadmap and commercial sponsor for Open-Source Eclipse Mosquitto MQTT Broker Open-Source MQTT Broker Version 1.5 released – Estimate for availability of MQTT 5.0 compliant version – German based Cedalo AG becomes commercial sponsor for future Mosquitto Open Source development A new version of the open source Eclipse Mosquitto MQTT broker is available on the Mosquitto website at [mosquitto.org](https://mosquitto.org). Mosquitto version 1.5 brings a host of changes to the broker, including performance improvements and more flexible authentication and access control, as well as numerous bug fixes. The client library has added some helper functions to allow the creation of extremely simple MQTT clients. The initiator and core developer of the Mosquitto project is now employed by the German based company Cedalo AG ([www.cedalo.com](https://www.cedalo.com)). Cedalo has hired Roger Light to sponsor the further development of Mosquitto and to accelerate the public availability of a powerful MQTT broker. Cedalo is the creator of the end-user oriented IoT modelling tool “Streamsheets”. With the new sponsorship the project is now able to accelerate the path towards new releases. The next version of Mosquitto will add support for MQTT version 5, which is the most substantial revision of the protocol since the first public specification was released. MQTT v5 adds error reporting, enhancements for scalability at the server side, features to help resource constrained clients, and extensible metadata - which is used amongst other things to introduce support for a request/response capability. The MQTT v5 compliant release is planned for the end of August 2018. Press Contact: Cedalo AG 79098 Freiburg, Schnewlinstr. 6 Kristian Raue Mail: presse@cedalo.com mosquitto-2.0.18/www/posts/2018/05/version-1-5-released.md000066400000000000000000000203001450213760600225500ustar00rootroot00000000000000 1.5 - 20180502 ============== This is a features release. Updated binaries will be available shortly. # Security * Fix memory leak that could be caused by a malicious CONNECT packet. This does not yet have a CVE assigned. Closes #533493 (on Eclipse bugtracker) # Broker features * Add `per_listener_settings` to allow authentication and access control to be per listener. * Add limited support for reloading listener settings. This allows settings for an already defined listener to be reloaded, but port numbers must not be changed. * Add ability to deny access to SUBSCRIBE messages as well as the current read/write accesses. Currently for auth plugins only. * Reduce calls to malloc through the use of UHPA. * Outgoing messages with QoS>1 are no longer retried after a timeout period. Messages will be retried when a client reconnects. This change in behaviour can be justified by considering when the timeout may have occurred. * If a connection is unreliable and has dropped, but without one end noticing, the messages will be retried on reconnection. Sending additional PUBLISH or PUBREL would not have changed anything. * If a client is overloaded/unable to respond/has a slow connection then sending additional PUBLISH or PUBREL would not help the client catch up. Once the backlog has cleared the client will respond. If it is not able to catch up, sending additional duplicates would not help either. * Add `use_subject_as_username` option for certificate based client authentication to use the entire certificate subject as a username, rather than just the CN. Closes #469467. * Change sys tree printing output. This format shouldn't be relied upon and may change at any time. Closes #470246. * Minimum supported libwebsockets version is now 1.3. * Add systemd startup notification and services. Closes #471053. * Reduce unnecessary malloc and memcpy when receiving a message and storing it. Closes #470258. * Support for Windows XP has been dropped. * Bridge connections now default to using MQTT v3.1.1. * `mosquitto_db_dump` tool can now output some stats on clients. * Perform utf-8 validation on incoming will, subscription and unsubscription topics. * new $SYS/broker/store/messages/count (deprecates $SYS/broker/messages/stored) * new $SYS/broker/store/messages/bytes * `max_queued_bytes` feature to limit queues by real size rather than than just message count. Closes Eclipse #452919 or Github #100 * Add support for bridges to be configured to only send notifications to the local broker. * Add `set_tcp_nodelay` option to allow Nagle's algorithm to be disabled on client sockets. Closes #433. * The behaviour of `allow_anonymous` has changed. In the old behaviour, the default if not set was to allow anonymous access. The new behaviour is to default is to allow anonymous access unless another security option is set. For example, if `password_file` is set and `allow_anonymous` is not set, then anonymous access will be denied. It is still possible to allow anonymous access by setting it explicitly. # Broker fixes * Fix UNSUBSCRIBE with no topic is accepted on MQTT 3.1.1. Closes #665. * Produce an error if two bridges share the same `local_clientid`. * Miscellaneous fixes on Windows. * `queue_qos0_messages` was not observing `max_queued_**` limits * When using the `include_dir` configuration option sort the files alphabetically before loading them. Closes #17. * IPv6 is no longer disabled for websockets listeners. * Remove all build timestamp information including $SYS/broker/timestamp. Closes #651. * Correctly handle incoming strings that contain a NULL byte. Closes #693. * Use constant time memcmp for password comparisons. * Fix incorrect PSK key being used if it had leading zeroes. * Fix memory leak if a client provided a username/password for a listener with `use_identity_as_username` configured. * Fix `use_identity_as_username` not working on websockets clients. * Don't crash if an auth plugin returns `MOSQ_ERR_AUTH` for a username check on a websockets client. Closes #490. * Fix 08-ssl-bridge.py test when using async dns lookups. Closes #507. * Lines in the config file are no longer limited to 1024 characters long. Closes #652. * Fix $SYS counters of messages and bytes sent when message is sent over a Websockets. Closes #250. * Fix `upgrade_outgoing_qos` for retained message. Closes #534. * Fix CONNACK message not being sent for unauthorised connect on websockets. Closes #8. * Maximum connections on Windows increased to 2048. * When a client with an in-use client-id connects, if the old client has a will, send the will message. Closes #26. * Fix parsing of configuration options that end with a space. Closes #804. # Client library features * Outgoing messages with QoS>1 are no longer retried after a timeout period. Messages will be retried when a client reconnects. * DNS-SRV support is now disabled by default. * Add `mosquitto_subscribe_simple()` This is a helper function to make retrieving messages from a broker very straightforward. Examples of its use are in `examples/subscribe_simple`. * Add `mosquitto_subscribe_callback()` This is a helper function to make processing messages from a broker very straightforward. An example of its use is in `examples/subscribe_simple`. * Connections now default to using MQTT v3.1.1. * Add `mosquitto_validate_utf8()` to check whether a string is valid UTF-8 according to the UTF-8 spec and to the additional restrictions imposed by the MQTT spec. * Topic inputs are checked for UTF-8 validity. * Add `mosquitto_userdata` function to allow retrieving the client userdata member variable. Closes #111. * Add `mosquitto_pub_topic_check2()`, `mosquitto_sub_topic_check2()`, and `mosquitto_topic_matches_sub2()` which are identical to the similarly named functions but also take length arguments. * Add `mosquitto_connect_with_flags_callback_set()`, which allows a second connect callback to be used which also exposes the connect flags parameter. Closes #738 and #128. * Add `MOSQ_OPT_SSL_CTX` option to allow a user specified `SSL_CTX` to be used instead of the one generated by libmosquitto. This allows greater control over what options can be set. Closes #715. * Add `MOSQ_OPT_SSL_CTX_WITH_DEFAULTS` to work with `MOSQ_OPT_SSL_CTX` and have the default libmosquitto `SSL_CTX` configuration applied to the user provided `SSL_CTX`. Closes #567. # Client library fixes * Fix incorrect PSK key being used if it had leading zeroes. * Initialise "result" variable as soon as possible in `mosquitto_topic_matches_sub`. Closes #654. * No need to close socket again if setting non-blocking failed. Closes #649. * Fix `mosquitto_topic_matches_sub()` not correctly matching foo/bar against foo/+/#. Closes #670. * SNI host support added. # Client features * Add -F to `mosquitto_sub` to allow the user to choose the output format. * Add -U to `mosquitto_sub` for unsubscribing from topics. * Add -c (clean session) to `mosquitto_pub`. * Add --retained-only to `mosquitto_sub` to exit after receiving all retained messages. * Add -W to allow `mosquitto_sub` to stop processing incoming messages after a timeout. * Connections now default to using MQTT v3.1.1. * Default to using port 8883 when using TLS. * `mosquitto_sub` doesn't continue to keep connecting if CONNACK tells it the connection was refused. # Client fixes * Correctly handle empty files with `mosquitto_pub -l`. Closes #676. # Build * Add `WITH_STRIP` option (defaulting to "no") that when set to "yes" will strip executables and shared libraries when installing. * Add `WITH_STATIC_LIBRARIES` (defaulting to "no") that when set to "yes" will build and install static versions of the client libraries. * Don't run TLS-PSK tests if TLS-PSK disabled at compile time. Closes #636. * Support for openssl versions 1.0.0 and 1.0.1 has been removed as these are no longer supported by openssl. # Documentation * Replace mentions of deprecated `c_rehash` with `openssl rehash`. mosquitto-2.0.18/www/posts/2018/08/000077500000000000000000000000001450213760600164675ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/08/updated-debian-repository-backend.md000066400000000000000000000016541450213760600254670ustar00rootroot00000000000000 The backend software for administering the Debian repository at https://repo.mosquitto.org/ has been migrated from `reprepro` to `aptly`. This has the benefit of allowing multiple versions of a package to remain in the repository. For mosquitto, this now means that old versions of the Debian packages will remain available even after newer versions are published, and so you can depend on a particular version. The recommendation is always to use the latest version of course. This change should be transparent to all current users, but there is the possibility that something is different between the two repository tools. If you do find a problem, please let us know. The repository now has builds for versions 1.4.15 and 1.5. mosquitto-2.0.18/www/posts/2018/08/version-151-released.md000066400000000000000000000070041450213760600225650ustar00rootroot00000000000000 This is a bugfix release. # Packaging changes * The snap package now has support for websockets included. * The Windows packages have changed. - Support for Windows XP was dropped in Mosquitto 1.5, so the need for the Cygwin build has gone, and this has been dropped. - There are now 64-bit and 32-bit native packages. - Websockets support is included. - Threading support is not included in libmosquitto to simplify installation, alternative solutions are being looked into for the future. - The only external dependency is now OpenSSL. # Version 1.5.1 changes ## Broker - Fix plugin cleanup function not being called on exit of the broker. Closes [#900]. - Print more OpenSSL errors when loading certificates/keys fail. - Use `AF_UNSPEC` etc. instead of `PF_UNSPEC` to comply with POSIX. Closes [#863]. - Remove use of `AI_ADDRCONFIG`, which means the broker can be used on systems where only the loopback interface is defined. Closes [#869], Closes [#901]. - Fix IPv6 addresses not being able to be used as bridge addresses. Closes [#886]. - All clients now time out if they exceed their keepalive\*1.5, rather than just reach it. This was inconsistent in two places. - Fix segfault on startup if bridge CA certificates could not be read. Closes [#851]. - Fix problem opening listeners on Pi caused by unsigned char being default. Found via [#849]. - ACL patterns that do not contain either `%c` or `%u` now produce a warning in the log. Closes [#209]. - Fix bridge publishing failing when `per_listener_settings` was true. Closes [#860]. - Fix `use_identity_as_username true` not working. Closes [#833]. - Fix UNSUBACK messages not being logged. Closes [#903]. - Fix possible endian issue when reading the `memory_limit` option. - Fix building for libwebsockets < 1.6. - Fix accessor functions for username and client id when used in plugin auth check. ## Library - Fix some places where return codes were incorrect, including to the `on_disconnect()` callback. This has resulted in two new error codes, `MOSQ_ERR_KEEPALIVE` and `MOSQ_ERR_LOOKUP`. - Fix connection problems when `mosquitto_loop_start()` was called before `mosquitto_connect_async()`. Closes [#848]. ## Clients - When compiled using `WITH_TLS=no`, the default port was incorrectly being set to -1. This has been fixed. - Fix compiling on Mac OS X <10.12. Closes `#813` and `#240`. ## Build - Fixes for building on NetBSD. Closes `#258`. - Fixes for building on FreeBSD. - Add support for compiling with static libwebsockets library. [#209]: https://github.com/eclipse/mosquitto/issues/209 [#240]: https://github.com/eclipse/mosquitto/issues/240 [#258]: https://github.com/eclipse/mosquitto/issues/258 [#813]: https://github.com/eclipse/mosquitto/issues/813 [#833]: https://github.com/eclipse/mosquitto/issues/833 [#848]: https://github.com/eclipse/mosquitto/issues/848 [#849]: https://github.com/eclipse/mosquitto/issues/849 [#851]: https://github.com/eclipse/mosquitto/issues/851 [#860]: https://github.com/eclipse/mosquitto/issues/860 [#863]: https://github.com/eclipse/mosquitto/issues/863 [#869]: https://github.com/eclipse/mosquitto/issues/869 [#886]: https://github.com/eclipse/mosquitto/issues/886 [#900]: https://github.com/eclipse/mosquitto/issues/900 [#901]: https://github.com/eclipse/mosquitto/issues/901 [#903]: https://github.com/eclipse/mosquitto/issues/903 mosquitto-2.0.18/www/posts/2018/09/000077500000000000000000000000001450213760600164705ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/09/security-advisory-cve-2018-12543.md000066400000000000000000000041011450213760600243320ustar00rootroot00000000000000 Mosquitto 1.5.3 has been released to address a security vulnerability. It also includes other bug fixes. # CVE-2018-12543 A vulnerability exists in Mosquitto versions 1.5 to 1.5.2 inclusive, known as [CVE-2018-12543]. If a message received by the broker has a topic that begins with `$`, but that does not begin `$SYS`, an assert is triggered that should otherwise not be accessible, causing Mosquitto to exit. The issue is fixed in Mosquitto 1.5.3. Patches for older versions are available at The fix addresses the problem by reverting a commit that intended to remove some unused checks, but also stopped part of the topic hierarchy being created. # Version 1.5.3 Changes The complete list of fixes addressed in version 1.5.3 is: ## Security * Fix [CVE-2018-12543]. If a message is sent to Mosquitto with a topic that begins with `$`, but is not `$SYS`, then an assert that should be unreachable is triggered and Mosquitto will exit. ## Broker * Elevate log level to warning for situation when socket limit is hit. * Remove requirement to use `user root` in snap package config files. * Fix retained messages not sent by bridges on outgoing topics at the first connection. Closes [#701]. * Documentation fixes. Closes [#520], [#600]. * Fix duplicate clients being added to by_id hash before the old client was removed. Closes [#645]. * Fix Windows version not starting if `include_dir` did not contain any files. Closes [#566]. ## Build * Various fixes to ease building. [CVE-2018-12543]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2018-12543 [#520]: https://github.com/eclipse/mosquitto/issues/520 [#566]: https://github.com/eclipse/mosquitto/issues/566 [#600]: https://github.com/eclipse/mosquitto/issues/600 [#645]: https://github.com/eclipse/mosquitto/issues/645 [#701]: https://github.com/eclipse/mosquitto/issues/701 mosquitto-2.0.18/www/posts/2018/09/version-152-released.md000066400000000000000000000024151450213760600225700ustar00rootroot00000000000000 This is a bugfix release. # Version 1.5.2 changes ## Broker - Fix build when using `WITH_ADNS=yes`. - Fix incorrect call to setsockopt() for `TCP_NODELAY`. Closes [#941]. - Fix excessive CPU usage when the number of sockets exceeds the system limit. Closes [#948]. - Fix for bridge connections when using `WITH_ADNS=yes`. - Fix `round_robin false` behaviour. Closes [#481]. - Fix segfault on HUP when bridges and security options are configured. Closes [#965]. ## Library - Fix situation where username and password is used with SOCKS5 proxy. Closes [#927]. - Fix SOCKS5 behaviour when passing IP addresses. Closes [#927]. ## Build - Make it easier to build without bundled uthash.h using `WITH_BUNDLED_DEPS=no`. - Fix build with OPENSSL_NO_ENGINE. Closes [#932]. [#481]: https://github.com/eclipse/mosquitto/issues/481 [#927]: https://github.com/eclipse/mosquitto/issues/927 [#932]: https://github.com/eclipse/mosquitto/issues/932 [#941]: https://github.com/eclipse/mosquitto/issues/941 [#948]: https://github.com/eclipse/mosquitto/issues/948 [#965]: https://github.com/eclipse/mosquitto/issues/965 mosquitto-2.0.18/www/posts/2018/11/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/11/mqtt5-progress.md000066400000000000000000000037731450213760600217310ustar00rootroot00000000000000 Development of support for MQTT 5 is ongoing and making good progress, but has been substantially delayed due to other non-Mosquitto work having to take priority. It is possible to test the current state of MQTT 5 support by using the `mqtt5` branch of the [repository]. Please note that this is very much a work in progress, so parts are incomplete and interfaces may yet change. The client library in particular has had to have an increase in functions available in order to provide the features needed whilst providing backwards compatibility. Part of the plan for the 2.0 release, which will follow after 1.6, is to consolidate the libmosquitto API with breaking changes. There are more details on the [roadmap]. Current features include: * Support for all incoming and outgoing packets, although not everything is processed. * Support for sending and receiving all properties, with not all properties processed. * Client support for setting properties * Request/response support (client cannot process incoming correlation data) * Retain availability * Message expiry interval support * Server support for assigned client identifiers * Payload format indicator support * Content-type support * Basic topic alias support from client to broker * Lots of new tests Both `mosquitto_pub` and `mosquitto_sub` support setting properties on the command line, for example: ``` mosquitto_sub -t topic -v -D connect session-expiry-interval 60 -D connect user-property key value -D subscribe user-property sub-key sub-value ``` ``` mosquitto_pub -t topic -m '{"key":"value"}' -D publish content-type "application/json" ``` ``` ./sensor_read.sh | mosquitto_pub -t topic -l -D publish topic-alias 1 ``` Further updates will be posted when more features are available. [repository]: https://github.com/eclipse/mosquitto/tree/mqtt5 [roadmap]: https://mosquitto.org/roadmap/ mosquitto-2.0.18/www/posts/2018/11/version-154-released.md000066400000000000000000000045101450213760600225610ustar00rootroot00000000000000 This is a bugfix and security release. # Version 1.5.4 changes ## Security - When using a TLS enabled websockets listener with `require_certificate` enabled, the mosquitto broker does not correctly verify client certificates. This is now fixed. All other security measures operate as expected, and in particular non-websockets listeners are not affected by this. Closes [#996]. ## Broker - Process all pending messages even when a client has disconnected. This means a client that send a PUBLISH then DISCONNECT quickly, then disconnects will have its DISCONNECT message processed properly and so no Will will be sent. Closes [#7]. - $SYS/broker/clients/disconnected should never be negative. Closes [#287]. - Give better error message if a client sends a password without a username. Closes [#1015]. - Fix bridge not honoring `restart_timeout`. Closes [#1019]. - Don't disconnect a client if an auth plugin denies access to SUBSCRIBE. Closes [#1016]. ## Library - Fix memory leak that occurred if `mosquitto_reconnect()` was used when TLS errors were present. Closes [#592]. - Fix TLS connections when using an external event loop with `mosquitto_loop_read()` and `mosquitto_write()`. Closes [#990]. ## Build - Fix clients not being compiled with threading support when using CMake. Closes [#983]. - Header fixes for FreeBSD. Closes [#977]. - Use `_GNU_SOURCE` to fix build errors in websockets and getaddrinfo usage. Closes [#862] and [#933]. - Fix builds on QNX 7.0.0. Closes [#1018]. [#7]: https://github.com/eclipse/mosquitto/issues/7 [#287]: https://github.com/eclipse/mosquitto/issues/287 [#592]: https://github.com/eclipse/mosquitto/issues/592 [#933]: https://github.com/eclipse/mosquitto/issues/933 [#977]: https://github.com/eclipse/mosquitto/issues/977 [#983]: https://github.com/eclipse/mosquitto/issues/983 [#990]: https://github.com/eclipse/mosquitto/issues/990 [#996]: https://github.com/eclipse/mosquitto/issues/996 [#1015]: https://github.com/eclipse/mosquitto/issues/1015 [#1016]: https://github.com/eclipse/mosquitto/issues/1016 [#1018]: https://github.com/eclipse/mosquitto/issues/1018 [#1019]: https://github.com/eclipse/mosquitto/issues/1019 mosquitto-2.0.18/www/posts/2018/12/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2018/12/version-155-released.md000066400000000000000000000046511450213760600225710ustar00rootroot00000000000000 This is a bugfix and security release. # Version 1.5.5 changes ## Security - If `per_listener_settings` is set to true, then the `acl_file` setting was ignored for the "default listener" only. This has been fixed. This does not affect any listeners defined with the `listener` option. Closes [#1073]. This is now tracked as [CVE-2018-20145]. ## Broker - Add `socket_domain` option to allow listeners to disable IPv6 support. This is required to work around a problem in libwebsockets that means sockets only listen on IPv6 by default if IPv6 support is compiled in. Closes [#1004]. - When using ADNS, don't ask for all network protocols when connecting, because this can lead to confusing "Protocol not supported" errors if the network is down. Closes [#1062]. - Fix outgoing retained messages not being sent by bridges on initial connection. Closes [#1040]. - Don't reload `auth_opt_` options on reload, to match the behaviour of the other plugin options. Closes [#1068]. - Print message on error when installing/uninstalling as a Windows service. - All non-error connect/disconnect messages are controlled by the `connection_messages` option. Closes [#772]. Closes [#613]. Closes [#537]. ## Library - Fix reconnect delay backoff behaviour. Closes [#1027]. - Don't call `on_disconnect()` twice if keepalive tests fail. Closes [#1067]. ## Client - Always print leading zeros in `mosquitto_sub` when output format is hex. Closes [#1066]. ## Build - Fix building where TLS-PSK is not available. Closes [#68]. [CVE-2018-20145]: https://nvd.nist.gov/vuln/detail/CVE-2018-20145 [#68]: https://github.com/eclipse/mosquitto/issues/68 [#537]: https://github.com/eclipse/mosquitto/issues/537 [#613]: https://github.com/eclipse/mosquitto/issues/613 [#772]: https://github.com/eclipse/mosquitto/issues/772 [#1004]: https://github.com/eclipse/mosquitto/issues/1004 [#1027]: https://github.com/eclipse/mosquitto/issues/1027 [#1040]: https://github.com/eclipse/mosquitto/issues/1040 [#1062]: https://github.com/eclipse/mosquitto/issues/1062 [#1066]: https://github.com/eclipse/mosquitto/issues/1066 [#1067]: https://github.com/eclipse/mosquitto/issues/1067 [#1068]: https://github.com/eclipse/mosquitto/issues/1068 [#1073]: https://github.com/eclipse/mosquitto/issues/1073 mosquitto-2.0.18/www/posts/2019/000077500000000000000000000000001450213760600162415ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2019/02/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2019/02/mqtt5-test-release.md000066400000000000000000000023521450213760600224530ustar00rootroot00000000000000 The 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. The source is available at [mosquitto-mqtt5-test1.tar.gz] and can be compiled as normal. ## Supported features * Session expiry * Message expiry * Reason code on all ACKs (not all reason codes are used) * Reason string on all ACKs (no reason strings are provided by the broker however) * Payload format and content type * Request / response pattern * Subscription ID * Topic alias * Flow control * User properties * Optional server feature availability * Subscription options * Server keep alive * Assigned Client ID ## Unsupported features * Shared subscriptions * Extended authentication * Server reference * Not all reason codes are used by the broker * Bridges do not use MQTT v5 * On disk persistence does not include MQTT 5 property support * The broker will not create topic aliases [mosquitto-mqtt5-test1.tar.gz]: https://mosquitto.org/files/source/test/mosquitto-mqtt5-test1.tar.gz mosquitto-2.0.18/www/posts/2019/02/version-1-5-6-released.md000066400000000000000000000073401450213760600227220ustar00rootroot00000000000000 Mosquitto 1.5.6 has been released to address three potential security vulnerabilities. # CVE-2018-12551 If Mosquitto is configured to use a password file for authentication, any malformed data in the password file will be treated as valid. This typically means that the malformed data becomes a username and no password. If this occurs, clients can circumvent authentication and get access to the broker by using the malformed username. In particular, a blank line will be treated as a valid empty username. Other security measures are unaffected. **Users who have only used the `mosquitto_passwd` utility to create and modify their password files are unaffected by this vulnerability**. Affects version 1.0 to 1.5.5 inclusive. Patches for older versions are available at # CVE-2018-12550 If an ACL file is empty, or has only blank lines or comments, then mosquitto treats the ACL file as not being defined, which means that no topic access is denied. Although denying access to all topics is not a useful configuration, this behaviour is unexpected and could lead to access being incorrectly granted in some circumstances. Affects versions 1.0 to 1.5.5 inclusive. Patches for older versions are available at # CVE-2018-12546 If a client publishes a retained message to a topic that they have access to, and then their access to that topic is revoked, the retained message will still be delivered to future subscribers. This behaviour may be undesirable in some applications, so a configuration option `check_retain_source` has been introduced to enforce checking of the retained message source on publish. Patches for older versions are available at # Version 1.5.6 Changes The list of other fixes addressed in version 1.5.6 is: ## Broker - Fixed comment handling for config options that have optional arguments. - Improved documentation around bridge topic remapping. - Handle mismatched handshakes (e.g. QoS1 PUBLISH with QoS2 reply) properly. - Fix spaces not being allowed in the bridge `remote_username option`. Closes [#1131]. - Allow broker to always restart on Windows when using `log_dest file`. Closes [#1080]. - Fix Will not being sent for Websockets clients. Closes [#1143]. - Windows: Fix possible crash when client disconnects. Closes [#1137]. - Fixed durable clients being unable to receive messages when offline, when `per_listener_settings` was set to true. Closes [#1081]. - Add log message for the case where a client is disconnected for sending a topic with invalid UTF-8. Closes [#1144]. ## Library - Fix TLS connections not working over SOCKS. - Don't clear SSL context when TLS connection is closed, meaning if a user provided an external SSL_CTX they have less chance of leaking references. ## Build - Fix comparison of boolean values in CMake build. Closes [#1101]. - Fix compilation when openssl deprecated APIs are not available. Closes [#1094]. - Man pages can now be built on any system. Closes [#1139]. [#1080]: https://github.com/eclipse/mosquitto/issues/1080 [#1081]: https://github.com/eclipse/mosquitto/issues/1081 [#1094]: https://github.com/eclipse/mosquitto/issues/1094 [#1101]: https://github.com/eclipse/mosquitto/issues/1101 [#1131]: https://github.com/eclipse/mosquitto/issues/1131 [#1137]: https://github.com/eclipse/mosquitto/issues/1137 [#1139]: https://github.com/eclipse/mosquitto/issues/1139 [#1143]: https://github.com/eclipse/mosquitto/issues/1143 [#1144]: https://github.com/eclipse/mosquitto/issues/1144 mosquitto-2.0.18/www/posts/2019/02/version-1-5-7-released.md000066400000000000000000000022701450213760600227200ustar00rootroot00000000000000 This is a bugfix release. ## Broker - Fix build failure when using `WITH_ADNS=yes` - Ensure that an error occurs if `per_listener_settings true` is given after other security options. Closes [#1149]. - Fix `include_dir` not sorting config files before loading. This was partially fixed in 1.5 previously. - Improve documentation around the `include_dir` option. Closes [#1154]. - Fix case where old unreferenced msg_store messages were being saved to the persistence file, bloating its size unnecessarily. Closes [#389]. ## Library - Fix `mosquitto_topic_matches_sub()` not returning MOSQ_ERR_INVAL for invalid subscriptions like `topic/#abc`. This only affects the return value, not the match/no match result, which was already correct. ## Build - Don't require C99 compiler. - Add rewritten build test script and remove some build warnings. [#389]: https://github.com/eclipse/mosquitto/issues/389 [#1149]: https://github.com/eclipse/mosquitto/issues/1149 [#1154]: https://github.com/eclipse/mosquitto/issues/1154 mosquitto-2.0.18/www/posts/2019/02/version-1-5-8-released.md000066400000000000000000000022421450213760600227200ustar00rootroot00000000000000 This is a bugfix release. ## Broker - Fix clients being disconnected when ACLs are in use. This only affects the case where a client connects using a username, and the anonymous ACL list is defined but specific user ACLs are not defined. Closes [#1162]. - Make error messages for missing config file clearer. - Fix some Coverity Scan reported errors that could occur when the broker was already failing to start. - Fix broken `mosquitto_passwd` on FreeBSD. Closes [#1032]. - Fix delayed bridge local subscriptions causing missing messages. Closes [#1174]. ## Library - Use higher resolution timer for random initialisation of client id generation. Closes [#1177]. - Fix some Coverity Scan reported errors that could occur when the library was already quitting. [#1032]: https://github.com/eclipse/mosquitto/issues/1032 [#1162]: https://github.com/eclipse/mosquitto/issues/1162 [#1174]: https://github.com/eclipse/mosquitto/issues/1174 [#1177]: https://github.com/eclipse/mosquitto/issues/1177 mosquitto-2.0.18/www/posts/2019/04/000077500000000000000000000000001450213760600164645ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2019/04/version-1-6-1-released.md000066400000000000000000000022231450213760600227130ustar00rootroot00000000000000 This is a minor service release. The fixes are only related to documentation and the build process, and so is primarily of interest for people building Mosquitto. ## Broker - Document `memory_limit` option. ## Clients - Fix compilation on non glibc systems due to missing sys/time.h header. ## Build: - Add `make check` target and document testing procedure. Closes [#1230]. - Document bundled dependencies and how to disable. Closes [#1231]. - Split CFLAGS and CPPFLAGS, and LDFLAGS and LDADD/LIBADD. - test/unit now respects CPPFLAGS and LDFLAGS. Closes [#1232]. - Don't call ldconfig in CMake scripts. Closes [#1048]. - Use `CMAKE_INSTALL_*` variables when installing in CMake. Closes [#1049]. [#1048]: https://github.com/eclipse/mosquitto/issues/1048 [#1049]: https://github.com/eclipse/mosquitto/issues/1049 [#1230]: https://github.com/eclipse/mosquitto/issues/1230 [#1231]: https://github.com/eclipse/mosquitto/issues/1231 [#1232]: https://github.com/eclipse/mosquitto/issues/1232 mosquitto-2.0.18/www/posts/2019/04/version-1-6-2-released.md000066400000000000000000000032041450213760600227140ustar00rootroot00000000000000 This is a security and bugfix release. ## Security If a client connects using MQTT v5, will a Will message that has MQTT v5 properties attached, and the very first Will property is one of `content-type`, `correlation-data`, `payload-format-indicator`, or `response-topic`, then at the point the client disconnects, the broker will attempt to read from freed memory, resulting in a possible crash. ## Broker - Fix memory access after free, leading to possible crash, when v5 client with Will message disconnects, where the Will message has as its first property one of `content-type`, `correlation-data`, `payload-format-indicator`, or `response-topic`. Closes [#1244]. - Fix build for `WITH_TLS=no`. Closes [#1250]. - Fix Will message not allowing `user-property` properties. - Fix broker originated messages (e.g. `$SYS/broker/version`) not being published when `check_retain_source` set to true. Closes [#1245]. - Fix `$SYS/broker/version` being incorrectly expired after 60 seconds. Closes [#1245]. ## Library - Fix crash after client has been unable to connect to a broker. This occurs when the client is exiting and is part of the final library cleanup routine. Closes [#1246]. ## Clients - Fix `-L` url parsing. Closes [#1248]. [#1244]: https://github.com/eclipse/mosquitto/issues/1244 [#1245]: https://github.com/eclipse/mosquitto/issues/1245 [#1246]: https://github.com/eclipse/mosquitto/issues/1246 [#1250]: https://github.com/eclipse/mosquitto/issues/1250 mosquitto-2.0.18/www/posts/2019/04/version-1-6-released.md000066400000000000000000000135761450213760600225720ustar00rootroot00000000000000 This is a feature release and represents a substantial amount of change in the project. Since version 1.5, the overall code line count for the broker, library and clients has increased by 37% to 28k. Testing has been an important focus for this release. The number of tests has increased from 102 to 412. The test coverage, whilst still needing further improvement, has increased from 56% to 61%. A summary of the notable features is given below. # MQTT v5 support The big addition for this release is support for MQTT v5. This covers the broker, client library and client, and gives full support for the new specification, although not all features are accessible as they will be. You can quickly test out a v5 client by using `-V 5` and adding properties with the `-D` option, for example: ``` mosquitto_sub -V 5 -D connect receive-maximum 3 -D subscribe subscription-identifier 1 ... ``` The authentication plugin interface has been extended to allow use of the v5 extended authentication feature. # Performance improvements A number of performance improvements have been implemented in the broker, including the message routing logic, topic matching, and persistence file reading/writing. More improvements are planned for the next release. # New client - mosquitto_rr `mosquitto_rr` is the "request-response" client, intended for the situation where you want to publish a request message and await a response. It works best with the MQTT v5 request-response features, but can be used with v3.1.1 servers if the client it is talking to knows how to respond. This tool is almost certainly not going to see as much use as `mosquitto_sub` or `mosquitto_pub`, but is a useful utility to have available. # Contributed features Some notable features have been contributed by the community. On the TLS front, support for ALPN allows bridges and clients to connect to servers that have multiple protocols on a single port. The new OCSP stapling support allows the status of TLS certificates to be validated. Finally, TLS Engine support has been added. Away from TLS, support for Automotive DLT logging has been added, disabled by default. # Deprecations The C++ wrapper library, libmosquittopp is now deprecated and will be removed in version 2.0. It remains largely unchanged since v1.5. The C library, libmosquitto, is having its interface changed for version 2.0, so any current function should be considered at risk. The rationale for this is to consolidate the changes introduced since version 1.0, in particular the large number of functions required to support MQTT v5, but that otherwise closely match existing functions. Support for TLS v1.0 has been dropped. Support for TLS v1.1 will be dropped in version 2.0. # Changelog The more detailed changelog is below, but does not include many of the fixes and improvements that have been made: ## Broker features - Add support for MQTT v5 - Add support for OCSP stapling. - Add support for ALPN on bridge TLS connections. Closes [#924]. - Add support for Automotive DLT logging. - Add TLS Engine support. - Persistence file read/write performance improvements. - General performance improvements. - Add `max_keepalive option`, to allow a maximum keepalive value to be set for MQTT v5 clients only. - Add `bind_interface` option which allows a listener to be bound to a specific network interface, in a similar fashion to the `bind_address` option. Linux only. - Add improved bridge restart interval based on Decorrelated Jitter. - Add `dhparamfile` option, to allow DH parameters to be loaded for Ephemeral DH support - Disallow writing to $ topics where appropriate. - Add explicit support for TLS v1.3. - Drop support for TLS v1.0. - Improved general support for broker generated client ids. Removed libuuid dependency. - `auto_id_prefix` now defaults to 'auto-'. - QoS 1 and 2 flow control improvements. ## Client library features - Add support for MQTT v5 - Add `mosquitto_subscribe_multiple()` for sending subscriptions to multiple topics in one command. - Add TLS Engine support. - Add explicit support for TLS v1.3. - Drop support for TLS v1.0. - QoS 1 and 2 flow control improvements. ## Client features - Add support for MQTT v5 - Add `mosquitto_rr ` client, which can be used for "request-response" messaging, by sending a request message and awaiting a response. - Add TLS Engine support. - Add support for ALPN on TLS connections. Closes [#924]. - Add `-D ` option for all clients to specify MQTT v5 properties. - Add `-E ` to `mosquitto_sub `, which causes it to exit immediately after having its subscriptions acknowledged. Use with `-c` to create a durable client session without requiring a message to be received. - Add `--remove-retained` to `mosquitto_sub`, which can be used to clear retained messages on a broker. - Add `--repeat` and `--repeat-delay` to `mosquitto_pub`, which can be used to repeat single message publishes at a regular interval. - -V now accepts `5`, `311`, `31`, as well as `mqttv5` etc. - Add explicit support for TLS v1.3. - Drop support for TLS v1.0. ## Broker fixes - Improve error reporting when creating listeners. - Fix `mosquitto_passwd` crashing on corrupt password file. Closes [#1207]. - Fix build on SmartOS due to missing IPV6_V6ONLY. Closes [#1212]. ## Client library fixes - Add missing `mosquitto_userdata()` function. ## Client fixes - `mosquitto_pub` wouldn't always publish all messages when using `-l` and QoS>0. This has been fixed. - `mosquitto_sub` was incorrectly encoding special characters when using %j output format. Closes [#1220]. [#924]: https://github.com/eclipse/mosquitto/issues/924 [#1208]: https://github.com/eclipse/mosquitto/issues/1208 [#1212]: https://github.com/eclipse/mosquitto/issues/1212 [#1220]: https://github.com/eclipse/mosquitto/issues/1220 mosquitto-2.0.18/www/posts/2019/06/000077500000000000000000000000001450213760600164665ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2019/06/version-1-6-3-released.md000066400000000000000000000073471450213760600227330ustar00rootroot00000000000000 This is a bugfix release. ## Broker - Fix detection of incoming v3.1/v3.1.1 bridges. Closes [#1263]. - Fix default `max_topic_alias` listener config not being copied to the in-use listener when compiled without TLS support. - Fix random number generation if compiling using `WITH_TLS=no` and on Linux with glibc >= 2.25. Without this fix, no random numbers would be generated for e.g. on broker client id generation, and so clients connecting expecting this feature would be unable to connect. - Fix compilation problem related to `getrandom()` on non-glibc systems. - Fix Will message for a persistent client incorrectly being sent when the client reconnects after a clean disconnect. Closes [#1273]. - Fix Will message for a persistent client not being sent on disconnect. Closes [#1273]. - Improve documentation around the upgrading of persistence files. Closes [#1276]. - Add 'extern "C"' on mosquitto_broker.h and mosquitto_plugin.h for C++ plugin writing. Closes [#1290]. - Fix persistent Websockets clients not receiving messages after they reconnect, having sent DISCONNECT on a previous session. Closes [#1227]. - Disable TLS renegotiation. Client initiated renegotiation is considered to be a potential attack vector against servers. Closes [#1257]. - Fix incorrect shared subscription topic '$shared'. - Fix zero length client ids being rejected for MQTT v5 clients with clean start set to true. - Fix MQTT v5 overlapping subscription behaviour. Clients now receive message from all matching subscriptions rather than the first one encountered, which ensures the maximum QoS requirement is met. - Fix incoming/outgoing quota problems for QoS>0. - Remove obsolete `store_clean_interval` from documentation. ## Client library - Fix typo causing build error on Windows when building without TLS support. Closes [#1264]. ## Clients - Fix -L url parsing when `/topic` part is missing. - Stop some error messages being printed even when `--quiet` was used. Closes [#1284]. - Fix `mosquitto_pub` exiting with error code 0 when an error occurred. Closes [#1285]. - Fix `mosquitto_pub` not using the `-c` option. Closes [#1273]. - Fix MQTT v5 clients not being able to specify a password without a username. Closes [#1274]. - Fix `mosquitto_pub -l` not handling network failures. Closes [#1152]. - Fix `mosquitto_pub -l` not handling zero length input. Closes [#1302]. - Fix double free on exit in `mosquitto_pub`. Closes [#1280]. ## Documentation: - Remove references to Python binding and C++ wrapper in libmosquitto man page. Closes [#1266]. ## Build - CLIENT_LDFLAGS now uses LDFLAGS. Closes [#1294]. [#1152]: https://github.com/eclipse/mosquitto/issues/1152 [#1227]: https://github.com/eclipse/mosquitto/issues/1227 [#1257]: https://github.com/eclipse/mosquitto/issues/1257 [#1263]: https://github.com/eclipse/mosquitto/issues/1263 [#1264]: https://github.com/eclipse/mosquitto/issues/1264 [#1266]: https://github.com/eclipse/mosquitto/issues/1266 [#1273]: https://github.com/eclipse/mosquitto/issues/1273 [#1273]: https://github.com/eclipse/mosquitto/issues/1273 [#1273]: https://github.com/eclipse/mosquitto/issues/1273 [#1274]: https://github.com/eclipse/mosquitto/issues/1274 [#1276]: https://github.com/eclipse/mosquitto/issues/1276 [#1280]: https://github.com/eclipse/mosquitto/issues/1280 [#1284]: https://github.com/eclipse/mosquitto/issues/1284 [#1285]: https://github.com/eclipse/mosquitto/issues/1285 [#1290]: https://github.com/eclipse/mosquitto/issues/1290 [#1294]: https://github.com/eclipse/mosquitto/issues/1294 [#1302]: https://github.com/eclipse/mosquitto/issues/1302 mosquitto-2.0.18/www/posts/2019/08/000077500000000000000000000000001450213760600164705ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2019/08/version-1-6-4-released.md000066400000000000000000000042031450213760600227220ustar00rootroot00000000000000 This is a bugfix release. ## Broker - Fix persistent clients being incorrectly expired on Raspberry Pis. Closes [#1272]. - Windows: Allow other applications access to the log file when running. Closes [#515]. - Fix incoming QoS 2 messages being blocked when `max_inflight_messages` was set to 1. Closes [#1332]. - Fix incoming messages not being removed for a client if the topic being published to does not have any subscribers. Closes [#1322]. ## Client library - Fix MQTT v5 subscription options being incorrectly set for MQTT v3 subscriptions. Closes [#1353]. - Make behaviour of `mosquitto_connect_async()` consistent with `mosquitto_connect()` when connecting to a non-existent server. Closes [#1345]. - `mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, ...)` was incorrectly returning `MOSQ_ERR_INVAL` with valid input. This has been fixed. Closes [#1360]. - `on_connect` callback is now called with the correct v5 reason code if a v5 client connects to a v3.x broker and is sent a CONNACK with the "unacceptable protocol version" connack reason code. - Fix memory leak when setting v5 properties in `mosquitto_connect_v5()`. - Fix properties not being sent on QoS>0 PUBLISH messages. ## Clients - `mosquitto_pub`: fix error codes not being returned when `mosquitto_pub` exits. Closes [#1354]. - All clients: improve error messages when connecting to a v3.x broker when in v5 mode. Closes [#1344]. ## Other - Various documentation fixes. [#515]: https://github.com/eclipse/mosquitto/issues/515 [#1272]: https://github.com/eclipse/mosquitto/issues/1272 [#1322]: https://github.com/eclipse/mosquitto/issues/1322 [#1332]: https://github.com/eclipse/mosquitto/issues/1332 [#1344]: https://github.com/eclipse/mosquitto/issues/1344 [#1345]: https://github.com/eclipse/mosquitto/issues/1345 [#1353]: https://github.com/eclipse/mosquitto/issues/1353 [#1354]: https://github.com/eclipse/mosquitto/issues/1354 [#1360]: https://github.com/eclipse/mosquitto/issues/1360 mosquitto-2.0.18/www/posts/2019/09/000077500000000000000000000000001450213760600164715ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2019/09/version-1-6-5-released.md000066400000000000000000000054471450213760600227370ustar00rootroot00000000000000 This is a bugfix release. # Compatibility - The most recent version of libwebsockets (3.2.0) changed its behaviour and is not compatible with Mosquitto. This has been fixed for the next libwebsockets release. The 1.6.5 release refuses to compile with libwebsockets 3.2.0. All previous versions of Mosquitto that use websockets are affected by the change in behaviour. # Broker - Fix v5 DISCONNECT packets with remaining length == 2 being treated as a protocol error. Closes [#1367]. - Fix support for libwebsockets 3.x (excluding 3.2.0) - Fix slow websockets performance when sending large messages. Closes [#1390]. - Fix bridges potentially not connecting on Windows. Closes [#478]. - Fix clients authorised using `use_identity_as_username` or `use_subject_as_username` being disconnected on SIGHUP. Closes [#1402]. - Improve error messages in some situations when clients disconnect. Reduces the number of "Socket error on client X, disconnecting" messages. - Fix Will for v5 clients not being sent if will delay interval was greater than the session expiry interval. Closes [#1401]. - Fix CRL file not being reloaded on HUP. Closes [#35]. - Fix repeated "Error in poll" messages on Windows when only websockets listeners are defined. Closes [#1391]. # Client library - Fix reconnect backoff for the situation where connections are dropped rather than refused. Closes [#737]. - Fix missing locks on `mosq->state`. Closes [#1374]. # Documentation - Improve details on global/per listener options in the mosquitto.conf man page. Closes [#274]. - Clarify behaviour when clients exceed the `message_size_limit`. Closes [#448]. - Improve documentation for `max_inflight_bytes`, `max_inflight_messages`, and `max_queued_messages`. # Build - Fix missing function warnings on NetBSD. - Fix `WITH_STATIC_LIBRARIES` using CMake on Windows. Closes [#1369]. - Guard `ssize_t` definition on Windows. Closes [#522]. [#35]: https://github.com/eclipse/mosquitto/issues/35 [#274]: https://github.com/eclipse/mosquitto/issues/274 [#448]: https://github.com/eclipse/mosquitto/issues/448 [#478]: https://github.com/eclipse/mosquitto/issues/478 [#522]: https://github.com/eclipse/mosquitto/issues/522 [#737]: https://github.com/eclipse/mosquitto/issues/737 [#1367]: https://github.com/eclipse/mosquitto/issues/1367 [#1369]: https://github.com/eclipse/mosquitto/issues/1369 [#1374]: https://github.com/eclipse/mosquitto/issues/1374 [#1390]: https://github.com/eclipse/mosquitto/issues/1390 [#1391]: https://github.com/eclipse/mosquitto/issues/1391 [#1401]: https://github.com/eclipse/mosquitto/issues/1401 [#1402]: https://github.com/eclipse/mosquitto/issues/1402 mosquitto-2.0.18/www/posts/2019/09/version-1-6-6-released.md000066400000000000000000000042251450213760600227310ustar00rootroot00000000000000 Mosquitto 1.6.6 and 1.5.9 have been released to address two security vulnerabilities. Titles and links will be updated once the CVE numbers are assigned. # CVE-2019-11779 A vulnerability exists in Mosquitto versions 1.5 to 1.6.5 inclusive, known as [CVE-2019-11779]. If a client sends a SUBSCRIBE packet containing a topic that consists of approximately 65400 or more '/' characters, i.e. the topic hierarchy separator, then a stack overflow will occur. The issue is fixed in Mosquitto 1.6.6 and 1.5.9. Patches for older versions are available at The fix addresses the problem by restricting the allowed number of topic hierarchy levels to 200. An alternative fix is to increase the size of the stack by a small amount. # CVE-2019-11778 A vulnerability exists in Mosquitto version 1.6 to 1.6.4 inclusive, known as [CVE-2019-11778] If an MQTT v5 client connects to Mosquitto, sets a last will and testament, sets a will delay interval, sets a session expiry interval, and the will delay interval is set longer than the session expiry interval, then a use after free error occurs, which has the potential to cause a crash in some situations. The issue is fixed in Mosquitto 1.6.5. Patches for older versions are available at # Version 1.6.6 Changes The complete list of fixes addressed in version 1.6.6 is: ## Security * Restrict topic hierarchy to 200 levels to prevent possible stack overflow. Closes [#1412]. ## Broker * Restrict topic hierarchy to 200 levels to prevent possible stack overflow. Closes [#1412]. * `mosquitto_passwd` now returns 1 when attempting to update a user that does not exist. Closes [#1414]. [CVE-2019-11778]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11778 [CVE-2019-11779]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2019-11779 [#1412]: https://github.com/eclipse/mosquitto/issues/1412 [#1414]: https://github.com/eclipse/mosquitto/issues/1414 mosquitto-2.0.18/www/posts/2019/09/version-1-6-7-released.md000066400000000000000000000022771450213760600227370ustar00rootroot00000000000000 Mosquitto 1.6.7 has been released, this is a bugfix release. # Broker - Add workaround for working with libwebsockets 3.2.0. - Fix potential crash when reloading config. Closes [#1424], [#1425]. # Client library - Don't use `/` in autogenerated client ids, to avoid confusing with topics. - Fix `mosquitto_max_inflight_messages_set()` and `mosquitto_int_option(..., MOSQ_OPT_*_MAX, ...)` behaviour. Closes [#1417]. - Fix regression on use of `mosquitto_connect_async()` not working. Closes [#1415] and [#1422]. # Clients - mosquitto_sub: Fix `-E` incorrectly not working unless `-d` was also specified. [Closes #1418]. - Updated documentation around automatic client ids. [#1415]: https://github.com/eclipse/mosquitto/issues/1415 [#1417]: https://github.com/eclipse/mosquitto/issues/1417 [#1418]: https://github.com/eclipse/mosquitto/issues/1418 [#1422]: https://github.com/eclipse/mosquitto/issues/1422 [#1424]: https://github.com/eclipse/mosquitto/issues/1424 [#1425]: https://github.com/eclipse/mosquitto/issues/1425 mosquitto-2.0.18/www/posts/2019/11/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2019/11/version-1-6-8-released.md000066400000000000000000000064371450213760600227330ustar00rootroot00000000000000 Mosquitto 1.6.8 has been released, this is a bugfix release. # Broker - Various fixes for `allow_zero_length_clientid` config, where this option was not being set correctly. Closes [#1429]. - Fix incorrect memory tracking causing problems with `memory_limit` option. Closes [#1437]. - Fix subscription topics being limited to 200 characters instead of 200 hierarchy levels. Closes [#1441]. - Only a single CRL could be loaded at once. This has been fixed. Closes [#1442]. - Fix problems with reloading config when `per_listener_settings` was true. Closes [#1459]. - Fix retained messages with an expiry interval not being expired after being restored from persistence. Closes [#1464]. - Fix messages with an expiry interval being sent without an expiry interval property just before they were expired. Closes [#1464]. - Fix TLS Websockets clients not receiving messages after taking over a previous connection. Closes [#1489]. - Fix MQTT 3.1.1 clients using clean session false, or MQTT 5.0 clients using session-expiry-interval set to infinity never expiring, even when the global `persistent_client_expiration` option was set. Closes [#1494]. # Client library - Fix publish properties not being passed to `on_message_v5()` callback for QoS 2 messages. Closes [#1432]. - Fix documentation issues in mosquitto.h. Closes [#1478]. - Document `mosquitto_connect_srv()`. Closes [#1499]. # Clients - Fix duplicate cfg definition in rr_client. Closes [#1453]. - Fix `mosquitto_pub -l` hang when stdin stream ends. Closes [#1448]. - Fix `mosquitto_pub -l` not sending the final line of stdin if it does not end with a new line. Closes [#1473]. - Make documentation for `mosquitto_pub -l` match reality - blank lines are sent as empty messages. Closes [#1474]. - Free memory in `mosquitto_sub` when quiting without having made a successful connection. Closes [#1513]. # Build - Added `CLIENT_STATIC_LDADD` to makefile builds to allow more libraries to be linked when compiling the clients with a static libmosquitto, as required for e.g. openssl on some systems. # Installer - Fix `mosquitto_rr.exe` not being included in Windows installers. Closes [#1463]. [#1429]: https://github.com/eclipse/mosquitto/issues/1429 [#1432]: https://github.com/eclipse/mosquitto/issues/1432 [#1437]: https://github.com/eclipse/mosquitto/issues/1437 [#1441]: https://github.com/eclipse/mosquitto/issues/1441 [#1442]: https://github.com/eclipse/mosquitto/issues/1442 [#1448]: https://github.com/eclipse/mosquitto/issues/1448 [#1453]: https://github.com/eclipse/mosquitto/issues/1453 [#1459]: https://github.com/eclipse/mosquitto/issues/1459 [#1463]: https://github.com/eclipse/mosquitto/issues/1463 [#1464]: https://github.com/eclipse/mosquitto/issues/1464 [#1473]: https://github.com/eclipse/mosquitto/issues/1473 [#1474]: https://github.com/eclipse/mosquitto/issues/1474 [#1478]: https://github.com/eclipse/mosquitto/issues/1478 [#1489]: https://github.com/eclipse/mosquitto/issues/1489 [#1494]: https://github.com/eclipse/mosquitto/issues/1494 [#1499]: https://github.com/eclipse/mosquitto/issues/1499 [#1513]: https://github.com/eclipse/mosquitto/issues/1513 mosquitto-2.0.18/www/posts/2020/000077500000000000000000000000001450213760600162315ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2020/02/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2020/02/version-1-6-9-released.md000066400000000000000000000047151450213760600227210ustar00rootroot00000000000000 Mosquitto 1.6.9 has been released, this is a bugfix release. # Broker - Fix session expiry with very large expiry intervals. Closes [#1525]. - Check ACL patterns for validity when loading. Closes [#1539]. - Use presence of password file as indicator for whether username checks should take place, not whether usernames are defined in the password file. Closes [#1545]. - Strip whitespace from end of config file string options. Closes [#1566]. - Satisfy valgrind when exiting on error due to not being able to open a listening socket, by calling freeaddrinfo. Closes [#1565]. - Fix `config->user` not being freed on exit. Closes [#1564]. - Fix trailing whitespace not being trimmed on acl users. Closes [#1539]. - Fix `bind_interface` not working for the default listener. Closes [#1533]. - Improve password file parsing in the broker and `mosquitto_passwd`. Closes [#1584]. - Print OpenSSL errors in more situations, like when loading certificates fails. Closes [#1552]. - Fix `mosquitto_client_protocol()` returning incorrect values. # Client library - Set minimum keepalive argument to `mosquitto_connect*()` to be 5 seconds. Closes [#1550]. - Fix `mosquitto_topic_matches_sub()` not returning `MOSQ_ERR_INVAL` if the topic contains a wildcard. Closes [#1589]. # Clients - Fix `--remove-retained` not obeying the `-T` option for filtering out topics. Closes [#1585]. - Default behaviour for v5 clients using `-c` is now to use infinite length sessions, as with v3 clients. Closes [#1546]. [#1525]: https://github.com/eclipse/mosquitto/issues/1525 [#1533]: https://github.com/eclipse/mosquitto/issues/1533 [#1539]: https://github.com/eclipse/mosquitto/issues/1539 [#1539]: https://github.com/eclipse/mosquitto/issues/1539 [#1545]: https://github.com/eclipse/mosquitto/issues/1545 [#1546]: https://github.com/eclipse/mosquitto/issues/1546 [#1550]: https://github.com/eclipse/mosquitto/issues/1550 [#1552]: https://github.com/eclipse/mosquitto/issues/1552 [#1564]: https://github.com/eclipse/mosquitto/issues/1564 [#1565]: https://github.com/eclipse/mosquitto/issues/1565 [#1566]: https://github.com/eclipse/mosquitto/issues/1566 [#1584]: https://github.com/eclipse/mosquitto/issues/1584 [#1585]: https://github.com/eclipse/mosquitto/issues/1585 [#1589]: https://github.com/eclipse/mosquitto/issues/1589 mosquitto-2.0.18/www/posts/2020/05/000077500000000000000000000000001450213760600164555ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2020/05/version-1-6-10-released.md000066400000000000000000000042011450213760600227620ustar00rootroot00000000000000 Mosquitto 1.6.10 has been released, this is a bugfix release. # Broker - Report invalid bridge prefix+pattern combinations at config parsing time rather than letting the bridge fail later. Issue [#1635]. - Fix `mosquitto_passwd -b` not updating passwords for existing users correctly. Creating a new user with `-b` worked without problem. Closes [#1664]. - Fix memory leak when connecting clients rejected. - Don't disconnect clients that are already disconnected. This prevents the session expiry being extended on SIGHUP. Closes [#1521]. - Fix support for openssl 3.0. - Fix check when loading persistence file of a different version than the native version. Closes [#1684]. - Fix possible assert crash associated with bridge reconnecting when compiled without epoll support. Closes [#1700]. # Client library - Don't treat an unexpected PUBACK, PUBREL, or PUBCOMP as a fatal error. Issue [#1629]. - Fix support for openssl 3.0. - Fix memory leaks from multiple calls to `mosquitto_lib_init()`/`mosquitto_lib_cleanup()`. Closes [#1691]. - Fix documentation on return code of `mosquitto_lib_init()` for Windows. Closes [#1690]. # Clients - Fix mosquitto_sub `%j` or `%J` not working on Windows. Closes [#1674]. # Build - Various fixes for building with below C99 support. Closes [#1622]. - Fix use of sed on BSD. Closes [#1614]. [#1521]: https://github.com/eclipse/mosquitto/issues/1521 [#1614]: https://github.com/eclipse/mosquitto/issues/1614 [#1622]: https://github.com/eclipse/mosquitto/issues/1622 [#1629]: https://github.com/eclipse/mosquitto/issues/1629 [#1635]: https://github.com/eclipse/mosquitto/issues/1635 [#1664]: https://github.com/eclipse/mosquitto/issues/1664 [#1674]: https://github.com/eclipse/mosquitto/issues/1674 [#1684]: https://github.com/eclipse/mosquitto/issues/1684 [#1690]: https://github.com/eclipse/mosquitto/issues/1690 [#1691]: https://github.com/eclipse/mosquitto/issues/1691 [#1700]: https://github.com/eclipse/mosquitto/issues/1700 mosquitto-2.0.18/www/posts/2020/06/000077500000000000000000000000001450213760600164565ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2020/06/mosquitto-ubuntu-appliance.md000066400000000000000000000013441450213760600243200ustar00rootroot00000000000000 Ubuntu 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. There are five different applications in the first set of appliances, and Mosquitto is one of them. Read more at the [Ubuntu blog]. [Ubuntu Appliance]: http://ubuntu.com/appliance [Ubuntu blog]: https://ubuntu.com/blog/the-ubuntu-appliance-portfolio mosquitto-2.0.18/www/posts/2020/06/test-mosquitto-org-cert-updated.md000066400000000000000000000012651450213760600251710ustar00rootroot00000000000000 The CA certificate and server certificate for the broker running at [test.mosquitto.org] has been updated to use a stronger key. This means that if you have downloaded the CA certificate, you will need to do so again. Likewise, if you have used the [client certificate generator] then your certificate will no longer be accepted and you must generate a new one. [test.mosquitto.org]: https://test.mosquitto.org [client certificate generator]: https://test.mosquitto.org/ssl/ mosquitto-2.0.18/www/posts/2020/08/000077500000000000000000000000001450213760600164605ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2020/08/version-1-6-11-released.md000066400000000000000000000062761450213760600230040ustar00rootroot00000000000000 Mosquitto 1.6.11 has been released, this is a bugfix release. # Security - On Windows the Mosquitto service was being installed without appropriate path quoting, this has been fixed. Closes [#565671]. # Broker - Fix usage message only mentioning v3.1.1. Closes [#1713]. - Fix broker refusing to start if only websockets listeners were defined. Closes [#1740]. - Change systemd unit files to create /var/log/mosquitto before starting. Closes [#821]. - Don't quit with an error if opening the log file isn't possible. Closes [#821]. - Fix bridge topic remapping when using "" as the topic. Closes [#1749]. - Fix messages being queued for disconnected bridges when clean start was set to true. Closes [#1729]. - Fix `autosave_interval` not being triggered by messages being delivered. Closes [#1726]. - Fix websockets clients sometimes not being disconnected promptly. Closes [#1718]. - Fix "slow" file based logging by switching to line based buffering. Closes [#1689]. Closes [#1741]. - Log protocol error message where appropriate from a bad UNSUBSCRIBE, rather than the generic "socket error". - Don't try to start DLT logging if DLT unavailable, to avoid a long delay when shutting down the broker. Closes [#1735]. - Fix potential memory leaks. Closes [#1773]. Closes [#1774]. - Fix clients not receiving messages after a previous client with the same client ID and positive will delay interval quit. Closes [#1752]. - Fix overly broad `HAVE_PTHREAD_CANCEL` compile guard. Closes [#1547]. # Client library - Improved documentation around connect callback return codes. Close [#1730]. - Fix `mosquitto_publish*()` no longer returning `MOSQ_ERR_NO_CONN` when not connected. Closes [#1725]. - `mosquitto_loop_start()` now sets a thread name on Linux, FreeBSD, NetBSD, and OpenBSD. Closes [#1777]. - Fix `mosquitto_loop_stop()` not stopping on Windows. Closes [#1748]. Closes [#117]. [#117]: https://github.com/eclipse/mosquitto/issues/117 [#821]: https://github.com/eclipse/mosquitto/issues/821 [#1547]: https://github.com/eclipse/mosquitto/issues/1547 [#1689]: https://github.com/eclipse/mosquitto/issues/1689 [#1713]: https://github.com/eclipse/mosquitto/issues/1713 [#1718]: https://github.com/eclipse/mosquitto/issues/1718 [#1725]: https://github.com/eclipse/mosquitto/issues/1725 [#1726]: https://github.com/eclipse/mosquitto/issues/1726 [#1729]: https://github.com/eclipse/mosquitto/issues/1729 [#1730]: https://github.com/eclipse/mosquitto/issues/1730 [#1735]: https://github.com/eclipse/mosquitto/issues/1735 [#1740]: https://github.com/eclipse/mosquitto/issues/1740 [#1741]: https://github.com/eclipse/mosquitto/issues/1741 [#1748]: https://github.com/eclipse/mosquitto/issues/1748 [#1749]: https://github.com/eclipse/mosquitto/issues/1749 [#1752]: https://github.com/eclipse/mosquitto/issues/1752 [#1773]: https://github.com/eclipse/mosquitto/issues/1773 [#1774]: https://github.com/eclipse/mosquitto/issues/1774 [#1777]: https://github.com/eclipse/mosquitto/issues/1777 [#565671]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=565671 mosquitto-2.0.18/www/posts/2020/08/version-1-6-12-released.md000066400000000000000000000023271450213760600227760ustar00rootroot00000000000000 Mosquitto 1.6.12 and 1.5.10 have been released. # Security - In some circumstances, Mosquitto could leak memory when handling PUBLISH messages. This is limited to incoming QoS 2 messages, and is related to the combination of the broker having persistence enabled, a clean session=false client, which was connected prior to the broker restarting, then has reconnected and has now sent messages at a sufficiently high rate that the incoming queue at the broker has filled up and hence messages are being dropped. This is more likely to have an effect where `max_queued_messages` is a small value. This has now been fixed. Closes [#1793]. The following fixes apply to 1.6.12 only. # Broker - Build warning fixes when building with `WITH_BRIDGE=no` and `WITH_TLS=no`. # Clients - All clients exit with an error exit code on CONNACK failure. Closes [#1778]. - Don't busy loop with `mosquitto_pub -l` on a slow connection. [#1778]: https://github.com/eclipse/mosquitto/issues/1778 [#1793]: https://github.com/eclipse/mosquitto/issues/1793 mosquitto-2.0.18/www/posts/2020/12/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2020/12/version-2-0-0-released.md000066400000000000000000000321001450213760600226710ustar00rootroot00000000000000 The Mosquitto project is happy to announce the release of version 2.0! This is a big change with breaking behaviour changes in the broker. Users, packages and plugin authors should read [migrating from 1.x to 2.0] to help with the changes. # Noteworthy changes Mosquitto is now more secure by default and requires users to take an active decision in how they configure security on their broker, instead of possibly relying on the older very permissive behaviour, as well as dropping privileged access more quickly. More details are in [migrating from 1.x to 2.0]. A new plugin interface has been introduced which goes beyond the existing authentication and access control plugin interface to offer more plugin capabilities, whilst being easier to develop for and easier to extend. More details will follow. Existing plugins are still supported, although plugin authors should look at [migrating from 1.x to 2.0] to ensure their plugins remain compatible when compiled against Mosquitto 2.0 headers. A new plugin has been introduced to provide client, group, and role based authentication and access control. The plugin configuration is managed over special topics and can be updated on the fly. It provides a flexible and straightforward means of configuring access to your broker. For more information, see [Dynamic Security plugin]. The broker performance has been improved, particularly for higher numbers of clients. We plan to run show some benchmarks to show the improvement. A new utility, `mosquitto_ctrl` has been added for controlling aspects of a running broker. At the present this is limited to controlling the dynamic security plugin, but will be extended to other features in later releases. Bridges now support MQTT v5. The mosquitto command line clients have received a variety of small improvements. mosquitto_sub can now format its output in fixed column widths, for example, and filter its output randomly so you can keep an eye on the overall behaviour of a topic without having to see every message, for example. # Breaking changes - When the Mosquitto broker is run without configuring any listeners it will now bind to the loopback interfaces 127.0.0.1 and/or ::1. This means that only connections from the local host will be possible. Running the broker as `mosquitto` or `mosquitto -p 1883` will bind to the loopback interface. Running the broker with a configuration file with no listeners configured will bind to the loopback interface with port 1883. Running the broker with a listener defined will bind by default to `0.0.0.0` / `::` and so will be accessible from any interface. It is still possible to bind to a specific address/interface. If the broker is run as `mosquitto -c mosquitto.conf -p 1884`, and a listener is defined in the configuration file, then the port defined on the command line will be IGNORED, and no listener configured for it. - All listeners now default to `allow_anonymous false` unless explicitly set to true in the configuration file. This means that when configuring a listener the user must either configure an authentication and access control method, or set `allow_anonymous true`. When the broker is run without a configured listener, and so binds to the loopback interface, anonymous connections are allowed. - If Mosquitto is run on as root on a unix like system, it will attempt to drop privileges as soon as the configuration file has been read. This is in contrast to the previous behaviour where elevated privileges were only dropped after listeners had been started (and hence TLS certificates loaded) and logging had been started. The change means that clients will never be able to connect to the broker when it is running as root, unless the user explicitly sets it to run as root, which is not advised. It also means that all locations that the broker needs to access must be available to the unprivileged user. In particular those people using TLS certificates from Lets Encrypt will need to do something to allow Mosquitto to access those certificates. An example deploy renewal hook script to help with this is at `misc/letsencrypt/mosquitto-copy.sh`. The user that Mosquitto will change to are the one provided in the configuration, `mosquitto`, or `nobody`, in order of availability. - The `pid_file` option will now always attempt to write a pid file, regardless of whether the `-d` argument is used when running the broker. - The `tls_version` option now defines the *minimum* TLS protocol version to be used, rather than the exact version. Closes [#1258]. - The `max_queued_messages` option has been increased from 100 to 1000 by default, and now also applies to QoS 0 messages, when a client is connected. - The mosquitto_sub, mosquitto_pub, and mosquitto_rr clients will now load OS provided CA certificates by default if `-L mqtts://...` is used, or if the port is set to 8883 and no other CA certificates are loaded. - Minimum support libwebsockets version is now 2.4.0 # Broker features - New plugin interface which is more flexible, easier to develop for and easier to extend. - New dynamic security plugin, which allows clients, groups, and roles to be defined and updated as the broker is running. - Performance improvements, particularly for higher numbers of clients. - When running as root, if dropping privileges to the "mosquitto" user fails, then try "nobody" instead. This reduces the burden on users installing Mosquitto themselves. - Add support for Unix domain socket listeners. - Add `bridge_outgoing_retain` option, to allow outgoing messages from a bridge to have the retain bit completely disabled, which is useful when bridging to e.g. Amazon or Google. - Add support for MQTT v5 bridges to handle the "retain-available" property being false. - Allow MQTT v5.0 outgoing bridges to fall back to MQTT v3.1.1 if connecting to a v3.x only broker. - DLT logging is now configurable at runtime with `log_dest dlt`. Closes [#1735]. - Add `mosquitto_plugin_publish()` function, which can be used by plugins to publish messages. - Add `mosquitto_client_protocol_version()` function which can be used by plugins to determine which version of MQTT a client has connected with. - Add `mosquitto_kick_client_by_clientid()` and `mosquitto_kick_client_by_username()` functions, which can be used by plugins to disconnect clients. - Add support for handling $CONTROL/ topics in plugins. - Add support for PBKDF2-SHA512 password hashing. - Enabling certificate based TLS encryption is now through certfile and keyfile, not capath or cafile. - Added support for controlling UNSUBSCRIBE calls in v5 plugin ACL checks. - Add "deny" acl type. Closes [#1611]. - The broker now sends the receive-maximum property for MQTT v5 CONNACKs. - Add the `bridge_max_packet_size` option. Closes [#265]. - Add the `bridge_bind_address` option. Closes [#1311]. - TLS certificates for the server are now reloaded on SIGHUP. - Default for max_queued_messages has been changed to 1000. - Add `ciphers_tls1.3` option, to allow setting TLS v1.3 ciphersuites. Closes [#1825]. - Bridges now obey MQTT v5 server-keepalive. - Add bridge support for the MQTT v5 maximum-qos property. - Log client port on new connections. Closes [#1911]. # Broker fixes - Send DISCONNECT with `malformed-packet` reason code on invalid PUBLISH, SUBSCRIBE, and UNSUBSCRIBE packets. - Document that X509_free() must be called after using mosquitto_client_certificate(). Closes [#1842]. - Fix listener not being reassociated with client when reloading a persistence file and `per_listener_settings true` is set and the client did not set a username. Closes [#1891]. - Fix bridge sock not being removed from sock hash on error. Closes [#1897]. - mosquitto_password now forbids the : character. Closes [#1833]. - Fix `log_timestamp_format` not applying to `log_dest topic`. Closes [#1862]. - Fix crash on Windows if loading a plugin fails. Closes [#1866]. - Fix file logging on Windows. Closes [#1880]. - Report an error if the config file is set to a directory. Closes [#1814]. - Fix bridges incorrectly setting Wills to manage remote notifications when `notifications_local_only` was set true. Closes [#1902]. # Client library features - Client no longer generates random client ids for v3.1.1 clients, these are now expected to be generated on the broker. This matches the behaviour for v5 clients. Closes [#291]. - Add support for connecting to brokers through Unix domain sockets. - Add `mosquitto_property_identifier()`, for retrieving the identifier integer for a property. - Add `mosquitto_property_identifier_to_string()` for converting a property identifier integer to the corresponding property name string. - Add `mosquitto_property_next()` to retrieve the next property in a list, for iterating over property lists. - mosquitto_pub now handles the MQTT v5 retain-available property by never setting the retain bit. - Added MOSQ_OPT_TCP_NODELAY, to allow disabling Nagle's algorithm on client sockets. Closes [#1526]. - Add `mosquitto_ssl_get()` to allow clients to access their SSL structure and perform additional verification. - Add MOSQ_OPT_BIND_ADDRESS to allow setting of a bind address independently of the `mosquitto_connect*()` call. - Add `MOSQ_OPT_TLS_USE_OS_CERTS` option, to instruct the client to load and trust OS provided CA certificates for use with TLS connections. # Client library fixes - Fix send quota being incorrecly reset on reconnect. Closes [#1822]. - Don't use logging until log mutex is initialised. Closes [#1819]. - Fix missing mach/mach_time.h header on OS X. Closes [#1831]. - Fix connect properties not being sent when the client automatically reconnects. Closes [#1846]. # Client features - Add timeout return code (27) for `mosquitto_sub -W ` and `mosquitto_rr -W `. Closes [#275]. - Add support for connecting to brokers through Unix domain sockets with the `--unix` argument. - Use cJSON library for producing JSON output, where available. Closes [#1222]. - Add support for outputting MQTT v5 property information to mosquitto_sub/rr JSON output. Closes [#1416]. - Add `--pretty` option to mosquitto_sub/rr for formatted/unformatted JSON output. - Add support for v5 property printing to mosquitto_sub/rr in non-JSON mode. Closes [#1416]. - Add `--nodelay` to all clients to allow them to use the MOSQ_OPT_TCP_NODELAY option. - Add `-x` to all clients to all the session-expiry-interval property to be easily set for MQTT v5 clients. - Add `--random-filter` to mosquitto_sub, to allow only a certain proportion of received messages to be printed. - mosquitto_sub %j and %J timestamps are now in a ISO 8601 compatible format. - mosquitto_sub now supports extra format specifiers for field width and precision for some parameters. - Add `--version` for all clients. - All clients now load OS provided CA certificates if used with `-L mqtts://...`, or if port is set to 8883 and no other CA certificates are used. Closes [#1824]. - Add the `--tls-use-os-certs` option to all clients. # Client fixes - mosquitto_sub will now exit if all subscriptions were denied. - mosquitto_pub now sends 0 length files without an error when using `-f`. - Fix description of `-e` and `-t` arguments in mosquitto_rr. Closes [#1881]. - mosquitto_sub will now quit with an error if the %U option is used on Windows, rather than just quitting. Closes [#1908]. [migrating from 1.x to 2.0]:/documentation/migrating-to-2-0/ [#265]: https://github.com/eclipse/mosquitto/issues/265 [#275]: https://github.com/eclipse/mosquitto/issues/275 [#291]: https://github.com/eclipse/mosquitto/issues/291 [#1222]: https://github.com/eclipse/mosquitto/issues/1222 [#1258]: https://github.com/eclipse/mosquitto/issues/1258 [#1311]: https://github.com/eclipse/mosquitto/issues/1311 [#1416]: https://github.com/eclipse/mosquitto/issues/1416 [#1526]: https://github.com/eclipse/mosquitto/issues/1526 [#1611]: https://github.com/eclipse/mosquitto/issues/1611 [#1735]: https://github.com/eclipse/mosquitto/issues/1735 [#1814]: https://github.com/eclipse/mosquitto/issues/1814 [#1819]: https://github.com/eclipse/mosquitto/issues/1819 [#1822]: https://github.com/eclipse/mosquitto/issues/1822 [#1824]: https://github.com/eclipse/mosquitto/issues/1824 [#1825]: https://github.com/eclipse/mosquitto/issues/1825 [#1831]: https://github.com/eclipse/mosquitto/issues/1831 [#1833]: https://github.com/eclipse/mosquitto/issues/1833 [#1842]: https://github.com/eclipse/mosquitto/issues/1842 [#1846]: https://github.com/eclipse/mosquitto/issues/1846 [#1862]: https://github.com/eclipse/mosquitto/issues/1862 [#1866]: https://github.com/eclipse/mosquitto/issues/1866 [#1880]: https://github.com/eclipse/mosquitto/issues/1880 [#1881]: https://github.com/eclipse/mosquitto/issues/1881 [#1891]: https://github.com/eclipse/mosquitto/issues/1891 [#1897]: https://github.com/eclipse/mosquitto/issues/1897 [#1902]: https://github.com/eclipse/mosquitto/issues/1902 [#1908]: https://github.com/eclipse/mosquitto/issues/1908 [#1911]: https://github.com/eclipse/mosquitto/issues/1911 mosquitto-2.0.18/www/posts/2020/12/version-2-0-2-released.md000066400000000000000000000024431450213760600227020ustar00rootroot00000000000000 Version 2.0.2 and 2.0.1 of Mosquitto has been released. These are bugfix releases. Version 2.0.2 fixes a build regression introduced in 2.0.1 when websockets support was enabled on non-Linux systems. The 2.0.1 changes are below. # Broker - Fix websockets connections on Windows blocking subsequent connections. Closes [#1934]. - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes [#1925]. Closes [#1476]. - Fix websockets listeners not causing the main loop not to wake up. Closes [#1936]. # Client library - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes [#1925]. Closes [#1476]. # Apps - Fix `mosquitto_passwd -U` # Build - Fix cjson include paths. - Fix build using WITH_TLS=no when the openssl headers aren't available. - Distribute cmake/ and snap/ directories in tar. [#1476]: https://github.com/eclipse/mosquitto/issues/1476 [#1925]: https://github.com/eclipse/mosquitto/issues/1925 [#1934]: https://github.com/eclipse/mosquitto/issues/1934 [#1936]: https://github.com/eclipse/mosquitto/issues/1936 mosquitto-2.0.18/www/posts/2020/12/version-2-0-3-released.md000066400000000000000000000034771450213760600227130ustar00rootroot00000000000000 Version 2.0.3 of Mosquitto has been released. This is a bugfix release. # Security - Running mosquitto_passwd with the following arguments only `mosquitto_passwd -b password_file username password` would cause the username to be used as the password. # Broker - Fix excessive CPU use on non-Linux systems when the open file limit is set high. Closes [#1947]. - Fix LWT not being sent on client takeover when the existing session wasn't being continued. Closes [#1946]. - Fix bridges possibly not completing connections when WITH_ADNS is in use. Closes [#1960]. - Fix QoS 0 messages not being delivered if max_queued_messages was set to 0. Closes [#1956]. - Fix local bridges being disconnected on SIGHUP. Closes [#1942]. - Fix slow initial bridge connections for WITH_ADNS=no. # Clients - Fix mosquitto_sub being unable to terminate with Ctrl-C if a successful connection is not made. Closes [#1957]. # Apps - Fix `mosquitto_passwd -b` using username as password (not if `-c` is also used). Closes [#1949]. # Build - Fix `install` target when using WITH_CJSON=no. Closes [#1938]. - Fix `generic` docker build. Closes [#1945]. [#1938]: https://github.com/eclipse/mosquitto/issues/1938 [#1942]: https://github.com/eclipse/mosquitto/issues/1942 [#1945]: https://github.com/eclipse/mosquitto/issues/1945 [#1946]: https://github.com/eclipse/mosquitto/issues/1946 [#1947]: https://github.com/eclipse/mosquitto/issues/1947 [#1949]: https://github.com/eclipse/mosquitto/issues/1949 [#1956]: https://github.com/eclipse/mosquitto/issues/1956 [#1957]: https://github.com/eclipse/mosquitto/issues/1957 [#1960]: https://github.com/eclipse/mosquitto/issues/1960 mosquitto-2.0.18/www/posts/2020/12/version-2-0-4-released.md000066400000000000000000000021011450213760600226730ustar00rootroot00000000000000 Version 2.0.4 of Mosquitto has been released. This is a bugfix release. # Broker - Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2 messages. Closes [#1968]. - `mosquitto_connect_bind_async()` and `mosquitto_connect_bind_v5()` should not reset the bind address option if called with `bind_address == NULL`. - Fix dynamic security configuration possibly not being reloaded on Windows only. Closes [#1962]. - Add more log messages for dynsec load/save error conditions. - Fix websockets connections blocking non-websockets connections on Windows. Closes [#1934]. # Build - Fix man pages not being built when using CMake. Closes [#1969]. [#1934]: https://github.com/eclipse/mosquitto/issues/1934 [#1962]: https://github.com/eclipse/mosquitto/issues/1962 [#1968]: https://github.com/eclipse/mosquitto/issues/1968 [#1969]: https://github.com/eclipse/mosquitto/issues/1969 mosquitto-2.0.18/www/posts/2021/000077500000000000000000000000001450213760600162325ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/01/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/01/version-2-0-5-released.md000066400000000000000000000043241450213760600227040ustar00rootroot00000000000000 Version 2.0.5 of Mosquitto has been released. This is a bugfix release. # Broker - Fix `auth_method` not being provided to the extended auth plugin event. Closes [#1975]. - Fix large packets not being completely published to slow clients. Closes [#1977]. - Fix bridge connection not relinquishing POLLOUT after messages are sent. Closes [#1979]. - Fix apparmor incorrectly denying access to /var/lib/mosquitto/mosquitto.db.new. Closes [#1978]. - Fix potential intermittent initial bridge connections when using poll(). - Fix `bind_interface` option. Closes [#1999]. - Fix invalid behaviour in dynsec plugin if a group or client is deleted before a role that was attached to the group or client is deleted. Closes [#1998]. - Improve logging in dynsec addGroupRole command. Closes [#2005]. - Improve logging in dynsec addGroupClient command. Closes [#2008]. # Client library - Improve documentation around the `_v5()` and non-v5 functions, e.g. `mosquitto_publish()` and `mosquitto_publish_v5(). # Build - `install` Makefile target should depend on `all`, not `mosquitto`, to ensure that man pages are always built. Closes [#1989]. - Fixes for lots of minor build warnings highlighted by Visual Studio. # Apps - Disallow control characters in mosquitto_passwd usernames. - Fix incorrect description in mosquitto_ctrl man page. Closes [#1995]. - Fix `mosquitto_ctrl dynsec getGroup` not showing roles. Closes [#1997]. [#1975]: https://github.com/eclipse/mosquitto/issues/1975 [#1977]: https://github.com/eclipse/mosquitto/issues/1977 [#1978]: https://github.com/eclipse/mosquitto/issues/1978 [#1979]: https://github.com/eclipse/mosquitto/issues/1979 [#1989]: https://github.com/eclipse/mosquitto/issues/1989 [#1995]: https://github.com/eclipse/mosquitto/issues/1995 [#1997]: https://github.com/eclipse/mosquitto/issues/1997 [#1998]: https://github.com/eclipse/mosquitto/issues/1998 [#1999]: https://github.com/eclipse/mosquitto/issues/1999 [#2005]: https://github.com/eclipse/mosquitto/issues/2005 [#2008]: https://github.com/eclipse/mosquitto/issues/2008 mosquitto-2.0.18/www/posts/2021/01/version-2-0-6-released.md000066400000000000000000000050711450213760600227050ustar00rootroot00000000000000 Version 2.0.6 of Mosquitto has been released. This is a bugfix release. # Broker - Fix calculation of remaining length parameter for websockets clients that send fragmented packets. Closes [#1974]. - Fix potential duplicate Will messages being sent when a will delay interval has been set. - Fix message expiry interval property not being honoured in `mosquitto_broker_publish` and `mosquitto_broker_publish_copy`. - Fix websockets listeners with TLS not responding. Closes [#2020]. - Add notes that libsystemd-dev or similar is needed if building with systemd support. Closes [#2019]. - Improve logging in obscure cases when a client disconnects. Closes [#2017]. - Fix reloading of listeners where multiple listeners have been defined with the same port but different bind addresses. Closes [#2029]. - Fix `message_size_limit` not applying to the Will payload. Closes [#2022]. - The error topic-alias-invalid was being sent if an MQTT v5 client published a message with empty topic and topic alias set, but the topic alias hadn't already been configured on the broker. This has been fixed to send a protocol error, as per section 3.3.4 of the specification. - Note in the man pages that SIGHUP reloads TLS certificates. Closes [#2037]. - Fix bridges not always connecting on Windows. Closes [#2043]. # Apps - Allow command line arguments to override config file options in mosquitto_ctrl. Closes [#2010]. - mosquitto_ctrl: produce an error when requesting a new password if both attempts do not match. Closes [#2011]. # Build - Fix cmake builds using `WITH_CJSON=no` not working if cJSON not found. Closes [#2026]. # Other - The SPDX identifiers for EDL-1.0 have been changed to BSD-3-Clause as per The Eclipse legal documentation generator. The licenses are identical. [#1974]: https://github.com/eclipse/mosquitto/issues/1974 [#2010]: https://github.com/eclipse/mosquitto/issues/2010 [#2011]: https://github.com/eclipse/mosquitto/issues/2011 [#2017]: https://github.com/eclipse/mosquitto/issues/2017 [#2019]: https://github.com/eclipse/mosquitto/issues/2019 [#2020]: https://github.com/eclipse/mosquitto/issues/2020 [#2022]: https://github.com/eclipse/mosquitto/issues/2022 [#2026]: https://github.com/eclipse/mosquitto/issues/2026 [#2029]: https://github.com/eclipse/mosquitto/issues/2029 [#2037]: https://github.com/eclipse/mosquitto/issues/2037 [#2043]: https://github.com/eclipse/mosquitto/issues/2043 mosquitto-2.0.18/www/posts/2021/02/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/02/version-2-0-7-released.md000066400000000000000000000056401450213760600227110ustar00rootroot00000000000000 Version 2.0.7 and 1.6.13 of Mosquitto have been released. These are bugfix releases. # 2.0.7 ## Broker - Fix exporting of executable symbols on BSD when building via makefile. - Fix some minor memory leaks on exit only. - Fix possible memory leak on connect. Closes [#2057]. - Fix openssl engine not being able to load private key. Closes [#2066]. ## Clients - Fix config files truncating options after the first space. Closes [#2059]. ## Build - Fix man page building to not absolutely require xsltproc when using CMake. This now handles the case where we are building from the released tar, or building from git if xsltproc is available, or building from git if xsltproc is not available. # 1.6.13 ## Broker: - Fix crash on Windows if loading a plugin fails. Closes [#1866]. - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes [#1925]. Closes [#1476]. - Fix local bridges being disconnected on SIGHUP. Closes [#1942]. - Fix $SYS/broker/publish/messages/+ counters not being updated for QoS 1, 2 messages. Closes [#1968]. - Fix listener not being reassociated with client when reloading a persistence file and `per_listener_settings true` is set and the client did not set a username. Closes [#1891]. - Fix file logging on Windows. Closes [#1880]. - Fix bridge sock not being removed from sock hash on error. Closes [#1897]. ## Client library: - Fix build on Mac Big Sur. Closes [#1905]. - Fix DH group not being set for TLS connections, which meant ciphers using DHE couldn't be used. Closes [#1925]. Closes [#1476]. ## Clients: - mosquitto_sub will now quit with an error if the %U option is used on Windows, rather than just quitting. Closes [#1908]. - Fix config files truncating options after the first space. Closes [#2059]. ## Apps: - Perform stricter parsing of input username in mosquitto_passwd. Closes [#570126] (Eclipse bugzilla). ## Build: - Enable epoll support in CMake builds. [#1476]: https://github.com/eclipse/mosquitto/issues/1476 [#1866]: https://github.com/eclipse/mosquitto/issues/1866 [#1880]: https://github.com/eclipse/mosquitto/issues/1880 [#1891]: https://github.com/eclipse/mosquitto/issues/1891 [#1897]: https://github.com/eclipse/mosquitto/issues/1897 [#1905]: https://github.com/eclipse/mosquitto/issues/1905 [#1908]: https://github.com/eclipse/mosquitto/issues/1908 [#1925]: https://github.com/eclipse/mosquitto/issues/1925 [#1942]: https://github.com/eclipse/mosquitto/issues/1942 [#1968]: https://github.com/eclipse/mosquitto/issues/1968 [#2057]: https://github.com/eclipse/mosquitto/issues/2057 [#2059]: https://github.com/eclipse/mosquitto/issues/2059 [#2066]: https://github.com/eclipse/mosquitto/issues/2066 [#570126]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=570126 mosquitto-2.0.18/www/posts/2021/02/version-2-0-8-released.md000066400000000000000000000030461450213760600227100ustar00rootroot00000000000000 Version 2.0.8 of Mosquitto has been released. This is a bugfix release. # Broker - Fix incorrect datatypes in `struct mosquitto_evt_tick`. This changes the size and offset of two of the members of this struct, and changes the size of the struct. This is an ABI break, but is considered to be acceptable because plugins should never be allocating their own instance of this struct, and currently none of the struct members are used for anything, so a plugin should not be accessing them. It would also be safe to read/write from the existing struct parameters. - Give compile time warning if libwebsockets compiled without external poll support. Closes [#2060]. - Fix memory tracking not being available on FreeBSD or macOS. Closes [#2096]. # Client library - Fix `mosquitto_{pub|sub}_topic_check()` functions not returning `MOSQ_ERR_INVAL` on topic == NULL. # Clients - Fix possible loss of data in `mosquitto_pub -l` when sending multiple long lines. Closes [#2078]. # Build - Provide a mechanism for Docker users to run a broker that doesn't use authentication, without having to provide their own configuration file. Closes [#2040]. [#2040]: https://github.com/eclipse/mosquitto/issues/2040 [#2060]: https://github.com/eclipse/mosquitto/issues/2060 [#2078]: https://github.com/eclipse/mosquitto/issues/2078 [#2096]: https://github.com/eclipse/mosquitto/issues/2096 mosquitto-2.0.18/www/posts/2021/03/000077500000000000000000000000001450213760600164545ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/03/version-2-0-9-released.md000066400000000000000000000100721450213760600227070ustar00rootroot00000000000000 Versions 2.0.9, 1.6.14, and 1.5.11 of Mosquitto have been released. These are bugfix releases and include a minor security fix. # 2.0.9 ## Security - If an empty or invalid CA file was provided to the client library for verifying the remote broker, then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes [#2130]. - If an empty or invalid CA file was provided to the broker for verifying the remote broker for an outgoing bridge connection then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes [#2130]. ## Broker - Fix encrypted bridge connections incorrectly connecting when `bridge_cafile` is empty or invalid. Closes [#2130]. - Fix `tls_version` behaviour not matching documentation. It was setting the exact TLS version to use, not the minimium TLS version to use. Closes [#2110]. - Fix messages to `$` prefixed topics being rejected. Closes [#2111]. - Fix QoS 0 messages not being delivered when max_queued_bytes was configured. Closes [#2123]. - Fix bridge increasing backoff calculation. - Improve handling of invalid combinations of listener address and bind interface configurations. Closes [#2081]. - Fix `max_keepalive` option not applying to clients connecting with keepalive set to 0. Closes [#2117]. ## Client library - Fix encrypted connections incorrectly connecting when the CA file passed to `mosquitto_tls_set()` is empty or invalid. Closes [#2130]. - Fix connections retrying very rapidly in some situations. ## Build - Fix cmake epoll detection. # 1.6.14 ## Security - If an empty or invalid CA file was provided to the client library for verifying the remote broker, then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes [#2130]. - If an empty or invalid CA file was provided to the broker for verifying the remote broker for an outgoing bridge connection then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes [#2130]. ## Broker - Fix encrypted bridge connections incorrectly connecting when `bridge_cafile` is empty or invalid. Closes [#2130]. ## Client library - Fix encrypted connections incorrectly connecting when the CA file passed to `mosquitto_tls_set()` is empty or invalid. Closes [#2130]. - Fix connections retrying very rapidly in some situations. ## Clients - Fix possible loss of data in `mosquitto_pub -l` when sending multiple long lines. Closes [#2078]. # 1.5.11 ## Security - If an empty or invalid CA file was provided to the client library for verifying the remote broker, then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes [#2130]. - If an empty or invalid CA file was provided to the broker for verifying the remote broker for an outgoing bridge connection then the initial connection would fail but subsequent connections would succeed without verifying the remote broker certificate. Closes [#2130]. ## Broker - Fix encrypted bridge connections incorrectly connecting when `bridge_cafile` is empty or invalid. Closes [#2130]. ## Client library - Fix encrypted connections incorrectly connecting when the CA file passed to `mosquitto_tls_set()` is empty or invalid. Closes [#2130]. [#2040]: https://github.com/eclipse/mosquitto/issues/2040 [#2078]: https://github.com/eclipse/mosquitto/issues/2078 [#2081]: https://github.com/eclipse/mosquitto/issues/2081 [#2110]: https://github.com/eclipse/mosquitto/issues/2110 [#2111]: https://github.com/eclipse/mosquitto/issues/2111 [#2117]: https://github.com/eclipse/mosquitto/issues/2117 [#2123]: https://github.com/eclipse/mosquitto/issues/2123 [#2130]: https://github.com/eclipse/mosquitto/issues/2130 mosquitto-2.0.18/www/posts/2021/04/000077500000000000000000000000001450213760600164555ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/04/version-2-0-10-released.md000066400000000000000000000034541450213760600227660ustar00rootroot00000000000000 Versions 2.0.10 of Mosquitto has been released. This is a security and bugfix release. # Security - [CVE-2021-23980]: If an authenticated client connected with MQTT v5 sent a malformed CONNACK message to the broker a NULL pointer dereference occurred, most likely resulting in a segfault. This will be updated with the CVE number when it is assigned. Affects versions 2.0.0 to 2.0.9 inclusive. # Broker - Don't overwrite new receive-maximum if a v5 client connects and takes over an old session. Closes [#2134]. - Fix CVE-xxxx-xxxx. Closes [#2163]. # Clients - Set `receive-maximum` to not exceed the `-C` message count in mosquitto_sub and mosquitto_rr, to avoid potentially lost messages. Closes [#2134]. - Fix TLS-PSK mode not working with port 8883. Closes [#2152]. # Client library - Fix possible socket leak. This would occur if a client was using `mosquitto_loop_start()`, then if the connection failed due to the remote server being inaccessible they called `mosquitto_loop_stop(, true)` and recreated the mosquitto object. # Build - A variety of minor build related fixes, like functions not having previous declarations. - Fix CMake cross compile builds not finding opensslconf.h. Closes [#2160]. - Fix build on Solaris non-sparc. Closes [#2136]. [CVE-2021-23980]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2021-28166 [#2134]: https://github.com/eclipse/mosquitto/issues/2134 [#2136]: https://github.com/eclipse/mosquitto/issues/2136 [#2152]: https://github.com/eclipse/mosquitto/issues/2152 [#2160]: https://github.com/eclipse/mosquitto/issues/2160 [#2163]: https://github.com/eclipse/mosquitto/issues/2163 mosquitto-2.0.18/www/posts/2021/06/000077500000000000000000000000001450213760600164575ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/06/version-2-0-11-released.md000066400000000000000000000036531450213760600227720ustar00rootroot00000000000000 Versions 2.0.11 and 1.6.15 of Mosquitto has been released. These are a security and bugfix releases. # 2.0.11 ## Security - If an authenticated client connected with MQTT v5 sent a crafted CONNECT message to the broker a memory leak would occur. Affects versions 1.6 to 2.0.10 inclusive. ## Broker - Fix possible crash having just upgraded from 1.6 if `per_listener_settings true` is set, and a SIGHUP is sent to the broker before a client has reconnected to the broker. Closes [#2167]. - Fix bridge not reconnectng if the first reconnection attempt fails. Closes [#2207]. - Improve QoS 0 outgoing packet queueing. - Fix non-reachable bridge blocking the broker on Windows. Closes #2172. - Fix possible corruption of pollfd array on Windows when bridges were reconnecting. Closes [#2173]. - Fix QoS 0 messages not being queued when `queue_qos0_messages` was enabled. Closes [#2224]. ## Clients - If sending mosquitto_sub output to a pipe, mosquitto_sub will now detect that the pipe has closed and disconnect. Closes [#2164]. - Fix `mosquitto_pub -l` quitting if a message publication is attempted when the broker is temporarily unavailable. Closes [#2187]. # 1.6.15 ## Security - If an authenticated client connected with MQTT v5 sent a crafted CONNECT message to the broker a memory leak would occur. Affects versions 1.6 to 2.0.10 inclusive. [#2164]: https://github.com/eclipse/mosquitto/issues/2164 [#2167]: https://github.com/eclipse/mosquitto/issues/2167 [#2172]: https://github.com/eclipse/mosquitto/issues/2172 [#2173]: https://github.com/eclipse/mosquitto/issues/2173 [#2187]: https://github.com/eclipse/mosquitto/issues/2187 [#2207]: https://github.com/eclipse/mosquitto/issues/2207 [#2224]: https://github.com/eclipse/mosquitto/issues/2224 mosquitto-2.0.18/www/posts/2021/08/000077500000000000000000000000001450213760600164615ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/08/version-2-0-12-released.md000066400000000000000000000111671450213760600227740ustar00rootroot00000000000000 Versions 2.0.12 of Mosquitto has been released. This is a security and bugfix release. # Security - An MQTT v5 client connecting with a large number of user-property properties could cause excessive CPU usage, leading to a loss of performance and possible denial of service. This has been fixed. - Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections. These clients are now rejected if their keepalive value exceeds max_keepalive. This option allows [CVE-2020-13849], which is for the MQTT v3.1.1 protocol itself rather than an implementation, to be addressed. - Using certain listener related configuration options e.g. `cafile`, that apply to the default listener without defining any listener would cause a remotely accessible listener to be opened that was not confined to the local machine but did have anonymous access enabled, contrary to the documentation. This has been fixed. Closes [#2283]. - [CVE-2021-34434]: If a plugin had granted ACL subscription access to a durable/non-clean-session client, then removed that access, the client would keep its existing subscription. This has been fixed. - Incoming QoS 2 messages that had not completed the QoS flow were not being checked for ACL access when a clean session=False client was reconnecting. This has been fixed. # Broker - Fix possible out of bounds memory reads when reading a corrupt/crafted configuration file. Unless your configuration file is writable by untrusted users this is not a risk. Closes [#567213]. - Fix `max_connections` option not being correctly counted. - Fix TLS certificates and TLS-PSK not being able to be configured at the same time. - Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured. - Fix `max_keepalive` not applying to MQTT v3.1.1 and v3.1 connections. These clients are now rejected if their keepalive value exceeds `max_keepalive`. This option allows CVE-2020-13849, which is for the MQTT v3.1.1 protocol itself rather than an implementation, to be addressed. - Fix broker not quiting if e.g. the `password_file` is specified as a directory. Closes [#2241]. - Fix listener `mount_point` not being removed on outgoing messages. Closes [#2244]. - Strict protocol compliance fixes, plus test suite. - Fix $share subscriptions not being recovered for durable clients that reconnect. - Update plugin configuration documentation. Closes [#2286]. # Client library - If a client uses TLS-PSK then force the default cipher list to use "PSK" ciphers only. This means that a client connecting to a broker configured with x509 certificates only will now fail. Prior to this, the client would connect successfully without verifying certificates, because they were not configured. - Disable TLS v1.3 when using TLS-PSK, because it isn't correctly configured. - Threaded mode is deconfigured when the `mosquitto_loop_start()` thread ends, which allows `mosquitto_loop_start()` to be called again. Closes [#2242]. - Fix `MOSQ_OPT_SSL_CTX` not being able to be set to NULL. Closes [#2289]. - Fix reconnecting failing when `MOSQ_OPT_TLS_USE_OS_CERTS` was in use, but none of `capath`, `cafile`, `psk`, nor `MOSQ_OPT_SSL_CTX` were set, and `MOSQ_OPT_SSL_CTX_WITH_DEFAULTS` was set to the default value of true. Closes [#2288]. # Apps - Fix `mosquitto_ctrl dynsec setDefaultACLAccess` command not working. # Clients - `mosquitto_sub` and `mosquitto_rr` now open stdout in binary mode on Windows so binary payloads are not modified when printing. - Document TLS certificate behaviour when using `-p 8883`. # Build - Fix installation using `WITH_TLS=no`. Closes [#2281]. - Fix builds with libressl 3.4.0. Closes [#2198]. - Remove some unnecessary code guards related to libressl. - Fix printf format build warning on MIPS. Closes [#2271]. [#2198]: https://github.com/eclipse/mosquitto/issues/2198 [#2241]: https://github.com/eclipse/mosquitto/issues/2241 [#2242]: https://github.com/eclipse/mosquitto/issues/2242 [#2244]: https://github.com/eclipse/mosquitto/issues/2244 [#2271]: https://github.com/eclipse/mosquitto/issues/2271 [#2281]: https://github.com/eclipse/mosquitto/issues/2281 [#2286]: https://github.com/eclipse/mosquitto/issues/2286 [#2288]: https://github.com/eclipse/mosquitto/issues/2288 [#2289]: https://github.com/eclipse/mosquitto/issues/2289 [#567213]: https://bugs.eclipse.org/bugs/show_bug.cgi?id=567213 [CVE-2020-13849]: https://nvd.nist.gov/vuln/detail/CVE-2020-13849 [CVE-2021-34434]: https://nvd.nist.gov/vuln/detail/CVE-2021-34434 mosquitto-2.0.18/www/posts/2021/10/000077500000000000000000000000001450213760600164525ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/10/version-2-0-13-released.md000066400000000000000000000030351450213760600227610ustar00rootroot00000000000000 Version 2.0.13 of Mosquitto has been released. This is a bugfix release. # Broker - Fix `max_keepalive` option not being able to be set to 0. - Fix LWT messages not being delivered if `per_listener_settings` was set to true. Closes [#2314]. - Various fixes around inflight quota management. Closes [#2306]. - Fix problem parsing config files with Windows line endings. Closes [#2297]. - Don't send retained messages when a shared subscription is made. - Fix log being truncated in Windows. - Fix client id not showing in log on failed connections, where possible. - Fix broker sending duplicate CONNACK on failed MQTT v5 reauthentication. Closes [#2339]. - Fix mosquitto_plugin.h not including mosquitto_broker.h. Closes [#2350]. # Client library - Initialise sockpairR/W to invalid in `mosquitto_reinitialise()` to avoid closing invalid sockets in `mosquitto_destroy()` on error. Closes [#2326]. # Clients - Fix date format in mosquitto_sub output. Closes [#2353]. [#2297]: https://github.com/eclipse/mosquitto/issues/2297 [#2306]: https://github.com/eclipse/mosquitto/issues/2306 [#2314]: https://github.com/eclipse/mosquitto/issues/2314 [#2326]: https://github.com/eclipse/mosquitto/issues/2326 [#2339]: https://github.com/eclipse/mosquitto/issues/2339 [#2350]: https://github.com/eclipse/mosquitto/issues/2350 [#2353]: https://github.com/eclipse/mosquitto/issues/2353 mosquitto-2.0.18/www/posts/2021/11/000077500000000000000000000000001450213760600164535ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2021/11/version-2-0-14-released.md000066400000000000000000000011751450213760600227660ustar00rootroot00000000000000 Versions 2.0.14 of Mosquitto has been released. This is a bugfix release. # Broker - Fix bridge not respecting receive-maximum when reconnecting with MQTT v5. # Client library - Fix `mosquitto_topic_matches_sub2()` not using the length parameters. Closes [#2364]. - Fix incorrect `subscribe_callback` in mosquittopp.h. Closes [#2367]. [#2364]: https://github.com/eclipse/mosquitto/issues/2364 [#2367]: https://github.com/eclipse/mosquitto/issues/2367 mosquitto-2.0.18/www/posts/2022/000077500000000000000000000000001450213760600162335ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2022/08/000077500000000000000000000000001450213760600164625ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2022/08/version-2-0-15-released.md000066400000000000000000000113021450213760600227670ustar00rootroot00000000000000 Versions 2.0.15 of Mosquitto has been released. This is a security and bugfix release. # Security - Deleting the group configured as the anonymous group in the Dynamic Security plugin, would leave a dangling pointer that could lead to a single crash. This is considered a minor issue - only administrative users should have access to dynsec, the impact on availability is one-off, and there is no associated loss of data. It is now forbidden to delete the group configured as the anonymous group. # Broker - Fix memory leak when a plugin modifies the topic of a message in `MOSQ_EVT_MESSAGE`. - Fix bridge `restart_timeout` not being honoured. - Fix potential memory leaks if a plugin modifies the message in the `MOSQ_EVT_MESSAGE` event. - Fix unused flags in CONNECT command being forced to be 0, which is not required for MQTT v3.1. Closes [#2522]. - Improve documentation of `persistent_client_expiration` option. Closes [#2404]. - Add clients to session expiry check list when restarting and reloading from persistence. Closes [#2546]. - Fix bridges not sending failure notification messages to the local broker if the remote bridge connection fails. Closes [#2467]. Closes [#1488]. - Fix some PUBLISH messages not being counted in $SYS stats. Closes [#2448]. - Fix incorrect return code being sent in DISCONNECT when a client session is taken over. Closes [#2607]. - Fix confusing "out of memory" error when a client is kicked in the dynamic security plugin. Closes [#2525]. - Fix confusing error message when dynamic security config file was a directory. Closes [#2520]. - Fix bridge queued messages not being persisted when local_cleansession is set to false and cleansession is set to true. Closes [#2604]. - Dynamic security: Fix modifyClient and modifyGroup commands to not modify the client/group if a new group/client being added is not valid. Closes [#2598]. - Dynamic security: Fix the plugin being able to be loaded twice. Currently only a single plugin can interact with a unique $CONTROL topic. Using multiple instances of the plugin would produce duplicate entries in the config file. Closes [#2601]. Closes [#2470]. - Fix case where expired messages were causing queued messages not to be delivered. Closes [#2609]. - Fix websockets not passing on the X-Forwarded-For header. # Client library - Fix threads library detection on Windows under cmake. Bumps the minimum cmake version to 3.1, which is still ancient. - Fix use of `MOSQ_OPT_TLS_ENGINE` being unable to be used due to the openssl ctx not being initialised until starting to connect. Closes [#2537]. - Fix incorrect use of SSL_connect. Closes [#2594]. - Don't set SIGPIPE to ignore, use MSG_NOSIGNAL instead. Closes [#2564]. - Add documentation of struct mosquitto_message to header. Closes [#2561]. - Fix documentation omission around mosquitto_reinitialise. Closes [#2489]. - Fix use of MOSQ_OPT_SSL_CTX when used in conjunction with MOSQ_OPT_SSL_CTX_DEFAULTS. Closes [#2463]. - Fix failure to close thread in some situations. Closes [#2545]. # Clients - Fix mosquitto_pub incorrectly reusing topic aliases when reconnecting. Closes [#2494]. # Apps - Fix `-o` not working in `mosquitto_ctrl`, and typo in related documentation. Closes [#2471]. [#1488]: https://github.com/eclipse/mosquitto/issues/1488 [#2404]: https://github.com/eclipse/mosquitto/issues/2404 [#2448]: https://github.com/eclipse/mosquitto/issues/2448 [#2463]: https://github.com/eclipse/mosquitto/issues/2463 [#2467]: https://github.com/eclipse/mosquitto/issues/2467 [#2470]: https://github.com/eclipse/mosquitto/issues/2470 [#2471]: https://github.com/eclipse/mosquitto/issues/2471 [#2489]: https://github.com/eclipse/mosquitto/issues/2489 [#2494]: https://github.com/eclipse/mosquitto/issues/2494 [#2520]: https://github.com/eclipse/mosquitto/issues/2520 [#2522]: https://github.com/eclipse/mosquitto/issues/2522 [#2525]: https://github.com/eclipse/mosquitto/issues/2525 [#2537]: https://github.com/eclipse/mosquitto/issues/2537 [#2545]: https://github.com/eclipse/mosquitto/issues/2545 [#2546]: https://github.com/eclipse/mosquitto/issues/2546 [#2561]: https://github.com/eclipse/mosquitto/issues/2561 [#2564]: https://github.com/eclipse/mosquitto/issues/2564 [#2594]: https://github.com/eclipse/mosquitto/issues/2594 [#2598]: https://github.com/eclipse/mosquitto/issues/2598 [#2601]: https://github.com/eclipse/mosquitto/issues/2601 [#2604]: https://github.com/eclipse/mosquitto/issues/2604 [#2607]: https://github.com/eclipse/mosquitto/issues/2607 [#2609]: https://github.com/eclipse/mosquitto/issues/2609 mosquitto-2.0.18/www/posts/2023/000077500000000000000000000000001450213760600162345ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2023/08/000077500000000000000000000000001450213760600164635ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2023/08/version-2-0-16-released.md000066400000000000000000000077601450213760600230060ustar00rootroot00000000000000 Version 2.0.16 of Mosquitto has been released. This is a security and bugfix release. # Security - [CVE-2023-28366]: Fix memory leak in broker when clients send multiple QoS 2 messages with the same message ID, but then never respond to the PUBREC commands. - [CVE-2023-0809]: Fix excessive memory being allocated based on malicious initial packets that are not CONNECT packets. - [CVE-2023-3592]: Fix memory leak when clients send v5 CONNECT packets with a will message that contains invalid property types. - Broker will now reject Will messages that attempt to publish to $CONTROL/. - Broker now validates usernames provided in a TLS certificate or TLS-PSK identity are valid UTF-8. - Fix potential crash when loading invalid persistence file. - Library will no longer allow single level wildcard certificates, e.g. *.com # Broker - Fix $SYS messages being expired after 60 seconds and hence unchanged values disappearing. - Fix some retained topic memory not being cleared immediately after used. - Fix error handling related to the `bind_interface` option. - Fix std* files not being redirected when daemonising, when built with assertions removed. Closes [#2708]. - Fix default settings incorrectly allowing TLS v1.1. Closes [#2722]. - Use line buffered mode for stdout. Closes #2354. Closes [#2749]. - Fix bridges with non-matching cleansession/local_cleansession being expired on start after restoring from persistence. Closes [#2634]. - Fix connections being limited to 2048 on Windows. The limit is now 8192, where supported. Closes [#2732]. - Broker will log warnings if sensitive files are world readable/writable, or if the owner/group is not the same as the user/group the broker is running as. In future versions the broker will refuse to open these files. - mosquitto_memcmp_const is now more constant time. - Only register with DLT if DLT logging is enabled. - Fix any possible case where a json string might be incorrectly loaded. This could have caused a crash if a textname or textdescription field of a role was not a string, when loading the dynsec config from file only. - Dynsec plugin will not allow duplicate clients/groups/roles when loading config from file, which matches the behaviour for when creating them. - Fix heap overflow when reading corrupt config with "log_dest file". # Client library - Use CLOCK_BOOTTIME when available, to keep track of time. This solves the problem of the client OS sleeping and the client hence not being able to calculate the actual time for keepalive purposes. Closes [#2760]. - Fix default settings incorrectly allowing TLS v1.1. Closes [#2722]. - Fix high CPU use on slow TLS connect. Closes [#2794]. # Clients - Fix incorrect topic-alias property value in mosquitto_sub json output. - Fix confusing message on TLS certificate verification. Closes [#2746]. # Apps - mosquitto_passwd uses mkstemp() for backup files. - `mosquitto_ctrl dynsec init` will refuse to overwrite an existing file, without a race-condition. [CVE-2023-0809]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-0809 [CVE-2023-28366]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-27366 [CVE-2023-3592]: https://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2023-3592 [#2354]: https://github.com/eclipse/mosquitto/issues/2354 [#2634]: https://github.com/eclipse/mosquitto/issues/2634 [#2708]: https://github.com/eclipse/mosquitto/issues/2708 [#2722]: https://github.com/eclipse/mosquitto/issues/2722 [#2722]: https://github.com/eclipse/mosquitto/issues/2722 [#2732]: https://github.com/eclipse/mosquitto/issues/2732 [#2746]: https://github.com/eclipse/mosquitto/issues/2746 [#2749]: https://github.com/eclipse/mosquitto/issues/2749 [#2760]: https://github.com/eclipse/mosquitto/issues/2760 [#2794]: https://github.com/eclipse/mosquitto/issues/2794 [#1488]: https://github.com/eclipse/mosquitto/issues/1488 mosquitto-2.0.18/www/posts/2023/08/version-2-0-17-released.md000066400000000000000000000012421450213760600227740ustar00rootroot00000000000000 Version 2.0.17 of Mosquitto has been released. This is a bugfix release. Broker: - Fix `max_queued_messages 0` stopping clients from receiving messages. Closes [#2879]. - Fix `max_inflight_messages` not being set correctly. Closes [#2876]. Apps: - Fix `mosquitto_passwd -U` backup file creation. Closes [#2873]. [#2873]: https://github.com/eclipse/mosquitto/issues/2873 [#2876]: https://github.com/eclipse/mosquitto/issues/2876 [#2879]: https://github.com/eclipse/mosquitto/issues/2879 mosquitto-2.0.18/www/posts/2023/09/000077500000000000000000000000001450213760600164645ustar00rootroot00000000000000mosquitto-2.0.18/www/posts/2023/09/version-2-0-18-released.md000066400000000000000000000011271450213760600230000ustar00rootroot00000000000000 Version 2.0.18 of Mosquitto has been released. This is a bugfix release. Broker: - Fix crash on subscribe under certain unlikely conditions. Closes [#2885]. Closes [#2881]. Clients: - Fix mosquitto_rr not honouring `-R`. Closes [#2893]. [#2881]: https://github.com/eclipse/mosquitto/issues/2881 [#2885]: https://github.com/eclipse/mosquitto/issues/2885 [#2893]: https://github.com/eclipse/mosquitto/issues/2893 mosquitto-2.0.18/www/templates/000077500000000000000000000000001450213760600164745ustar00rootroot00000000000000mosquitto-2.0.18/www/templates/book.tmpl000066400000000000000000000064551450213760600203360ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="helper" file="post_helper.tmpl"/> <%namespace name="pheader" file="post_header.tmpl"/> <%namespace name="comments" file="comments_helper.tmpl"/> <%inherit file="post.tmpl"/> <%block name="extra_head"> ${parent.extra_head()} <%block name="content">

${post.title()}

${post.text()}
<%block name="extra_js"> mosquitto-2.0.18/www/themes/000077500000000000000000000000001450213760600157635ustar00rootroot00000000000000mosquitto-2.0.18/www/themes/mosquitto/000077500000000000000000000000001450213760600200275ustar00rootroot00000000000000mosquitto-2.0.18/www/themes/mosquitto/assets/000077500000000000000000000000001450213760600213315ustar00rootroot00000000000000mosquitto-2.0.18/www/themes/mosquitto/assets/css/000077500000000000000000000000001450213760600221215ustar00rootroot00000000000000mosquitto-2.0.18/www/themes/mosquitto/assets/css/bulma.css000066400000000000000000006612431450213760600237470ustar00rootroot00000000000000/*! bulma.io v0.6.1 | MIT License | github.com/jgthms/bulma */ @-webkit-keyframes spinAround { from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } to { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } @keyframes spinAround { from { -webkit-transform: rotate(0deg); transform: rotate(0deg); } to { -webkit-transform: rotate(359deg); transform: rotate(359deg); } } /*! minireset.css v0.0.2 | MIT License | github.com/jgthms/minireset.css */ html, body, p, ol, ul, li, dl, dt, dd, blockquote, figure, fieldset, legend, textarea, pre, iframe, hr, h1, h2, h3, h4, h5, h6 { margin: 0; padding: 0; } h1, h2, h3, h4, h5, h6 { font-size: 100%; font-weight: normal; } ul { list-style: none; } button, input, select, textarea { margin: 0; } html { -webkit-box-sizing: border-box; box-sizing: border-box; } * { -webkit-box-sizing: inherit; box-sizing: inherit; } *:before, *:after { -webkit-box-sizing: inherit; box-sizing: inherit; } img, embed, object, audio, video { max-width: 100%; } iframe { border: 0; } table { border-collapse: collapse; border-spacing: 0; } td, th { padding: 0; text-align: left; } html { background-color: white; font-size: 16px; -moz-osx-font-smoothing: grayscale; -webkit-font-smoothing: antialiased; min-width: 300px; overflow-x: hidden; overflow-y: scroll; text-rendering: optimizeLegibility; -webkit-text-size-adjust: 100%; -moz-text-size-adjust: 100%; -ms-text-size-adjust: 100%; text-size-adjust: 100%; } article, aside, figure, footer, header, hgroup, section { display: block; } body, button, input, select, textarea { font-family: BlinkMacSystemFont, -apple-system, "Segoe UI", "Roboto", "Oxygen", "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", "Helvetica", "Arial", sans-serif; } code, pre { -moz-osx-font-smoothing: auto; -webkit-font-smoothing: auto; font-family: monospace; } body { color: #4a4a4a; font-size: 1rem; font-weight: 400; line-height: 1.5; } a { color: #7a91c1; cursor: pointer; text-decoration: none; } a strong { color: currentColor; } a:hover { color: #363636; } code { background-color: whitesmoke; color: #ff3860; font-size: 0.875em; font-weight: normal; padding: 0.25em 0.5em 0.25em; } hr { background-color: #dbdbdb; border: none; display: block; height: 1px; margin: 1.5rem 0; } img { height: auto; max-width: 100%; } input[type="checkbox"], input[type="radio"] { vertical-align: baseline; } small { font-size: 0.875em; } span { font-style: inherit; font-weight: inherit; } strong { color: #363636; font-weight: 700; } pre { -webkit-overflow-scrolling: touch; background-color: whitesmoke; color: #4a4a4a; font-size: 0.875em; overflow-x: auto; padding: 1.25rem 1.5rem; white-space: pre; word-wrap: normal; } pre code { background-color: transparent; color: currentColor; font-size: 1em; padding: 0; } table td, table th { text-align: left; vertical-align: top; } table th { color: #363636; } .is-clearfix:after { clear: both; content: " "; display: table; } .is-pulled-left { float: left !important; } .is-pulled-right { float: right !important; } .is-clipped { overflow: hidden !important; } .is-overlay { bottom: 0; left: 0; position: absolute; right: 0; top: 0; } .is-size-1 { font-size: 3rem !important; } .is-size-2 { font-size: 2.5rem !important; } .is-size-3 { font-size: 2rem !important; } .is-size-4 { font-size: 1.5rem !important; } .is-size-5 { font-size: 1.25rem !important; } .is-size-6 { font-size: 1rem !important; } .is-size-7 { font-size: 0.75rem !important; } @media screen and (max-width: 768px) { .is-size-1-mobile { font-size: 3rem !important; } .is-size-2-mobile { font-size: 2.5rem !important; } .is-size-3-mobile { font-size: 2rem !important; } .is-size-4-mobile { font-size: 1.5rem !important; } .is-size-5-mobile { font-size: 1.25rem !important; } .is-size-6-mobile { font-size: 1rem !important; } .is-size-7-mobile { font-size: 0.75rem !important; } } @media screen and (min-width: 769px), print { .is-size-1-tablet { font-size: 3rem !important; } .is-size-2-tablet { font-size: 2.5rem !important; } .is-size-3-tablet { font-size: 2rem !important; } .is-size-4-tablet { font-size: 1.5rem !important; } .is-size-5-tablet { font-size: 1.25rem !important; } .is-size-6-tablet { font-size: 1rem !important; } .is-size-7-tablet { font-size: 0.75rem !important; } } @media screen and (max-width: 1023px) { .is-size-1-touch { font-size: 3rem !important; } .is-size-2-touch { font-size: 2.5rem !important; } .is-size-3-touch { font-size: 2rem !important; } .is-size-4-touch { font-size: 1.5rem !important; } .is-size-5-touch { font-size: 1.25rem !important; } .is-size-6-touch { font-size: 1rem !important; } .is-size-7-touch { font-size: 0.75rem !important; } } @media screen and (min-width: 1024px) { .is-size-1-desktop { font-size: 3rem !important; } .is-size-2-desktop { font-size: 2.5rem !important; } .is-size-3-desktop { font-size: 2rem !important; } .is-size-4-desktop { font-size: 1.5rem !important; } .is-size-5-desktop { font-size: 1.25rem !important; } .is-size-6-desktop { font-size: 1rem !important; } .is-size-7-desktop { font-size: 0.75rem !important; } } @media screen and (min-width: 1216px) { .is-size-1-widescreen { font-size: 3rem !important; } .is-size-2-widescreen { font-size: 2.5rem !important; } .is-size-3-widescreen { font-size: 2rem !important; } .is-size-4-widescreen { font-size: 1.5rem !important; } .is-size-5-widescreen { font-size: 1.25rem !important; } .is-size-6-widescreen { font-size: 1rem !important; } .is-size-7-widescreen { font-size: 0.75rem !important; } } @media screen and (min-width: 1408px) { .is-size-1-fullhd { font-size: 3rem !important; } .is-size-2-fullhd { font-size: 2.5rem !important; } .is-size-3-fullhd { font-size: 2rem !important; } .is-size-4-fullhd { font-size: 1.5rem !important; } .is-size-5-fullhd { font-size: 1.25rem !important; } .is-size-6-fullhd { font-size: 1rem !important; } .is-size-7-fullhd { font-size: 0.75rem !important; } } .has-text-centered { text-align: center !important; } @media screen and (max-width: 768px) { .has-text-centered-mobile { text-align: center !important; } } @media screen and (min-width: 769px), print { .has-text-centered-tablet { text-align: center !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .has-text-centered-tablet-only { text-align: center !important; } } @media screen and (max-width: 1023px) { .has-text-centered-touch { text-align: center !important; } } @media screen and (min-width: 1024px) { .has-text-centered-desktop { text-align: center !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .has-text-centered-desktop-only { text-align: center !important; } } @media screen and (min-width: 1216px) { .has-text-centered-widescreen { text-align: center !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .has-text-centered-widescreen-only { text-align: center !important; } } @media screen and (min-width: 1408px) { .has-text-centered-fullhd { text-align: center !important; } } .has-text-justified { text-align: justify !important; } @media screen and (max-width: 768px) { .has-text-justified-mobile { text-align: justify !important; } } @media screen and (min-width: 769px), print { .has-text-justified-tablet { text-align: justify !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .has-text-justified-tablet-only { text-align: justify !important; } } @media screen and (max-width: 1023px) { .has-text-justified-touch { text-align: justify !important; } } @media screen and (min-width: 1024px) { .has-text-justified-desktop { text-align: justify !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .has-text-justified-desktop-only { text-align: justify !important; } } @media screen and (min-width: 1216px) { .has-text-justified-widescreen { text-align: justify !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .has-text-justified-widescreen-only { text-align: justify !important; } } @media screen and (min-width: 1408px) { .has-text-justified-fullhd { text-align: justify !important; } } .has-text-left { text-align: left !important; } @media screen and (max-width: 768px) { .has-text-left-mobile { text-align: left !important; } } @media screen and (min-width: 769px), print { .has-text-left-tablet { text-align: left !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .has-text-left-tablet-only { text-align: left !important; } } @media screen and (max-width: 1023px) { .has-text-left-touch { text-align: left !important; } } @media screen and (min-width: 1024px) { .has-text-left-desktop { text-align: left !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .has-text-left-desktop-only { text-align: left !important; } } @media screen and (min-width: 1216px) { .has-text-left-widescreen { text-align: left !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .has-text-left-widescreen-only { text-align: left !important; } } @media screen and (min-width: 1408px) { .has-text-left-fullhd { text-align: left !important; } } .has-text-right { text-align: right !important; } @media screen and (max-width: 768px) { .has-text-right-mobile { text-align: right !important; } } @media screen and (min-width: 769px), print { .has-text-right-tablet { text-align: right !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .has-text-right-tablet-only { text-align: right !important; } } @media screen and (max-width: 1023px) { .has-text-right-touch { text-align: right !important; } } @media screen and (min-width: 1024px) { .has-text-right-desktop { text-align: right !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .has-text-right-desktop-only { text-align: right !important; } } @media screen and (min-width: 1216px) { .has-text-right-widescreen { text-align: right !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .has-text-right-widescreen-only { text-align: right !important; } } @media screen and (min-width: 1408px) { .has-text-right-fullhd { text-align: right !important; } } .is-capitalized { text-transform: capitalize !important; } .is-lowercase { text-transform: lowercase !important; } .is-uppercase { text-transform: uppercase !important; } .has-text-white { color: white !important; } a.has-text-white:hover, a.has-text-white:focus { color: #e6e6e6 !important; } .has-text-black { color: #0a0a0a !important; } a.has-text-black:hover, a.has-text-black:focus { color: black !important; } .has-text-light { color: whitesmoke !important; } a.has-text-light:hover, a.has-text-light:focus { color: #dbdbdb !important; } .has-text-dark { color: #363636 !important; } a.has-text-dark:hover, a.has-text-dark:focus { color: #1c1c1c !important; } .has-text-primary { color: #00d1b2 !important; } a.has-text-primary:hover, a.has-text-primary:focus { color: #009e86 !important; } .has-text-link { color: #7a91c1 !important; } a.has-text-link:hover, a.has-text-link:focus { color: #205bbc !important; } .has-text-info { color: #209cee !important; } a.has-text-info:hover, a.has-text-info:focus { color: #0f81cc !important; } .has-text-success { color: #23d160 !important; } a.has-text-success:hover, a.has-text-success:focus { color: #1ca64c !important; } .has-text-warning { color: #ffdd57 !important; } a.has-text-warning:hover, a.has-text-warning:focus { color: #ffd324 !important; } .has-text-danger { color: #ff3860 !important; } a.has-text-danger:hover, a.has-text-danger:focus { color: #ff0537 !important; } .has-text-black-bis { color: #121212 !important; } .has-text-black-ter { color: #242424 !important; } .has-text-grey-darker { color: #363636 !important; } .has-text-grey-dark { color: #4a4a4a !important; } .has-text-grey { color: #7a7a7a !important; } .has-text-grey-light { color: #b5b5b5 !important; } .has-text-grey-lighter { color: #dbdbdb !important; } .has-text-white-ter { color: whitesmoke !important; } .has-text-white-bis { color: #fafafa !important; } .has-text-weight-light { font-weight: 300 !important; } .has-text-weight-normal { font-weight: 400 !important; } .has-text-weight-semibold { font-weight: 600 !important; } .has-text-weight-bold { font-weight: 700 !important; } .is-block { display: block !important; } @media screen and (max-width: 768px) { .is-block-mobile { display: block !important; } } @media screen and (min-width: 769px), print { .is-block-tablet { display: block !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .is-block-tablet-only { display: block !important; } } @media screen and (max-width: 1023px) { .is-block-touch { display: block !important; } } @media screen and (min-width: 1024px) { .is-block-desktop { display: block !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .is-block-desktop-only { display: block !important; } } @media screen and (min-width: 1216px) { .is-block-widescreen { display: block !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .is-block-widescreen-only { display: block !important; } } @media screen and (min-width: 1408px) { .is-block-fullhd { display: block !important; } } .is-flex { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } @media screen and (max-width: 768px) { .is-flex-mobile { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (min-width: 769px), print { .is-flex-tablet { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .is-flex-tablet-only { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (max-width: 1023px) { .is-flex-touch { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (min-width: 1024px) { .is-flex-desktop { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .is-flex-desktop-only { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (min-width: 1216px) { .is-flex-widescreen { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .is-flex-widescreen-only { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } @media screen and (min-width: 1408px) { .is-flex-fullhd { display: -webkit-box !important; display: -ms-flexbox !important; display: flex !important; } } .is-inline { display: inline !important; } @media screen and (max-width: 768px) { .is-inline-mobile { display: inline !important; } } @media screen and (min-width: 769px), print { .is-inline-tablet { display: inline !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .is-inline-tablet-only { display: inline !important; } } @media screen and (max-width: 1023px) { .is-inline-touch { display: inline !important; } } @media screen and (min-width: 1024px) { .is-inline-desktop { display: inline !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .is-inline-desktop-only { display: inline !important; } } @media screen and (min-width: 1216px) { .is-inline-widescreen { display: inline !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .is-inline-widescreen-only { display: inline !important; } } @media screen and (min-width: 1408px) { .is-inline-fullhd { display: inline !important; } } .is-inline-block { display: inline-block !important; } @media screen and (max-width: 768px) { .is-inline-block-mobile { display: inline-block !important; } } @media screen and (min-width: 769px), print { .is-inline-block-tablet { display: inline-block !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .is-inline-block-tablet-only { display: inline-block !important; } } @media screen and (max-width: 1023px) { .is-inline-block-touch { display: inline-block !important; } } @media screen and (min-width: 1024px) { .is-inline-block-desktop { display: inline-block !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .is-inline-block-desktop-only { display: inline-block !important; } } @media screen and (min-width: 1216px) { .is-inline-block-widescreen { display: inline-block !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .is-inline-block-widescreen-only { display: inline-block !important; } } @media screen and (min-width: 1408px) { .is-inline-block-fullhd { display: inline-block !important; } } .is-inline-flex { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } @media screen and (max-width: 768px) { .is-inline-flex-mobile { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (min-width: 769px), print { .is-inline-flex-tablet { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .is-inline-flex-tablet-only { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (max-width: 1023px) { .is-inline-flex-touch { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (min-width: 1024px) { .is-inline-flex-desktop { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .is-inline-flex-desktop-only { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (min-width: 1216px) { .is-inline-flex-widescreen { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .is-inline-flex-widescreen-only { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } @media screen and (min-width: 1408px) { .is-inline-flex-fullhd { display: -webkit-inline-box !important; display: -ms-inline-flexbox !important; display: inline-flex !important; } } .is-hidden { display: none !important; } @media screen and (max-width: 768px) { .is-hidden-mobile { display: none !important; } } @media screen and (min-width: 769px), print { .is-hidden-tablet { display: none !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .is-hidden-tablet-only { display: none !important; } } @media screen and (max-width: 1023px) { .is-hidden-touch { display: none !important; } } @media screen and (min-width: 1024px) { .is-hidden-desktop { display: none !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .is-hidden-desktop-only { display: none !important; } } @media screen and (min-width: 1216px) { .is-hidden-widescreen { display: none !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .is-hidden-widescreen-only { display: none !important; } } @media screen and (min-width: 1408px) { .is-hidden-fullhd { display: none !important; } } .is-invisible { visibility: hidden !important; } @media screen and (max-width: 768px) { .is-invisible-mobile { visibility: hidden !important; } } @media screen and (min-width: 769px), print { .is-invisible-tablet { visibility: hidden !important; } } @media screen and (min-width: 769px) and (max-width: 1023px) { .is-invisible-tablet-only { visibility: hidden !important; } } @media screen and (max-width: 1023px) { .is-invisible-touch { visibility: hidden !important; } } @media screen and (min-width: 1024px) { .is-invisible-desktop { visibility: hidden !important; } } @media screen and (min-width: 1024px) and (max-width: 1215px) { .is-invisible-desktop-only { visibility: hidden !important; } } @media screen and (min-width: 1216px) { .is-invisible-widescreen { visibility: hidden !important; } } @media screen and (min-width: 1216px) and (max-width: 1407px) { .is-invisible-widescreen-only { visibility: hidden !important; } } @media screen and (min-width: 1408px) { .is-invisible-fullhd { visibility: hidden !important; } } .is-marginless { margin: 0 !important; } .is-paddingless { padding: 0 !important; } .is-radiusless { border-radius: 0 !important; } .is-shadowless { -webkit-box-shadow: none !important; box-shadow: none !important; } .is-unselectable { -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; } .box { background-color: white; border-radius: 5px; -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); color: #4a4a4a; display: block; padding: 1.25rem; } .box:not(:last-child) { margin-bottom: 1.5rem; } a.box:hover, a.box:focus { -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px #7a91c1; box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px #7a91c1; } a.box:active { -webkit-box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2), 0 0 0 1px #7a91c1; box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2), 0 0 0 1px #7a91c1; } .button { -moz-appearance: none; -webkit-appearance: none; -webkit-box-align: center; -ms-flex-align: center; align-items: center; border: 1px solid transparent; border-radius: 3px; -webkit-box-shadow: none; box-shadow: none; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; font-size: 1rem; height: 2.25em; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; line-height: 1.5; padding-bottom: calc(0.375em - 1px); padding-left: calc(0.625em - 1px); padding-right: calc(0.625em - 1px); padding-top: calc(0.375em - 1px); position: relative; vertical-align: top; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; background-color: white; border-color: #dbdbdb; color: #363636; cursor: pointer; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; padding-left: 0.75em; padding-right: 0.75em; text-align: center; white-space: nowrap; } .button:focus, .button.is-focused, .button:active, .button.is-active { outline: none; } .button[disabled] { cursor: not-allowed; } .button strong { color: inherit; } .button .icon, .button .icon.is-small, .button .icon.is-medium, .button .icon.is-large { height: 1.5em; width: 1.5em; } .button .icon:first-child:not(:last-child) { margin-left: calc(-0.375em - 1px); margin-right: 0.1875em; } .button .icon:last-child:not(:first-child) { margin-left: 0.1875em; margin-right: calc(-0.375em - 1px); } .button .icon:first-child:last-child { margin-left: calc(-0.375em - 1px); margin-right: calc(-0.375em - 1px); } .button:hover, .button.is-hovered { border-color: #b5b5b5; color: #363636; } .button:focus, .button.is-focused { border-color: #7a91c1; color: #363636; } .button:focus:not(:active), .button.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } .button:active, .button.is-active { border-color: #4a4a4a; color: #363636; } .button.is-text { background-color: transparent; border-color: transparent; color: #4a4a4a; text-decoration: underline; } .button.is-text:hover, .button.is-text.is-hovered, .button.is-text:focus, .button.is-text.is-focused { background-color: whitesmoke; color: #363636; } .button.is-text:active, .button.is-text.is-active { background-color: #e8e8e8; color: #363636; } .button.is-text[disabled] { background-color: transparent; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-white { background-color: white; border-color: transparent; color: #0a0a0a; } .button.is-white:hover, .button.is-white.is-hovered { background-color: #f9f9f9; border-color: transparent; color: #0a0a0a; } .button.is-white:focus, .button.is-white.is-focused { border-color: transparent; color: #0a0a0a; } .button.is-white:focus:not(:active), .button.is-white.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); } .button.is-white:active, .button.is-white.is-active { background-color: #f2f2f2; border-color: transparent; color: #0a0a0a; } .button.is-white[disabled] { background-color: white; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-white.is-inverted { background-color: #0a0a0a; color: white; } .button.is-white.is-inverted:hover { background-color: black; } .button.is-white.is-inverted[disabled] { background-color: #0a0a0a; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: white; } .button.is-white.is-loading:after { border-color: transparent transparent #0a0a0a #0a0a0a !important; } .button.is-white.is-outlined { background-color: transparent; border-color: white; color: white; } .button.is-white.is-outlined:hover, .button.is-white.is-outlined:focus { background-color: white; border-color: white; color: #0a0a0a; } .button.is-white.is-outlined.is-loading:after { border-color: transparent transparent white white !important; } .button.is-white.is-outlined[disabled] { background-color: transparent; border-color: white; -webkit-box-shadow: none; box-shadow: none; color: white; } .button.is-white.is-inverted.is-outlined { background-color: transparent; border-color: #0a0a0a; color: #0a0a0a; } .button.is-white.is-inverted.is-outlined:hover, .button.is-white.is-inverted.is-outlined:focus { background-color: #0a0a0a; color: white; } .button.is-white.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: #0a0a0a; -webkit-box-shadow: none; box-shadow: none; color: #0a0a0a; } .button.is-black { background-color: #0a0a0a; border-color: transparent; color: white; } .button.is-black:hover, .button.is-black.is-hovered { background-color: #040404; border-color: transparent; color: white; } .button.is-black:focus, .button.is-black.is-focused { border-color: transparent; color: white; } .button.is-black:focus:not(:active), .button.is-black.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); } .button.is-black:active, .button.is-black.is-active { background-color: black; border-color: transparent; color: white; } .button.is-black[disabled] { background-color: #0a0a0a; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-black.is-inverted { background-color: white; color: #0a0a0a; } .button.is-black.is-inverted:hover { background-color: #f2f2f2; } .button.is-black.is-inverted[disabled] { background-color: white; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #0a0a0a; } .button.is-black.is-loading:after { border-color: transparent transparent white white !important; } .button.is-black.is-outlined { background-color: transparent; border-color: #0a0a0a; color: #0a0a0a; } .button.is-black.is-outlined:hover, .button.is-black.is-outlined:focus { background-color: #0a0a0a; border-color: #0a0a0a; color: white; } .button.is-black.is-outlined.is-loading:after { border-color: transparent transparent #0a0a0a #0a0a0a !important; } .button.is-black.is-outlined[disabled] { background-color: transparent; border-color: #0a0a0a; -webkit-box-shadow: none; box-shadow: none; color: #0a0a0a; } .button.is-black.is-inverted.is-outlined { background-color: transparent; border-color: white; color: white; } .button.is-black.is-inverted.is-outlined:hover, .button.is-black.is-inverted.is-outlined:focus { background-color: white; color: #0a0a0a; } .button.is-black.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: white; -webkit-box-shadow: none; box-shadow: none; color: white; } .button.is-light { background-color: whitesmoke; border-color: transparent; color: #363636; } .button.is-light:hover, .button.is-light.is-hovered { background-color: #eeeeee; border-color: transparent; color: #363636; } .button.is-light:focus, .button.is-light.is-focused { border-color: transparent; color: #363636; } .button.is-light:focus:not(:active), .button.is-light.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); } .button.is-light:active, .button.is-light.is-active { background-color: #e8e8e8; border-color: transparent; color: #363636; } .button.is-light[disabled] { background-color: whitesmoke; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-light.is-inverted { background-color: #363636; color: whitesmoke; } .button.is-light.is-inverted:hover { background-color: #292929; } .button.is-light.is-inverted[disabled] { background-color: #363636; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: whitesmoke; } .button.is-light.is-loading:after { border-color: transparent transparent #363636 #363636 !important; } .button.is-light.is-outlined { background-color: transparent; border-color: whitesmoke; color: whitesmoke; } .button.is-light.is-outlined:hover, .button.is-light.is-outlined:focus { background-color: whitesmoke; border-color: whitesmoke; color: #363636; } .button.is-light.is-outlined.is-loading:after { border-color: transparent transparent whitesmoke whitesmoke !important; } .button.is-light.is-outlined[disabled] { background-color: transparent; border-color: whitesmoke; -webkit-box-shadow: none; box-shadow: none; color: whitesmoke; } .button.is-light.is-inverted.is-outlined { background-color: transparent; border-color: #363636; color: #363636; } .button.is-light.is-inverted.is-outlined:hover, .button.is-light.is-inverted.is-outlined:focus { background-color: #363636; color: whitesmoke; } .button.is-light.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: #363636; -webkit-box-shadow: none; box-shadow: none; color: #363636; } .button.is-dark { background-color: #363636; border-color: transparent; color: whitesmoke; } .button.is-dark:hover, .button.is-dark.is-hovered { background-color: #2f2f2f; border-color: transparent; color: whitesmoke; } .button.is-dark:focus, .button.is-dark.is-focused { border-color: transparent; color: whitesmoke; } .button.is-dark:focus:not(:active), .button.is-dark.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); } .button.is-dark:active, .button.is-dark.is-active { background-color: #292929; border-color: transparent; color: whitesmoke; } .button.is-dark[disabled] { background-color: #363636; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-dark.is-inverted { background-color: whitesmoke; color: #363636; } .button.is-dark.is-inverted:hover { background-color: #e8e8e8; } .button.is-dark.is-inverted[disabled] { background-color: whitesmoke; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #363636; } .button.is-dark.is-loading:after { border-color: transparent transparent whitesmoke whitesmoke !important; } .button.is-dark.is-outlined { background-color: transparent; border-color: #363636; color: #363636; } .button.is-dark.is-outlined:hover, .button.is-dark.is-outlined:focus { background-color: #363636; border-color: #363636; color: whitesmoke; } .button.is-dark.is-outlined.is-loading:after { border-color: transparent transparent #363636 #363636 !important; } .button.is-dark.is-outlined[disabled] { background-color: transparent; border-color: #363636; -webkit-box-shadow: none; box-shadow: none; color: #363636; } .button.is-dark.is-inverted.is-outlined { background-color: transparent; border-color: whitesmoke; color: whitesmoke; } .button.is-dark.is-inverted.is-outlined:hover, .button.is-dark.is-inverted.is-outlined:focus { background-color: whitesmoke; color: #363636; } .button.is-dark.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: whitesmoke; -webkit-box-shadow: none; box-shadow: none; color: whitesmoke; } .button.is-primary { background-color: #00d1b2; border-color: transparent; color: #fff; } .button.is-primary:hover, .button.is-primary.is-hovered { background-color: #00c4a7; border-color: transparent; color: #fff; } .button.is-primary:focus, .button.is-primary.is-focused { border-color: transparent; color: #fff; } .button.is-primary:focus:not(:active), .button.is-primary.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25); box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25); } .button.is-primary:active, .button.is-primary.is-active { background-color: #00b89c; border-color: transparent; color: #fff; } .button.is-primary[disabled] { background-color: #00d1b2; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-primary.is-inverted { background-color: #fff; color: #00d1b2; } .button.is-primary.is-inverted:hover { background-color: #f2f2f2; } .button.is-primary.is-inverted[disabled] { background-color: #fff; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #00d1b2; } .button.is-primary.is-loading:after { border-color: transparent transparent #fff #fff !important; } .button.is-primary.is-outlined { background-color: transparent; border-color: #00d1b2; color: #00d1b2; } .button.is-primary.is-outlined:hover, .button.is-primary.is-outlined:focus { background-color: #00d1b2; border-color: #00d1b2; color: #fff; } .button.is-primary.is-outlined.is-loading:after { border-color: transparent transparent #00d1b2 #00d1b2 !important; } .button.is-primary.is-outlined[disabled] { background-color: transparent; border-color: #00d1b2; -webkit-box-shadow: none; box-shadow: none; color: #00d1b2; } .button.is-primary.is-inverted.is-outlined { background-color: transparent; border-color: #fff; color: #fff; } .button.is-primary.is-inverted.is-outlined:hover, .button.is-primary.is-inverted.is-outlined:focus { background-color: #fff; color: #00d1b2; } .button.is-primary.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: #fff; -webkit-box-shadow: none; box-shadow: none; color: #fff; } .button.is-link { background-color: #7a91c1; border-color: transparent; color: #fff; } .button.is-link:hover, .button.is-link.is-hovered { background-color: #276cda; border-color: transparent; color: #fff; } .button.is-link:focus, .button.is-link.is-focused { border-color: transparent; color: #fff; } .button.is-link:focus:not(:active), .button.is-link.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } .button.is-link:active, .button.is-link.is-active { background-color: #2366d1; border-color: transparent; color: #fff; } .button.is-link[disabled] { background-color: #7a91c1; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-link.is-inverted { background-color: #fff; color: #7a91c1; } .button.is-link.is-inverted:hover { background-color: #f2f2f2; } .button.is-link.is-inverted[disabled] { background-color: #fff; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #7a91c1; } .button.is-link.is-loading:after { border-color: transparent transparent #fff #fff !important; } .button.is-link.is-outlined { background-color: transparent; border-color: #7a91c1; color: #7a91c1; } .button.is-link.is-outlined:hover, .button.is-link.is-outlined:focus { background-color: #7a91c1; border-color: #7a91c1; color: #fff; } .button.is-link.is-outlined.is-loading:after { border-color: transparent transparent #7a91c1 #7a91c1 !important; } .button.is-link.is-outlined[disabled] { background-color: transparent; border-color: #7a91c1; -webkit-box-shadow: none; box-shadow: none; color: #7a91c1; } .button.is-link.is-inverted.is-outlined { background-color: transparent; border-color: #fff; color: #fff; } .button.is-link.is-inverted.is-outlined:hover, .button.is-link.is-inverted.is-outlined:focus { background-color: #fff; color: #7a91c1; } .button.is-link.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: #fff; -webkit-box-shadow: none; box-shadow: none; color: #fff; } .button.is-info { background-color: #209cee; border-color: transparent; color: #fff; } .button.is-info:hover, .button.is-info.is-hovered { background-color: #1496ed; border-color: transparent; color: #fff; } .button.is-info:focus, .button.is-info.is-focused { border-color: transparent; color: #fff; } .button.is-info:focus:not(:active), .button.is-info.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); } .button.is-info:active, .button.is-info.is-active { background-color: #118fe4; border-color: transparent; color: #fff; } .button.is-info[disabled] { background-color: #209cee; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-info.is-inverted { background-color: #fff; color: #209cee; } .button.is-info.is-inverted:hover { background-color: #f2f2f2; } .button.is-info.is-inverted[disabled] { background-color: #fff; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #209cee; } .button.is-info.is-loading:after { border-color: transparent transparent #fff #fff !important; } .button.is-info.is-outlined { background-color: transparent; border-color: #209cee; color: #209cee; } .button.is-info.is-outlined:hover, .button.is-info.is-outlined:focus { background-color: #209cee; border-color: #209cee; color: #fff; } .button.is-info.is-outlined.is-loading:after { border-color: transparent transparent #209cee #209cee !important; } .button.is-info.is-outlined[disabled] { background-color: transparent; border-color: #209cee; -webkit-box-shadow: none; box-shadow: none; color: #209cee; } .button.is-info.is-inverted.is-outlined { background-color: transparent; border-color: #fff; color: #fff; } .button.is-info.is-inverted.is-outlined:hover, .button.is-info.is-inverted.is-outlined:focus { background-color: #fff; color: #209cee; } .button.is-info.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: #fff; -webkit-box-shadow: none; box-shadow: none; color: #fff; } .button.is-success { background-color: #23d160; border-color: transparent; color: #fff; } .button.is-success:hover, .button.is-success.is-hovered { background-color: #22c65b; border-color: transparent; color: #fff; } .button.is-success:focus, .button.is-success.is-focused { border-color: transparent; color: #fff; } .button.is-success:focus:not(:active), .button.is-success.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); } .button.is-success:active, .button.is-success.is-active { background-color: #20bc56; border-color: transparent; color: #fff; } .button.is-success[disabled] { background-color: #23d160; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-success.is-inverted { background-color: #fff; color: #23d160; } .button.is-success.is-inverted:hover { background-color: #f2f2f2; } .button.is-success.is-inverted[disabled] { background-color: #fff; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #23d160; } .button.is-success.is-loading:after { border-color: transparent transparent #fff #fff !important; } .button.is-success.is-outlined { background-color: transparent; border-color: #23d160; color: #23d160; } .button.is-success.is-outlined:hover, .button.is-success.is-outlined:focus { background-color: #23d160; border-color: #23d160; color: #fff; } .button.is-success.is-outlined.is-loading:after { border-color: transparent transparent #23d160 #23d160 !important; } .button.is-success.is-outlined[disabled] { background-color: transparent; border-color: #23d160; -webkit-box-shadow: none; box-shadow: none; color: #23d160; } .button.is-success.is-inverted.is-outlined { background-color: transparent; border-color: #fff; color: #fff; } .button.is-success.is-inverted.is-outlined:hover, .button.is-success.is-inverted.is-outlined:focus { background-color: #fff; color: #23d160; } .button.is-success.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: #fff; -webkit-box-shadow: none; box-shadow: none; color: #fff; } .button.is-warning { background-color: #ffdd57; border-color: transparent; color: rgba(0, 0, 0, 0.7); } .button.is-warning:hover, .button.is-warning.is-hovered { background-color: #ffdb4a; border-color: transparent; color: rgba(0, 0, 0, 0.7); } .button.is-warning:focus, .button.is-warning.is-focused { border-color: transparent; color: rgba(0, 0, 0, 0.7); } .button.is-warning:focus:not(:active), .button.is-warning.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); } .button.is-warning:active, .button.is-warning.is-active { background-color: #ffd83d; border-color: transparent; color: rgba(0, 0, 0, 0.7); } .button.is-warning[disabled] { background-color: #ffdd57; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-warning.is-inverted { background-color: rgba(0, 0, 0, 0.7); color: #ffdd57; } .button.is-warning.is-inverted:hover { background-color: rgba(0, 0, 0, 0.7); } .button.is-warning.is-inverted[disabled] { background-color: rgba(0, 0, 0, 0.7); border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #ffdd57; } .button.is-warning.is-loading:after { border-color: transparent transparent rgba(0, 0, 0, 0.7) rgba(0, 0, 0, 0.7) !important; } .button.is-warning.is-outlined { background-color: transparent; border-color: #ffdd57; color: #ffdd57; } .button.is-warning.is-outlined:hover, .button.is-warning.is-outlined:focus { background-color: #ffdd57; border-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } .button.is-warning.is-outlined.is-loading:after { border-color: transparent transparent #ffdd57 #ffdd57 !important; } .button.is-warning.is-outlined[disabled] { background-color: transparent; border-color: #ffdd57; -webkit-box-shadow: none; box-shadow: none; color: #ffdd57; } .button.is-warning.is-inverted.is-outlined { background-color: transparent; border-color: rgba(0, 0, 0, 0.7); color: rgba(0, 0, 0, 0.7); } .button.is-warning.is-inverted.is-outlined:hover, .button.is-warning.is-inverted.is-outlined:focus { background-color: rgba(0, 0, 0, 0.7); color: #ffdd57; } .button.is-warning.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: rgba(0, 0, 0, 0.7); -webkit-box-shadow: none; box-shadow: none; color: rgba(0, 0, 0, 0.7); } .button.is-danger { background-color: #ff3860; border-color: transparent; color: #fff; } .button.is-danger:hover, .button.is-danger.is-hovered { background-color: #ff2b56; border-color: transparent; color: #fff; } .button.is-danger:focus, .button.is-danger.is-focused { border-color: transparent; color: #fff; } .button.is-danger:focus:not(:active), .button.is-danger.is-focused:not(:active) { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); } .button.is-danger:active, .button.is-danger.is-active { background-color: #ff1f4b; border-color: transparent; color: #fff; } .button.is-danger[disabled] { background-color: #ff3860; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; } .button.is-danger.is-inverted { background-color: #fff; color: #ff3860; } .button.is-danger.is-inverted:hover { background-color: #f2f2f2; } .button.is-danger.is-inverted[disabled] { background-color: #fff; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; color: #ff3860; } .button.is-danger.is-loading:after { border-color: transparent transparent #fff #fff !important; } .button.is-danger.is-outlined { background-color: transparent; border-color: #ff3860; color: #ff3860; } .button.is-danger.is-outlined:hover, .button.is-danger.is-outlined:focus { background-color: #ff3860; border-color: #ff3860; color: #fff; } .button.is-danger.is-outlined.is-loading:after { border-color: transparent transparent #ff3860 #ff3860 !important; } .button.is-danger.is-outlined[disabled] { background-color: transparent; border-color: #ff3860; -webkit-box-shadow: none; box-shadow: none; color: #ff3860; } .button.is-danger.is-inverted.is-outlined { background-color: transparent; border-color: #fff; color: #fff; } .button.is-danger.is-inverted.is-outlined:hover, .button.is-danger.is-inverted.is-outlined:focus { background-color: #fff; color: #ff3860; } .button.is-danger.is-inverted.is-outlined[disabled] { background-color: transparent; border-color: #fff; -webkit-box-shadow: none; box-shadow: none; color: #fff; } .button.is-small { border-radius: 2px; font-size: 0.75rem; } .button.is-medium { font-size: 1.25rem; } .button.is-large { font-size: 1.5rem; } .button[disabled] { background-color: white; border-color: #dbdbdb; -webkit-box-shadow: none; box-shadow: none; opacity: 0.5; } .button.is-fullwidth { display: -webkit-box; display: -ms-flexbox; display: flex; width: 100%; } .button.is-loading { color: transparent !important; pointer-events: none; } .button.is-loading:after { -webkit-animation: spinAround 500ms infinite linear; animation: spinAround 500ms infinite linear; border: 2px solid #dbdbdb; border-radius: 290486px; border-right-color: transparent; border-top-color: transparent; content: ""; display: block; height: 1em; position: relative; width: 1em; position: absolute; left: calc(50% - (1em / 2)); top: calc(50% - (1em / 2)); position: absolute !important; } .button.is-static { background-color: whitesmoke; border-color: #dbdbdb; color: #7a7a7a; -webkit-box-shadow: none; box-shadow: none; pointer-events: none; } .buttons { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-wrap: wrap; flex-wrap: wrap; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } .buttons .button { margin-bottom: 0.5rem; } .buttons .button:not(:last-child) { margin-right: 0.5rem; } .buttons:last-child { margin-bottom: -0.5rem; } .buttons:not(:last-child) { margin-bottom: 1rem; } .buttons.has-addons .button:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .buttons.has-addons .button:not(:last-child) { border-bottom-right-radius: 0; border-top-right-radius: 0; margin-right: -1px; } .buttons.has-addons .button:last-child { margin-right: 0; } .buttons.has-addons .button:hover, .buttons.has-addons .button.is-hovered { z-index: 2; } .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 { z-index: 3; } .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 { z-index: 4; } .buttons.is-centered { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .buttons.is-right { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } .container { margin: 0 auto; position: relative; } @media screen and (min-width: 1024px) { .container { max-width: 960px; width: 960px; } .container.is-fluid { margin-left: 32px; margin-right: 32px; max-width: none; width: auto; } } @media screen and (max-width: 1215px) { .container.is-widescreen { max-width: 1152px; width: auto; } } @media screen and (max-width: 1407px) { .container.is-fullhd { max-width: 1344px; width: auto; } } @media screen and (min-width: 1216px) { .container { max-width: 1152px; width: 1152px; } } @media screen and (min-width: 1408px) { .container { max-width: 1344px; width: 1344px; } } .content:not(:last-child) { margin-bottom: 1.5rem; } .content li + li { margin-top: 0.25em; } .content p:not(:last-child), .content dl:not(:last-child), .content ol:not(:last-child), .content ul:not(:last-child), .content blockquote:not(:last-child), .content pre:not(:last-child), .content table:not(:last-child) { margin-bottom: 1em; } .content h1, .content h2, .content h3, .content h4, .content h5, .content h6 { color: #363636; font-weight: 400; line-height: 1.125; } .content h1 { font-size: 2em; margin-bottom: 0.5em; } .content h1:not(:first-child) { margin-top: 1em; } .content h2 { font-size: 1.75em; margin-bottom: 0.5714em; } .content h2:not(:first-child) { margin-top: 1.1428em; } .content h3 { font-size: 1.5em; margin-bottom: 0.6666em; } .content h3:not(:first-child) { margin-top: 1.3333em; } .content h4 { font-size: 1.25em; margin-bottom: 0.8em; } .content h5 { font-size: 1.125em; margin-bottom: 0.8888em; } .content h6 { font-size: 1em; margin-bottom: 1em; } .content blockquote { background-color: whitesmoke; border-left: 5px solid #dbdbdb; padding: 1.25em 1.5em; } .content ol { list-style: decimal outside; margin-left: 2em; margin-top: 1em; } .content ul { list-style: disc outside; margin-left: 2em; margin-top: 1em; } .content ul ul { list-style-type: circle; margin-top: 0.5em; } .content ul ul ul { list-style-type: square; } .content dd { margin-left: 2em; } .content figure { margin-left: 2em; margin-right: 2em; text-align: center; } .content figure:not(:first-child) { margin-top: 2em; } .content figure:not(:last-child) { margin-bottom: 2em; } .content figure img { display: inline-block; } .content figure figcaption { font-style: italic; } .content pre { -webkit-overflow-scrolling: touch; overflow-x: auto; padding: 1.25em 1.5em; white-space: pre; word-wrap: normal; } .content sup, .content sub { font-size: 75%; } .content table { width: 100%; } .content table td, .content table th { border: 1px solid #dbdbdb; border-width: 0 0 1px; padding: 0.5em 0.75em; vertical-align: top; } .content table th { color: #363636; text-align: left; } .content table tr:hover { background-color: whitesmoke; } .content table thead td, .content table thead th { border-width: 0 0 2px; color: #363636; } .content table tfoot td, .content table tfoot th { border-width: 2px 0 0; color: #363636; } .content table tbody tr:last-child td, .content table tbody tr:last-child th { border-bottom-width: 0; } .content.is-small { font-size: 0.75rem; } .content.is-medium { font-size: 1.25rem; } .content.is-large { font-size: 1.5rem; } .input, .textarea { -moz-appearance: none; -webkit-appearance: none; -webkit-box-align: center; -ms-flex-align: center; align-items: center; border: 1px solid transparent; border-radius: 3px; -webkit-box-shadow: none; box-shadow: none; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; font-size: 1rem; height: 2.25em; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; line-height: 1.5; padding-bottom: calc(0.375em - 1px); padding-left: calc(0.625em - 1px); padding-right: calc(0.625em - 1px); padding-top: calc(0.375em - 1px); position: relative; vertical-align: top; background-color: white; border-color: #dbdbdb; color: #363636; -webkit-box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1); box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.1); max-width: 100%; width: 100%; } .input:focus, .input.is-focused, .input:active, .input.is-active, .textarea:focus, .textarea.is-focused, .textarea:active, .textarea.is-active { outline: none; } .input[disabled], .textarea[disabled] { cursor: not-allowed; } .input::-moz-placeholder, .textarea::-moz-placeholder { color: rgba(54, 54, 54, 0.3); } .input::-webkit-input-placeholder, .textarea::-webkit-input-placeholder { color: rgba(54, 54, 54, 0.3); } .input:-moz-placeholder, .textarea:-moz-placeholder { color: rgba(54, 54, 54, 0.3); } .input:-ms-input-placeholder, .textarea:-ms-input-placeholder { color: rgba(54, 54, 54, 0.3); } .input:hover, .input.is-hovered, .textarea:hover, .textarea.is-hovered { border-color: #b5b5b5; } .input:focus, .input.is-focused, .input:active, .input.is-active, .textarea:focus, .textarea.is-focused, .textarea:active, .textarea.is-active { border-color: #7a91c1; -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } .input[disabled], .textarea[disabled] { background-color: whitesmoke; border-color: whitesmoke; -webkit-box-shadow: none; box-shadow: none; color: #7a7a7a; } .input[disabled]::-moz-placeholder, .textarea[disabled]::-moz-placeholder { color: rgba(122, 122, 122, 0.3); } .input[disabled]::-webkit-input-placeholder, .textarea[disabled]::-webkit-input-placeholder { color: rgba(122, 122, 122, 0.3); } .input[disabled]:-moz-placeholder, .textarea[disabled]:-moz-placeholder { color: rgba(122, 122, 122, 0.3); } .input[disabled]:-ms-input-placeholder, .textarea[disabled]:-ms-input-placeholder { color: rgba(122, 122, 122, 0.3); } .input[type="search"], .textarea[type="search"] { border-radius: 290486px; } .input[readonly], .textarea[readonly] { -webkit-box-shadow: none; box-shadow: none; } .input.is-white, .textarea.is-white { border-color: white; } .input.is-white:focus, .input.is-white.is-focused, .input.is-white:active, .input.is-white.is-active, .textarea.is-white:focus, .textarea.is-white.is-focused, .textarea.is-white:active, .textarea.is-white.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); } .input.is-black, .textarea.is-black { border-color: #0a0a0a; } .input.is-black:focus, .input.is-black.is-focused, .input.is-black:active, .input.is-black.is-active, .textarea.is-black:focus, .textarea.is-black.is-focused, .textarea.is-black:active, .textarea.is-black.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); } .input.is-light, .textarea.is-light { border-color: whitesmoke; } .input.is-light:focus, .input.is-light.is-focused, .input.is-light:active, .input.is-light.is-active, .textarea.is-light:focus, .textarea.is-light.is-focused, .textarea.is-light:active, .textarea.is-light.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); } .input.is-dark, .textarea.is-dark { border-color: #363636; } .input.is-dark:focus, .input.is-dark.is-focused, .input.is-dark:active, .input.is-dark.is-active, .textarea.is-dark:focus, .textarea.is-dark.is-focused, .textarea.is-dark:active, .textarea.is-dark.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); } .input.is-primary, .textarea.is-primary { border-color: #00d1b2; } .input.is-primary:focus, .input.is-primary.is-focused, .input.is-primary:active, .input.is-primary.is-active, .textarea.is-primary:focus, .textarea.is-primary.is-focused, .textarea.is-primary:active, .textarea.is-primary.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25); box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25); } .input.is-link, .textarea.is-link { border-color: #7a91c1; } .input.is-link:focus, .input.is-link.is-focused, .input.is-link:active, .input.is-link.is-active, .textarea.is-link:focus, .textarea.is-link.is-focused, .textarea.is-link:active, .textarea.is-link.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } .input.is-info, .textarea.is-info { border-color: #209cee; } .input.is-info:focus, .input.is-info.is-focused, .input.is-info:active, .input.is-info.is-active, .textarea.is-info:focus, .textarea.is-info.is-focused, .textarea.is-info:active, .textarea.is-info.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); } .input.is-success, .textarea.is-success { border-color: #23d160; } .input.is-success:focus, .input.is-success.is-focused, .input.is-success:active, .input.is-success.is-active, .textarea.is-success:focus, .textarea.is-success.is-focused, .textarea.is-success:active, .textarea.is-success.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); } .input.is-warning, .textarea.is-warning { border-color: #ffdd57; } .input.is-warning:focus, .input.is-warning.is-focused, .input.is-warning:active, .input.is-warning.is-active, .textarea.is-warning:focus, .textarea.is-warning.is-focused, .textarea.is-warning:active, .textarea.is-warning.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); } .input.is-danger, .textarea.is-danger { border-color: #ff3860; } .input.is-danger:focus, .input.is-danger.is-focused, .input.is-danger:active, .input.is-danger.is-active, .textarea.is-danger:focus, .textarea.is-danger.is-focused, .textarea.is-danger:active, .textarea.is-danger.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); } .input.is-small, .textarea.is-small { border-radius: 2px; font-size: 0.75rem; } .input.is-medium, .textarea.is-medium { font-size: 1.25rem; } .input.is-large, .textarea.is-large { font-size: 1.5rem; } .input.is-fullwidth, .textarea.is-fullwidth { display: block; width: 100%; } .input.is-inline, .textarea.is-inline { display: inline; width: auto; } .input.is-static { background-color: transparent; border-color: transparent; -webkit-box-shadow: none; box-shadow: none; padding-left: 0; padding-right: 0; } .textarea { display: block; max-width: 100%; min-width: 100%; padding: 0.625em; resize: vertical; } .textarea:not([rows]) { max-height: 600px; min-height: 120px; } .textarea[rows] { height: unset; } .textarea.has-fixed-size { resize: none; } .checkbox, .radio { cursor: pointer; display: inline-block; line-height: 1.25; position: relative; } .checkbox input, .radio input { cursor: pointer; } .checkbox:hover, .radio:hover { color: #363636; } .checkbox[disabled], .radio[disabled] { color: #7a7a7a; cursor: not-allowed; } .radio + .radio { margin-left: 0.5em; } .select { display: inline-block; max-width: 100%; position: relative; vertical-align: top; } .select:not(.is-multiple) { height: 2.25em; } .select:not(.is-multiple)::after { border: 1px solid #7a91c1; border-right: 0; border-top: 0; content: " "; display: block; height: 0.5em; pointer-events: none; position: absolute; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-transform-origin: center; transform-origin: center; width: 0.5em; margin-top: -0.375em; right: 1.125em; top: 50%; z-index: 4; } .select select { -moz-appearance: none; -webkit-appearance: none; -webkit-box-align: center; -ms-flex-align: center; align-items: center; border: 1px solid transparent; border-radius: 3px; -webkit-box-shadow: none; box-shadow: none; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; font-size: 1rem; height: 2.25em; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; line-height: 1.5; padding-bottom: calc(0.375em - 1px); padding-left: calc(0.625em - 1px); padding-right: calc(0.625em - 1px); padding-top: calc(0.375em - 1px); position: relative; vertical-align: top; background-color: white; border-color: #dbdbdb; color: #363636; cursor: pointer; display: block; font-size: 1em; max-width: 100%; outline: none; } .select select:focus, .select select.is-focused, .select select:active, .select select.is-active { outline: none; } .select select[disabled] { cursor: not-allowed; } .select select::-moz-placeholder { color: rgba(54, 54, 54, 0.3); } .select select::-webkit-input-placeholder { color: rgba(54, 54, 54, 0.3); } .select select:-moz-placeholder { color: rgba(54, 54, 54, 0.3); } .select select:-ms-input-placeholder { color: rgba(54, 54, 54, 0.3); } .select select:hover, .select select.is-hovered { border-color: #b5b5b5; } .select select:focus, .select select.is-focused, .select select:active, .select select.is-active { border-color: #7a91c1; -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } .select select[disabled] { background-color: whitesmoke; border-color: whitesmoke; -webkit-box-shadow: none; box-shadow: none; color: #7a7a7a; } .select select[disabled]::-moz-placeholder { color: rgba(122, 122, 122, 0.3); } .select select[disabled]::-webkit-input-placeholder { color: rgba(122, 122, 122, 0.3); } .select select[disabled]:-moz-placeholder { color: rgba(122, 122, 122, 0.3); } .select select[disabled]:-ms-input-placeholder { color: rgba(122, 122, 122, 0.3); } .select select::-ms-expand { display: none; } .select select[disabled]:hover { border-color: whitesmoke; } .select select:not([multiple]) { padding-right: 2.5em; } .select select[multiple] { height: unset; padding: 0; } .select select[multiple] option { padding: 0.5em 1em; } .select:hover::after { border-color: #363636; } .select.is-white select { border-color: white; } .select.is-white select:focus, .select.is-white select.is-focused, .select.is-white select:active, .select.is-white select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 255, 255, 0.25); } .select.is-black select { border-color: #0a0a0a; } .select.is-black select:focus, .select.is-black select.is-focused, .select.is-black select:active, .select.is-black select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); box-shadow: 0 0 0 0.125em rgba(10, 10, 10, 0.25); } .select.is-light select { border-color: whitesmoke; } .select.is-light select:focus, .select.is-light select.is-focused, .select.is-light select:active, .select.is-light select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); box-shadow: 0 0 0 0.125em rgba(245, 245, 245, 0.25); } .select.is-dark select { border-color: #363636; } .select.is-dark select:focus, .select.is-dark select.is-focused, .select.is-dark select:active, .select.is-dark select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); box-shadow: 0 0 0 0.125em rgba(54, 54, 54, 0.25); } .select.is-primary select { border-color: #00d1b2; } .select.is-primary select:focus, .select.is-primary select.is-focused, .select.is-primary select:active, .select.is-primary select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25); box-shadow: 0 0 0 0.125em rgba(0, 209, 178, 0.25); } .select.is-link select { border-color: #7a91c1; } .select.is-link select:focus, .select.is-link select.is-focused, .select.is-link select:active, .select.is-link select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); box-shadow: 0 0 0 0.125em rgba(50, 115, 220, 0.25); } .select.is-info select { border-color: #209cee; } .select.is-info select:focus, .select.is-info select.is-focused, .select.is-info select:active, .select.is-info select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); box-shadow: 0 0 0 0.125em rgba(32, 156, 238, 0.25); } .select.is-success select { border-color: #23d160; } .select.is-success select:focus, .select.is-success select.is-focused, .select.is-success select:active, .select.is-success select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); box-shadow: 0 0 0 0.125em rgba(35, 209, 96, 0.25); } .select.is-warning select { border-color: #ffdd57; } .select.is-warning select:focus, .select.is-warning select.is-focused, .select.is-warning select:active, .select.is-warning select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 221, 87, 0.25); } .select.is-danger select { border-color: #ff3860; } .select.is-danger select:focus, .select.is-danger select.is-focused, .select.is-danger select:active, .select.is-danger select.is-active { -webkit-box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); box-shadow: 0 0 0 0.125em rgba(255, 56, 96, 0.25); } .select.is-small { border-radius: 2px; font-size: 0.75rem; } .select.is-medium { font-size: 1.25rem; } .select.is-large { font-size: 1.5rem; } .select.is-disabled::after { border-color: #7a7a7a; } .select.is-fullwidth { width: 100%; } .select.is-fullwidth select { width: 100%; } .select.is-loading::after { -webkit-animation: spinAround 500ms infinite linear; animation: spinAround 500ms infinite linear; border: 2px solid #dbdbdb; border-radius: 290486px; border-right-color: transparent; border-top-color: transparent; content: ""; display: block; height: 1em; position: relative; width: 1em; margin-top: 0; position: absolute; right: 0.625em; top: 0.625em; -webkit-transform: none; transform: none; } .select.is-loading.is-small:after { font-size: 0.75rem; } .select.is-loading.is-medium:after { font-size: 1.25rem; } .select.is-loading.is-large:after { font-size: 1.5rem; } .file { -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; position: relative; } .file.is-white .file-cta { background-color: white; border-color: transparent; color: #0a0a0a; } .file.is-white:hover .file-cta, .file.is-white.is-hovered .file-cta { background-color: #f9f9f9; border-color: transparent; color: #0a0a0a; } .file.is-white:focus .file-cta, .file.is-white.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(255, 255, 255, 0.25); box-shadow: 0 0 0.5em rgba(255, 255, 255, 0.25); color: #0a0a0a; } .file.is-white:active .file-cta, .file.is-white.is-active .file-cta { background-color: #f2f2f2; border-color: transparent; color: #0a0a0a; } .file.is-black .file-cta { background-color: #0a0a0a; border-color: transparent; color: white; } .file.is-black:hover .file-cta, .file.is-black.is-hovered .file-cta { background-color: #040404; border-color: transparent; color: white; } .file.is-black:focus .file-cta, .file.is-black.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(10, 10, 10, 0.25); box-shadow: 0 0 0.5em rgba(10, 10, 10, 0.25); color: white; } .file.is-black:active .file-cta, .file.is-black.is-active .file-cta { background-color: black; border-color: transparent; color: white; } .file.is-light .file-cta { background-color: whitesmoke; border-color: transparent; color: #363636; } .file.is-light:hover .file-cta, .file.is-light.is-hovered .file-cta { background-color: #eeeeee; border-color: transparent; color: #363636; } .file.is-light:focus .file-cta, .file.is-light.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(245, 245, 245, 0.25); box-shadow: 0 0 0.5em rgba(245, 245, 245, 0.25); color: #363636; } .file.is-light:active .file-cta, .file.is-light.is-active .file-cta { background-color: #e8e8e8; border-color: transparent; color: #363636; } .file.is-dark .file-cta { background-color: #363636; border-color: transparent; color: whitesmoke; } .file.is-dark:hover .file-cta, .file.is-dark.is-hovered .file-cta { background-color: #2f2f2f; border-color: transparent; color: whitesmoke; } .file.is-dark:focus .file-cta, .file.is-dark.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(54, 54, 54, 0.25); box-shadow: 0 0 0.5em rgba(54, 54, 54, 0.25); color: whitesmoke; } .file.is-dark:active .file-cta, .file.is-dark.is-active .file-cta { background-color: #292929; border-color: transparent; color: whitesmoke; } .file.is-primary .file-cta { background-color: #00d1b2; border-color: transparent; color: #fff; } .file.is-primary:hover .file-cta, .file.is-primary.is-hovered .file-cta { background-color: #00c4a7; border-color: transparent; color: #fff; } .file.is-primary:focus .file-cta, .file.is-primary.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(0, 209, 178, 0.25); box-shadow: 0 0 0.5em rgba(0, 209, 178, 0.25); color: #fff; } .file.is-primary:active .file-cta, .file.is-primary.is-active .file-cta { background-color: #00b89c; border-color: transparent; color: #fff; } .file.is-link .file-cta { background-color: #7a91c1; border-color: transparent; color: #fff; } .file.is-link:hover .file-cta, .file.is-link.is-hovered .file-cta { background-color: #276cda; border-color: transparent; color: #fff; } .file.is-link:focus .file-cta, .file.is-link.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(50, 115, 220, 0.25); box-shadow: 0 0 0.5em rgba(50, 115, 220, 0.25); color: #fff; } .file.is-link:active .file-cta, .file.is-link.is-active .file-cta { background-color: #2366d1; border-color: transparent; color: #fff; } .file.is-info .file-cta { background-color: #209cee; border-color: transparent; color: #fff; } .file.is-info:hover .file-cta, .file.is-info.is-hovered .file-cta { background-color: #1496ed; border-color: transparent; color: #fff; } .file.is-info:focus .file-cta, .file.is-info.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(32, 156, 238, 0.25); box-shadow: 0 0 0.5em rgba(32, 156, 238, 0.25); color: #fff; } .file.is-info:active .file-cta, .file.is-info.is-active .file-cta { background-color: #118fe4; border-color: transparent; color: #fff; } .file.is-success .file-cta { background-color: #23d160; border-color: transparent; color: #fff; } .file.is-success:hover .file-cta, .file.is-success.is-hovered .file-cta { background-color: #22c65b; border-color: transparent; color: #fff; } .file.is-success:focus .file-cta, .file.is-success.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(35, 209, 96, 0.25); box-shadow: 0 0 0.5em rgba(35, 209, 96, 0.25); color: #fff; } .file.is-success:active .file-cta, .file.is-success.is-active .file-cta { background-color: #20bc56; border-color: transparent; color: #fff; } .file.is-warning .file-cta { background-color: #ffdd57; border-color: transparent; color: rgba(0, 0, 0, 0.7); } .file.is-warning:hover .file-cta, .file.is-warning.is-hovered .file-cta { background-color: #ffdb4a; border-color: transparent; color: rgba(0, 0, 0, 0.7); } .file.is-warning:focus .file-cta, .file.is-warning.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(255, 221, 87, 0.25); box-shadow: 0 0 0.5em rgba(255, 221, 87, 0.25); color: rgba(0, 0, 0, 0.7); } .file.is-warning:active .file-cta, .file.is-warning.is-active .file-cta { background-color: #ffd83d; border-color: transparent; color: rgba(0, 0, 0, 0.7); } .file.is-danger .file-cta { background-color: #ff3860; border-color: transparent; color: #fff; } .file.is-danger:hover .file-cta, .file.is-danger.is-hovered .file-cta { background-color: #ff2b56; border-color: transparent; color: #fff; } .file.is-danger:focus .file-cta, .file.is-danger.is-focused .file-cta { border-color: transparent; -webkit-box-shadow: 0 0 0.5em rgba(255, 56, 96, 0.25); box-shadow: 0 0 0.5em rgba(255, 56, 96, 0.25); color: #fff; } .file.is-danger:active .file-cta, .file.is-danger.is-active .file-cta { background-color: #ff1f4b; border-color: transparent; color: #fff; } .file.is-small { font-size: 0.75rem; } .file.is-medium { font-size: 1.25rem; } .file.is-medium .file-icon .fa { font-size: 21px; } .file.is-large { font-size: 1.5rem; } .file.is-large .file-icon .fa { font-size: 28px; } .file.has-name .file-cta { border-bottom-right-radius: 0; border-top-right-radius: 0; } .file.has-name .file-name { border-bottom-left-radius: 0; border-top-left-radius: 0; } .file.has-name.is-empty .file-cta { border-radius: 3px; } .file.has-name.is-empty .file-name { display: none; } .file.is-centered { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .file.is-right { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } .file.is-boxed .file-label { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } .file.is-boxed .file-cta { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; height: auto; padding: 1em 3em; } .file.is-boxed .file-name { border-width: 0 1px 1px; } .file.is-boxed .file-icon { height: 1.5em; width: 1.5em; } .file.is-boxed .file-icon .fa { font-size: 21px; } .file.is-boxed.is-small .file-icon .fa { font-size: 14px; } .file.is-boxed.is-medium .file-icon .fa { font-size: 28px; } .file.is-boxed.is-large .file-icon .fa { font-size: 35px; } .file.is-boxed.has-name .file-cta { border-radius: 3px 3px 0 0; } .file.is-boxed.has-name .file-name { border-radius: 0 0 3px 3px; border-width: 0 1px 1px; } .file.is-right .file-cta { border-radius: 0 3px 3px 0; } .file.is-right .file-name { border-radius: 3px 0 0 3px; border-width: 1px 0 1px 1px; -webkit-box-ordinal-group: 0; -ms-flex-order: -1; order: -1; } .file.is-fullwidth .file-label { width: 100%; } .file.is-fullwidth .file-name { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; max-width: none; } .file-label { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; cursor: pointer; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; overflow: hidden; position: relative; } .file-label:hover .file-cta { background-color: #eeeeee; color: #363636; } .file-label:hover .file-name { border-color: #d5d5d5; } .file-label:active .file-cta { background-color: #e8e8e8; color: #363636; } .file-label:active .file-name { border-color: #cfcfcf; } .file-input { height: 0.01em; left: 0; outline: none; position: absolute; top: 0; width: 0.01em; } .file-cta, .file-name { -moz-appearance: none; -webkit-appearance: none; -webkit-box-align: center; -ms-flex-align: center; align-items: center; border: 1px solid transparent; border-radius: 3px; -webkit-box-shadow: none; box-shadow: none; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; font-size: 1rem; height: 2.25em; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; line-height: 1.5; padding-bottom: calc(0.375em - 1px); padding-left: calc(0.625em - 1px); padding-right: calc(0.625em - 1px); padding-top: calc(0.375em - 1px); position: relative; vertical-align: top; border-color: #dbdbdb; border-radius: 3px; font-size: 1em; padding-left: 1em; padding-right: 1em; white-space: nowrap; } .file-cta:focus, .file-cta.is-focused, .file-cta:active, .file-cta.is-active, .file-name:focus, .file-name.is-focused, .file-name:active, .file-name.is-active { outline: none; } .file-cta[disabled], .file-name[disabled] { cursor: not-allowed; } .file-cta { background-color: whitesmoke; color: #4a4a4a; } .file-name { border-color: #dbdbdb; border-style: solid; border-width: 1px 1px 1px 0; display: block; max-width: 16em; overflow: hidden; text-align: left; text-overflow: ellipsis; } .file-icon { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; height: 1em; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; margin-right: 0.5em; width: 1em; } .file-icon .fa { font-size: 14px; } .label { color: #363636; display: block; font-size: 1rem; font-weight: 700; } .label:not(:last-child) { margin-bottom: 0.5em; } .label.is-small { font-size: 0.75rem; } .label.is-medium { font-size: 1.25rem; } .label.is-large { font-size: 1.5rem; } .help { display: block; font-size: 0.75rem; margin-top: 0.25rem; } .help.is-white { color: white; } .help.is-black { color: #0a0a0a; } .help.is-light { color: whitesmoke; } .help.is-dark { color: #363636; } .help.is-primary { color: #00d1b2; } .help.is-link { color: #7a91c1; } .help.is-info { color: #209cee; } .help.is-success { color: #23d160; } .help.is-warning { color: #ffdd57; } .help.is-danger { color: #ff3860; } .field:not(:last-child) { margin-bottom: 0.75rem; } .field.has-addons { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } .field.has-addons .control:not(:last-child) { margin-right: -1px; } .field.has-addons .control:first-child .button, .field.has-addons .control:first-child .input, .field.has-addons .control:first-child .select select { border-bottom-left-radius: 3px; border-top-left-radius: 3px; } .field.has-addons .control:last-child .button, .field.has-addons .control:last-child .input, .field.has-addons .control:last-child .select select { border-bottom-right-radius: 3px; border-top-right-radius: 3px; } .field.has-addons .control .button, .field.has-addons .control .input, .field.has-addons .control .select select { border-radius: 0; } .field.has-addons .control .button:hover, .field.has-addons .control .button.is-hovered, .field.has-addons .control .input:hover, .field.has-addons .control .input.is-hovered, .field.has-addons .control .select select:hover, .field.has-addons .control .select select.is-hovered { z-index: 2; } .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, .field.has-addons .control .input:focus, .field.has-addons .control .input.is-focused, .field.has-addons .control .input:active, .field.has-addons .control .input.is-active, .field.has-addons .control .select select:focus, .field.has-addons .control .select select.is-focused, .field.has-addons .control .select select:active, .field.has-addons .control .select select.is-active { z-index: 3; } .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, .field.has-addons .control .input:focus:hover, .field.has-addons .control .input.is-focused:hover, .field.has-addons .control .input:active:hover, .field.has-addons .control .input.is-active:hover, .field.has-addons .control .select select:focus:hover, .field.has-addons .control .select select.is-focused:hover, .field.has-addons .control .select select:active:hover, .field.has-addons .control .select select.is-active:hover { z-index: 4; } .field.has-addons .control.is-expanded { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } .field.has-addons.has-addons-centered { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .field.has-addons.has-addons-right { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } .field.has-addons.has-addons-fullwidth .control { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; } .field.is-grouped { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } .field.is-grouped > .control { -ms-flex-negative: 0; flex-shrink: 0; } .field.is-grouped > .control:not(:last-child) { margin-bottom: 0; margin-right: 0.75rem; } .field.is-grouped > .control.is-expanded { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; } .field.is-grouped.is-grouped-centered { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .field.is-grouped.is-grouped-right { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } .field.is-grouped.is-grouped-multiline { -ms-flex-wrap: wrap; flex-wrap: wrap; } .field.is-grouped.is-grouped-multiline > .control:last-child, .field.is-grouped.is-grouped-multiline > .control:not(:last-child) { margin-bottom: 0.75rem; } .field.is-grouped.is-grouped-multiline:last-child { margin-bottom: -0.75rem; } .field.is-grouped.is-grouped-multiline:not(:last-child) { margin-bottom: 0; } @media screen and (min-width: 769px), print { .field.is-horizontal { display: -webkit-box; display: -ms-flexbox; display: flex; } } .field-label .label { font-size: inherit; } @media screen and (max-width: 768px) { .field-label { margin-bottom: 0.5rem; } } @media screen and (min-width: 769px), print { .field-label { -ms-flex-preferred-size: 0; flex-basis: 0; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; margin-right: 1.5rem; text-align: right; } .field-label.is-small { font-size: 0.75rem; padding-top: 0.375em; } .field-label.is-normal { padding-top: 0.375em; } .field-label.is-medium { font-size: 1.25rem; padding-top: 0.375em; } .field-label.is-large { font-size: 1.5rem; padding-top: 0.375em; } } .field-body .field .field { margin-bottom: 0; } @media screen and (min-width: 769px), print { .field-body { display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-preferred-size: 0; flex-basis: 0; -webkit-box-flex: 5; -ms-flex-positive: 5; flex-grow: 5; -ms-flex-negative: 1; flex-shrink: 1; } .field-body .field { margin-bottom: 0; } .field-body > .field { -ms-flex-negative: 1; flex-shrink: 1; } .field-body > .field:not(.is-narrow) { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } .field-body > .field:not(:last-child) { margin-right: 0.75rem; } } .control { font-size: 1rem; position: relative; text-align: left; } .control.has-icon .icon { color: #dbdbdb; height: 2.25em; pointer-events: none; position: absolute; top: 0; width: 2.25em; z-index: 4; } .control.has-icon .input:focus + .icon { color: #7a7a7a; } .control.has-icon .input.is-small + .icon { font-size: 0.75rem; } .control.has-icon .input.is-medium + .icon { font-size: 1.25rem; } .control.has-icon .input.is-large + .icon { font-size: 1.5rem; } .control.has-icon:not(.has-icon-right) .icon { left: 0; } .control.has-icon:not(.has-icon-right) .input { padding-left: 2.25em; } .control.has-icon.has-icon-right .icon { right: 0; } .control.has-icon.has-icon-right .input { padding-right: 2.25em; } .control.has-icons-left .input:focus ~ .icon, .control.has-icons-left .select:focus ~ .icon, .control.has-icons-right .input:focus ~ .icon, .control.has-icons-right .select:focus ~ .icon { color: #7a7a7a; } .control.has-icons-left .input.is-small ~ .icon, .control.has-icons-left .select.is-small ~ .icon, .control.has-icons-right .input.is-small ~ .icon, .control.has-icons-right .select.is-small ~ .icon { font-size: 0.75rem; } .control.has-icons-left .input.is-medium ~ .icon, .control.has-icons-left .select.is-medium ~ .icon, .control.has-icons-right .input.is-medium ~ .icon, .control.has-icons-right .select.is-medium ~ .icon { font-size: 1.25rem; } .control.has-icons-left .input.is-large ~ .icon, .control.has-icons-left .select.is-large ~ .icon, .control.has-icons-right .input.is-large ~ .icon, .control.has-icons-right .select.is-large ~ .icon { font-size: 1.5rem; } .control.has-icons-left .icon, .control.has-icons-right .icon { color: #dbdbdb; height: 2.25em; pointer-events: none; position: absolute; top: 0; width: 2.25em; z-index: 4; } .control.has-icons-left .input, .control.has-icons-left .select select { padding-left: 2.25em; } .control.has-icons-left .icon.is-left { left: 0; } .control.has-icons-right .input, .control.has-icons-right .select select { padding-right: 2.25em; } .control.has-icons-right .icon.is-right { right: 0; } .control.is-loading::after { -webkit-animation: spinAround 500ms infinite linear; animation: spinAround 500ms infinite linear; border: 2px solid #dbdbdb; border-radius: 290486px; border-right-color: transparent; border-top-color: transparent; content: ""; display: block; height: 1em; position: relative; width: 1em; position: absolute !important; right: 0.625em; top: 0.625em; } .control.is-loading.is-small:after { font-size: 0.75rem; } .control.is-loading.is-medium:after { font-size: 1.25rem; } .control.is-loading.is-large:after { font-size: 1.5rem; } .icon { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; height: 1.5rem; width: 1.5rem; } .icon.is-small { height: 1rem; width: 1rem; } .icon.is-medium { height: 2rem; width: 2rem; } .icon.is-large { height: 3rem; width: 3rem; } .image { display: block; position: relative; } .image img { display: block; height: auto; width: 100%; } .image.is-square img, .image.is-1by1 img, .image.is-4by3 img, .image.is-3by2 img, .image.is-16by9 img, .image.is-2by1 img { bottom: 0; left: 0; position: absolute; right: 0; top: 0; height: 100%; width: 100%; } .image.is-square, .image.is-1by1 { padding-top: 100%; } .image.is-4by3 { padding-top: 75%; } .image.is-3by2 { padding-top: 66.6666%; } .image.is-16by9 { padding-top: 56.25%; } .image.is-2by1 { padding-top: 50%; } .image.is-16x16 { height: 16px; width: 16px; } .image.is-24x24 { height: 24px; width: 24px; } .image.is-32x32 { height: 32px; width: 32px; } .image.is-48x48 { height: 48px; width: 48px; } .image.is-64x64 { height: 64px; width: 64px; } .image.is-96x96 { height: 96px; width: 96px; } .image.is-128x128 { height: 128px; width: 128px; } .notification { background-color: whitesmoke; border-radius: 3px; padding: 1.25rem 2.5rem 1.25rem 1.5rem; position: relative; } .notification:not(:last-child) { margin-bottom: 1.5rem; } .notification a:not(.button) { color: currentColor; text-decoration: underline; } .notification strong { color: currentColor; } .notification code, .notification pre { background: white; } .notification pre code { background: transparent; } .notification > .delete { position: absolute; right: 0.5em; top: 0.5em; } .notification .title, .notification .subtitle, .notification .content { color: currentColor; } .notification.is-white { background-color: white; color: #0a0a0a; } .notification.is-black { background-color: #0a0a0a; color: white; } .notification.is-light { background-color: whitesmoke; color: #363636; } .notification.is-dark { background-color: #363636; color: whitesmoke; } .notification.is-primary { background-color: #00d1b2; color: #fff; } .notification.is-link { background-color: #7a91c1; color: #fff; } .notification.is-info { background-color: #209cee; color: #fff; } .notification.is-success { background-color: #23d160; color: #fff; } .notification.is-warning { background-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } .notification.is-danger { background-color: #ff3860; color: #fff; } .progress { -moz-appearance: none; -webkit-appearance: none; border: none; border-radius: 290486px; display: block; height: 1rem; overflow: hidden; padding: 0; width: 100%; } .progress:not(:last-child) { margin-bottom: 1.5rem; } .progress::-webkit-progress-bar { background-color: #dbdbdb; } .progress::-webkit-progress-value { background-color: #4a4a4a; } .progress::-moz-progress-bar { background-color: #4a4a4a; } .progress::-ms-fill { background-color: #4a4a4a; border: none; } .progress.is-white::-webkit-progress-value { background-color: white; } .progress.is-white::-moz-progress-bar { background-color: white; } .progress.is-white::-ms-fill { background-color: white; } .progress.is-black::-webkit-progress-value { background-color: #0a0a0a; } .progress.is-black::-moz-progress-bar { background-color: #0a0a0a; } .progress.is-black::-ms-fill { background-color: #0a0a0a; } .progress.is-light::-webkit-progress-value { background-color: whitesmoke; } .progress.is-light::-moz-progress-bar { background-color: whitesmoke; } .progress.is-light::-ms-fill { background-color: whitesmoke; } .progress.is-dark::-webkit-progress-value { background-color: #363636; } .progress.is-dark::-moz-progress-bar { background-color: #363636; } .progress.is-dark::-ms-fill { background-color: #363636; } .progress.is-primary::-webkit-progress-value { background-color: #00d1b2; } .progress.is-primary::-moz-progress-bar { background-color: #00d1b2; } .progress.is-primary::-ms-fill { background-color: #00d1b2; } .progress.is-link::-webkit-progress-value { background-color: #7a91c1; } .progress.is-link::-moz-progress-bar { background-color: #7a91c1; } .progress.is-link::-ms-fill { background-color: #7a91c1; } .progress.is-info::-webkit-progress-value { background-color: #209cee; } .progress.is-info::-moz-progress-bar { background-color: #209cee; } .progress.is-info::-ms-fill { background-color: #209cee; } .progress.is-success::-webkit-progress-value { background-color: #23d160; } .progress.is-success::-moz-progress-bar { background-color: #23d160; } .progress.is-success::-ms-fill { background-color: #23d160; } .progress.is-warning::-webkit-progress-value { background-color: #ffdd57; } .progress.is-warning::-moz-progress-bar { background-color: #ffdd57; } .progress.is-warning::-ms-fill { background-color: #ffdd57; } .progress.is-danger::-webkit-progress-value { background-color: #ff3860; } .progress.is-danger::-moz-progress-bar { background-color: #ff3860; } .progress.is-danger::-ms-fill { background-color: #ff3860; } .progress.is-small { height: 0.75rem; } .progress.is-medium { height: 1.25rem; } .progress.is-large { height: 1.5rem; } .table { background-color: white; color: #363636; margin-bottom: 1.5rem; } .table td, .table th { border: 1px solid #dbdbdb; border-width: 0 0 1px; padding: 0.5em 0.75em; vertical-align: top; } .table td.is-white, .table th.is-white { background-color: white; border-color: white; color: #0a0a0a; } .table td.is-black, .table th.is-black { background-color: #0a0a0a; border-color: #0a0a0a; color: white; } .table td.is-light, .table th.is-light { background-color: whitesmoke; border-color: whitesmoke; color: #363636; } .table td.is-dark, .table th.is-dark { background-color: #363636; border-color: #363636; color: whitesmoke; } .table td.is-primary, .table th.is-primary { background-color: #00d1b2; border-color: #00d1b2; color: #fff; } .table td.is-link, .table th.is-link { background-color: #7a91c1; border-color: #7a91c1; color: #fff; } .table td.is-info, .table th.is-info { background-color: #209cee; border-color: #209cee; color: #fff; } .table td.is-success, .table th.is-success { background-color: #23d160; border-color: #23d160; color: #fff; } .table td.is-warning, .table th.is-warning { background-color: #ffdd57; border-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } .table td.is-danger, .table th.is-danger { background-color: #ff3860; border-color: #ff3860; color: #fff; } .table td.is-narrow, .table th.is-narrow { white-space: nowrap; width: 1%; } .table td.is-selected, .table th.is-selected { background-color: #00d1b2; color: #fff; } .table td.is-selected a, .table td.is-selected strong, .table th.is-selected a, .table th.is-selected strong { color: currentColor; } .table th { color: #363636; text-align: left; } .table tr.is-selected { background-color: #00d1b2; color: #fff; } .table tr.is-selected a, .table tr.is-selected strong { color: currentColor; } .table tr.is-selected td, .table tr.is-selected th { border-color: #fff; color: currentColor; } .table thead td, .table thead th { border-width: 0 0 2px; color: #363636; } .table tfoot td, .table tfoot th { border-width: 2px 0 0; color: #363636; } .table tbody tr:last-child td, .table tbody tr:last-child th { border-bottom-width: 0; } .table.is-bordered td, .table.is-bordered th { border-width: 1px; } .table.is-bordered tr:last-child td, .table.is-bordered tr:last-child th { border-bottom-width: 1px; } .table.is-fullwidth { width: 100%; } .table.is-hoverable tbody tr:not(.is-selected):hover { background-color: #fafafa; } .table.is-hoverable.is-striped tbody tr:not(.is-selected):hover { background-color: whitesmoke; } .table.is-narrow td, .table.is-narrow th { padding: 0.25em 0.5em; } .table.is-striped tbody tr:not(.is-selected):nth-child(even) { background-color: #fafafa; } .tags { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-wrap: wrap; flex-wrap: wrap; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } .tags .tag { margin-bottom: 0.5rem; } .tags .tag:not(:last-child) { margin-right: 0.5rem; } .tags:last-child { margin-bottom: -0.5rem; } .tags:not(:last-child) { margin-bottom: 1rem; } .tags.has-addons .tag { margin-right: 0; } .tags.has-addons .tag:not(:first-child) { border-bottom-left-radius: 0; border-top-left-radius: 0; } .tags.has-addons .tag:not(:last-child) { border-bottom-right-radius: 0; border-top-right-radius: 0; } .tags.is-centered { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .tags.is-centered .tag { margin-right: 0.25rem; margin-left: 0.25rem; } .tags.is-right { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } .tags.is-right .tag:not(:first-child) { margin-left: 0.5rem; } .tags.is-right .tag:not(:last-child) { margin-right: 0; } .tag:not(body) { -webkit-box-align: center; -ms-flex-align: center; align-items: center; background-color: whitesmoke; border-radius: 3px; color: #4a4a4a; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; font-size: 0.75rem; height: 2em; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; line-height: 1.5; padding-left: 0.75em; padding-right: 0.75em; white-space: nowrap; } .tag:not(body) .delete { margin-left: 0.25em; margin-right: -0.375em; } .tag:not(body).is-white { background-color: white; color: #0a0a0a; } .tag:not(body).is-black { background-color: #0a0a0a; color: white; } .tag:not(body).is-light { background-color: whitesmoke; color: #363636; } .tag:not(body).is-dark { background-color: #363636; color: whitesmoke; } .tag:not(body).is-primary { background-color: #00d1b2; color: #fff; } .tag:not(body).is-link { background-color: #7a91c1; color: #fff; } .tag:not(body).is-info { background-color: #209cee; color: #fff; } .tag:not(body).is-success { background-color: #23d160; color: #fff; } .tag:not(body).is-warning { background-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } .tag:not(body).is-danger { background-color: #ff3860; color: #fff; } .tag:not(body).is-medium { font-size: 1rem; } .tag:not(body).is-large { font-size: 1.25rem; } .tag:not(body) .icon:first-child:not(:last-child) { margin-left: -0.375em; margin-right: 0.1875em; } .tag:not(body) .icon:last-child:not(:first-child) { margin-left: 0.1875em; margin-right: -0.375em; } .tag:not(body) .icon:first-child:last-child { margin-left: -0.375em; margin-right: -0.375em; } .tag:not(body).is-delete { margin-left: 1px; padding: 0; position: relative; width: 2em; } .tag:not(body).is-delete:before, .tag:not(body).is-delete:after { background-color: currentColor; content: ""; display: block; left: 50%; position: absolute; top: 50%; -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg); transform: translateX(-50%) translateY(-50%) rotate(45deg); -webkit-transform-origin: center center; transform-origin: center center; } .tag:not(body).is-delete:before { height: 1px; width: 50%; } .tag:not(body).is-delete:after { height: 50%; width: 1px; } .tag:not(body).is-delete:hover, .tag:not(body).is-delete:focus { background-color: #e8e8e8; } .tag:not(body).is-delete:active { background-color: #dbdbdb; } .tag:not(body).is-rounded { border-radius: 290486px; } a.tag:hover { text-decoration: underline; } .title, .subtitle { word-break: break-word; } .title:not(:last-child), .subtitle:not(:last-child) { margin-bottom: 1.5rem; } .title em, .title span, .subtitle em, .subtitle span { font-weight: inherit; } .title .tag, .subtitle .tag { vertical-align: middle; } .title { color: #363636; font-size: 2rem; font-weight: 600; line-height: 1.125; } .title strong { color: inherit; font-weight: inherit; } .title + .highlight { margin-top: -0.75rem; } .title:not(.is-spaced) + .subtitle { margin-top: -1.5rem; } .title.is-1 { font-size: 3rem; } .title.is-2 { font-size: 2.5rem; } .title.is-3 { font-size: 2rem; } .title.is-4 { font-size: 1.5rem; } .title.is-5 { font-size: 1.25rem; } .title.is-6 { font-size: 1rem; } .title.is-7 { font-size: 0.75rem; } .subtitle { color: #4a4a4a; font-size: 1.25rem; font-weight: 400; line-height: 1.25; } .subtitle strong { color: #363636; font-weight: 600; } .subtitle:not(.is-spaced) + .title { margin-top: -1.5rem; } .subtitle.is-1 { font-size: 3rem; } .subtitle.is-2 { font-size: 2.5rem; } .subtitle.is-3 { font-size: 2rem; } .subtitle.is-4 { font-size: 1.5rem; } .subtitle.is-5 { font-size: 1.25rem; } .subtitle.is-6 { font-size: 1rem; } .subtitle.is-7 { font-size: 0.75rem; } .block:not(:last-child) { margin-bottom: 1.5rem; } .delete { -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -moz-appearance: none; -webkit-appearance: none; background-color: rgba(10, 10, 10, 0.2); border: none; border-radius: 290486px; cursor: pointer; display: inline-block; -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; font-size: 0; height: 20px; max-height: 20px; max-width: 20px; min-height: 20px; min-width: 20px; outline: none; position: relative; vertical-align: top; width: 20px; } .delete:before, .delete:after { background-color: white; content: ""; display: block; left: 50%; position: absolute; top: 50%; -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg); transform: translateX(-50%) translateY(-50%) rotate(45deg); -webkit-transform-origin: center center; transform-origin: center center; } .delete:before { height: 2px; width: 50%; } .delete:after { height: 50%; width: 2px; } .delete:hover, .delete:focus { background-color: rgba(10, 10, 10, 0.3); } .delete:active { background-color: rgba(10, 10, 10, 0.4); } .delete.is-small { height: 16px; max-height: 16px; max-width: 16px; min-height: 16px; min-width: 16px; width: 16px; } .delete.is-medium { height: 24px; max-height: 24px; max-width: 24px; min-height: 24px; min-width: 24px; width: 24px; } .delete.is-large { height: 32px; max-height: 32px; max-width: 32px; min-height: 32px; min-width: 32px; width: 32px; } .heading { display: block; font-size: 11px; letter-spacing: 1px; margin-bottom: 5px; text-transform: uppercase; } .highlight { font-weight: 400; max-width: 100%; overflow: hidden; padding: 0; } .highlight:not(:last-child) { margin-bottom: 1.5rem; } .highlight pre { overflow: auto; max-width: 100%; } .loader { -webkit-animation: spinAround 500ms infinite linear; animation: spinAround 500ms infinite linear; border: 2px solid #dbdbdb; border-radius: 290486px; border-right-color: transparent; border-top-color: transparent; content: ""; display: block; height: 1em; position: relative; width: 1em; } .number { -webkit-box-align: center; -ms-flex-align: center; align-items: center; background-color: whitesmoke; border-radius: 290486px; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; font-size: 1.25rem; height: 2em; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; margin-right: 1.5rem; min-width: 2.5em; padding: 0.25rem 0.5rem; text-align: center; vertical-align: top; } .breadcrumb { -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; font-size: 1rem; overflow: hidden; overflow-x: auto; white-space: nowrap; } .breadcrumb:not(:last-child) { margin-bottom: 1.5rem; } .breadcrumb a { -webkit-box-align: center; -ms-flex-align: center; align-items: center; color: #7a91c1; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; padding: 0.5em 0.75em; } .breadcrumb a:hover { color: #363636; } .breadcrumb li { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; } .breadcrumb li:first-child a { padding-left: 0; } .breadcrumb li.is-active a { color: #363636; cursor: default; pointer-events: none; } .breadcrumb li + li::before { color: #4a4a4a; content: "\0002f"; } .breadcrumb ul, .breadcrumb ol { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } .breadcrumb .icon:first-child { margin-right: 0.5em; } .breadcrumb .icon:last-child { margin-left: 0.5em; } .breadcrumb.is-centered ol, .breadcrumb.is-centered ul { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .breadcrumb.is-right ol, .breadcrumb.is-right ul { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } .breadcrumb.is-small { font-size: 0.75rem; } .breadcrumb.is-medium { font-size: 1.25rem; } .breadcrumb.is-large { font-size: 1.5rem; } .breadcrumb.has-arrow-separator li + li::before { content: "\02192"; } .breadcrumb.has-bullet-separator li + li::before { content: "\02022"; } .breadcrumb.has-dot-separator li + li::before { content: "\000b7"; } .breadcrumb.has-succeeds-separator li + li::before { content: "\0227B"; } .card { background-color: white; -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); color: #4a4a4a; max-width: 100%; position: relative; } .card-header { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; -webkit-box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1); box-shadow: 0 1px 2px rgba(10, 10, 10, 0.1); display: -webkit-box; display: -ms-flexbox; display: flex; } .card-header-title { -webkit-box-align: center; -ms-flex-align: center; align-items: center; color: #363636; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; font-weight: 700; padding: 0.75rem; } .card-header-title.is-centered { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .card-header-icon { -webkit-box-align: center; -ms-flex-align: center; align-items: center; cursor: pointer; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; padding: 0.75rem; } .card-image { display: block; position: relative; } .card-content { padding: 1.5rem; } .card-footer { border-top: 1px solid #dbdbdb; -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; } .card-footer-item { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-preferred-size: 0; flex-basis: 0; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; padding: 0.75rem; } .card-footer-item:not(:last-child) { border-right: 1px solid #dbdbdb; } .card .media:not(:last-child) { margin-bottom: 0.75rem; } .dropdown { display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; position: relative; vertical-align: top; } .dropdown.is-active .dropdown-menu, .dropdown.is-hoverable:hover .dropdown-menu { display: block; } .dropdown.is-right .dropdown-menu { left: auto; right: 0; } .dropdown.is-up .dropdown-menu { bottom: 100%; padding-bottom: 4px; padding-top: unset; top: auto; } .dropdown-menu { display: none; left: 0; min-width: 12rem; padding-top: 4px; position: absolute; top: 100%; z-index: 20; } .dropdown-content { background-color: white; border-radius: 3px; -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); padding-bottom: 0.5rem; padding-top: 0.5rem; } .dropdown-item { color: #4a4a4a; display: block; font-size: 0.875rem; line-height: 1.5; padding: 0.375rem 1rem; position: relative; } a.dropdown-item { padding-right: 3rem; white-space: nowrap; } a.dropdown-item:hover { background-color: whitesmoke; color: #0a0a0a; } a.dropdown-item.is-active { background-color: #7a91c1; color: #fff; } .dropdown-divider { background-color: #dbdbdb; border: none; display: block; height: 1px; margin: 0.5rem 0; } .level { -webkit-box-align: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } .level:not(:last-child) { margin-bottom: 1.5rem; } .level code { border-radius: 3px; } .level img { display: inline-block; vertical-align: top; } .level.is-mobile { display: -webkit-box; display: -ms-flexbox; display: flex; } .level.is-mobile .level-left, .level.is-mobile .level-right { display: -webkit-box; display: -ms-flexbox; display: flex; } .level.is-mobile .level-left + .level-right { margin-top: 0; } .level.is-mobile .level-item { margin-right: 0.75rem; } .level.is-mobile .level-item:not(:last-child) { margin-bottom: 0; } .level.is-mobile .level-item:not(.is-narrow) { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } @media screen and (min-width: 769px), print { .level { display: -webkit-box; display: -ms-flexbox; display: flex; } .level > .level-item:not(.is-narrow) { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } } .level-item { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-preferred-size: auto; flex-basis: auto; -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .level-item .title, .level-item .subtitle { margin-bottom: 0; } @media screen and (max-width: 768px) { .level-item:not(:last-child) { margin-bottom: 0.75rem; } } .level-left, .level-right { -ms-flex-preferred-size: auto; flex-basis: auto; -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; } .level-left .level-item.is-flexible, .level-right .level-item.is-flexible { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; } @media screen and (min-width: 769px), print { .level-left .level-item:not(:last-child), .level-right .level-item:not(:last-child) { margin-right: 0.75rem; } } .level-left { -webkit-box-align: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } @media screen and (max-width: 768px) { .level-left + .level-right { margin-top: 1.5rem; } } @media screen and (min-width: 769px), print { .level-left { display: -webkit-box; display: -ms-flexbox; display: flex; } } .level-right { -webkit-box-align: center; -ms-flex-align: center; align-items: center; -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } @media screen and (min-width: 769px), print { .level-right { display: -webkit-box; display: -ms-flexbox; display: flex; } } .media { -webkit-box-align: start; -ms-flex-align: start; align-items: flex-start; display: -webkit-box; display: -ms-flexbox; display: flex; text-align: left; } .media .content:not(:last-child) { margin-bottom: 0.75rem; } .media .media { border-top: 1px solid rgba(219, 219, 219, 0.5); display: -webkit-box; display: -ms-flexbox; display: flex; padding-top: 0.75rem; } .media .media .content:not(:last-child), .media .media .control:not(:last-child) { margin-bottom: 0.5rem; } .media .media .media { padding-top: 0.5rem; } .media .media .media + .media { margin-top: 0.5rem; } .media + .media { border-top: 1px solid rgba(219, 219, 219, 0.5); margin-top: 1rem; padding-top: 1rem; } .media.is-large + .media { margin-top: 1.5rem; padding-top: 1.5rem; } .media-left, .media-right { -ms-flex-preferred-size: auto; flex-basis: auto; -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; } .media-left { margin-right: 1rem; } .media-right { margin-left: 1rem; } .media-content { -ms-flex-preferred-size: auto; flex-basis: auto; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; text-align: left; } .menu { font-size: 1rem; } .menu.is-small { font-size: 0.75rem; } .menu.is-medium { font-size: 1.25rem; } .menu.is-large { font-size: 1.5rem; } .menu-list { line-height: 1.25; } .menu-list a { border-radius: 2px; color: #4a4a4a; display: block; padding: 0.5em 0.75em; } .menu-list a:hover { background-color: whitesmoke; color: #363636; } .menu-list a.is-active { background-color: #7a91c1; color: #fff; } .menu-list li ul { border-left: 1px solid #dbdbdb; margin: 0.75em; padding-left: 0.75em; } .menu-label { color: #7a7a7a; font-size: 0.75em; letter-spacing: 0.1em; text-transform: uppercase; } .menu-label:not(:first-child) { margin-top: 1em; } .menu-label:not(:last-child) { margin-bottom: 1em; } .message { background-color: whitesmoke; border-radius: 3px; font-size: 1rem; } .message:not(:last-child) { margin-bottom: 1.5rem; } .message strong { color: currentColor; } .message a:not(.button):not(.tag) { color: currentColor; text-decoration: underline; } .message.is-small { font-size: 0.75rem; } .message.is-medium { font-size: 1.25rem; } .message.is-large { font-size: 1.5rem; } .message.is-white { background-color: white; } .message.is-white .message-header { background-color: white; color: #0a0a0a; } .message.is-white .message-body { border-color: white; color: #4d4d4d; } .message.is-black { background-color: #fafafa; } .message.is-black .message-header { background-color: #0a0a0a; color: white; } .message.is-black .message-body { border-color: #0a0a0a; color: #090909; } .message.is-light { background-color: #fafafa; } .message.is-light .message-header { background-color: whitesmoke; color: #363636; } .message.is-light .message-body { border-color: whitesmoke; color: #505050; } .message.is-dark { background-color: #fafafa; } .message.is-dark .message-header { background-color: #363636; color: whitesmoke; } .message.is-dark .message-body { border-color: #363636; color: #2a2a2a; } .message.is-primary { background-color: #f5fffd; } .message.is-primary .message-header { background-color: #00d1b2; color: #fff; } .message.is-primary .message-body { border-color: #00d1b2; color: #021310; } .message.is-link { background-color: #f6f9fe; } .message.is-link .message-header { background-color: #7a91c1; color: #fff; } .message.is-link .message-body { border-color: #7a91c1; color: #22509a; } .message.is-info { background-color: #f6fbfe; } .message.is-info .message-header { background-color: #209cee; color: #fff; } .message.is-info .message-body { border-color: #209cee; color: #12537e; } .message.is-success { background-color: #f6fef9; } .message.is-success .message-header { background-color: #23d160; color: #fff; } .message.is-success .message-body { border-color: #23d160; color: #0e301a; } .message.is-warning { background-color: #fffdf5; } .message.is-warning .message-header { background-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } .message.is-warning .message-body { border-color: #ffdd57; color: #3b3108; } .message.is-danger { background-color: #fff5f7; } .message.is-danger .message-header { background-color: #ff3860; color: #fff; } .message.is-danger .message-body { border-color: #ff3860; color: #cd0930; } .message-header { -webkit-box-align: center; -ms-flex-align: center; align-items: center; background-color: #4a4a4a; border-radius: 3px 3px 0 0; color: #fff; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; line-height: 1.25; padding: 0.5em 0.75em; position: relative; } .message-header .delete { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; margin-left: 0.75em; } .message-header + .message-body { border-top-left-radius: 0; border-top-right-radius: 0; border-top: none; } .message-body { border: 1px solid #dbdbdb; border-radius: 3px; color: #4a4a4a; padding: 1em 1.25em; } .message-body code, .message-body pre { background-color: white; } .message-body pre code { background-color: transparent; } .modal { bottom: 0; left: 0; position: absolute; right: 0; top: 0; -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: none; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; overflow: hidden; position: fixed; z-index: 20; } .modal.is-active { display: -webkit-box; display: -ms-flexbox; display: flex; } .modal-background { bottom: 0; left: 0; position: absolute; right: 0; top: 0; background-color: rgba(10, 10, 10, 0.86); } .modal-content, .modal-card { margin: 0 20px; max-height: calc(100vh - 160px); overflow: auto; position: relative; width: 100%; } @media screen and (min-width: 769px), print { .modal-content, .modal-card { margin: 0 auto; max-height: calc(100vh - 40px); width: 640px; } } .modal-close { -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -moz-appearance: none; -webkit-appearance: none; background-color: rgba(10, 10, 10, 0.2); border: none; border-radius: 290486px; cursor: pointer; display: inline-block; -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; font-size: 0; height: 20px; max-height: 20px; max-width: 20px; min-height: 20px; min-width: 20px; outline: none; position: relative; vertical-align: top; width: 20px; background: none; height: 40px; position: fixed; right: 20px; top: 20px; width: 40px; } .modal-close:before, .modal-close:after { background-color: white; content: ""; display: block; left: 50%; position: absolute; top: 50%; -webkit-transform: translateX(-50%) translateY(-50%) rotate(45deg); transform: translateX(-50%) translateY(-50%) rotate(45deg); -webkit-transform-origin: center center; transform-origin: center center; } .modal-close:before { height: 2px; width: 50%; } .modal-close:after { height: 50%; width: 2px; } .modal-close:hover, .modal-close:focus { background-color: rgba(10, 10, 10, 0.3); } .modal-close:active { background-color: rgba(10, 10, 10, 0.4); } .modal-close.is-small { height: 16px; max-height: 16px; max-width: 16px; min-height: 16px; min-width: 16px; width: 16px; } .modal-close.is-medium { height: 24px; max-height: 24px; max-width: 24px; min-height: 24px; min-width: 24px; width: 24px; } .modal-close.is-large { height: 32px; max-height: 32px; max-width: 32px; min-height: 32px; min-width: 32px; width: 32px; } .modal-card { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; max-height: calc(100vh - 40px); overflow: hidden; } .modal-card-head, .modal-card-foot { -webkit-box-align: center; -ms-flex-align: center; align-items: center; background-color: whitesmoke; display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-negative: 0; flex-shrink: 0; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; padding: 20px; position: relative; } .modal-card-head { border-bottom: 1px solid #dbdbdb; border-top-left-radius: 5px; border-top-right-radius: 5px; } .modal-card-title { color: #363636; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; font-size: 1.5rem; line-height: 1; } .modal-card-foot { border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; border-top: 1px solid #dbdbdb; } .modal-card-foot .button:not(:last-child) { margin-right: 10px; } .modal-card-body { -webkit-overflow-scrolling: touch; background-color: white; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; overflow: auto; padding: 20px; } .navbar { background-color: white; min-height: 3.25rem; position: relative; } .navbar.is-white { background-color: white; color: #0a0a0a; } .navbar.is-white .navbar-brand > .navbar-item, .navbar.is-white .navbar-brand .navbar-link { color: #0a0a0a; } .navbar.is-white .navbar-brand > a.navbar-item:hover, .navbar.is-white .navbar-brand > a.navbar-item.is-active, .navbar.is-white .navbar-brand .navbar-link:hover, .navbar.is-white .navbar-brand .navbar-link.is-active { background-color: #f2f2f2; color: #0a0a0a; } .navbar.is-white .navbar-brand .navbar-link::after { border-color: #0a0a0a; } @media screen and (min-width: 1024px) { .navbar.is-white .navbar-start > .navbar-item, .navbar.is-white .navbar-start .navbar-link, .navbar.is-white .navbar-end > .navbar-item, .navbar.is-white .navbar-end .navbar-link { color: #0a0a0a; } .navbar.is-white .navbar-start > a.navbar-item:hover, .navbar.is-white .navbar-start > a.navbar-item.is-active, .navbar.is-white .navbar-start .navbar-link:hover, .navbar.is-white .navbar-start .navbar-link.is-active, .navbar.is-white .navbar-end > a.navbar-item:hover, .navbar.is-white .navbar-end > a.navbar-item.is-active, .navbar.is-white .navbar-end .navbar-link:hover, .navbar.is-white .navbar-end .navbar-link.is-active { background-color: #f2f2f2; color: #0a0a0a; } .navbar.is-white .navbar-start .navbar-link::after, .navbar.is-white .navbar-end .navbar-link::after { border-color: #0a0a0a; } .navbar.is-white .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-white .navbar-item.has-dropdown.is-active .navbar-link { background-color: #f2f2f2; color: #0a0a0a; } .navbar.is-white .navbar-dropdown a.navbar-item.is-active { background-color: white; color: #0a0a0a; } } .navbar.is-black { background-color: #0a0a0a; color: white; } .navbar.is-black .navbar-brand > .navbar-item, .navbar.is-black .navbar-brand .navbar-link { color: white; } .navbar.is-black .navbar-brand > a.navbar-item:hover, .navbar.is-black .navbar-brand > a.navbar-item.is-active, .navbar.is-black .navbar-brand .navbar-link:hover, .navbar.is-black .navbar-brand .navbar-link.is-active { background-color: black; color: white; } .navbar.is-black .navbar-brand .navbar-link::after { border-color: white; } @media screen and (min-width: 1024px) { .navbar.is-black .navbar-start > .navbar-item, .navbar.is-black .navbar-start .navbar-link, .navbar.is-black .navbar-end > .navbar-item, .navbar.is-black .navbar-end .navbar-link { color: white; } .navbar.is-black .navbar-start > a.navbar-item:hover, .navbar.is-black .navbar-start > a.navbar-item.is-active, .navbar.is-black .navbar-start .navbar-link:hover, .navbar.is-black .navbar-start .navbar-link.is-active, .navbar.is-black .navbar-end > a.navbar-item:hover, .navbar.is-black .navbar-end > a.navbar-item.is-active, .navbar.is-black .navbar-end .navbar-link:hover, .navbar.is-black .navbar-end .navbar-link.is-active { background-color: black; color: white; } .navbar.is-black .navbar-start .navbar-link::after, .navbar.is-black .navbar-end .navbar-link::after { border-color: white; } .navbar.is-black .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-black .navbar-item.has-dropdown.is-active .navbar-link { background-color: black; color: white; } .navbar.is-black .navbar-dropdown a.navbar-item.is-active { background-color: #0a0a0a; color: white; } } .navbar.is-light { background-color: whitesmoke; color: #363636; } .navbar.is-light .navbar-brand > .navbar-item, .navbar.is-light .navbar-brand .navbar-link { color: #363636; } .navbar.is-light .navbar-brand > a.navbar-item:hover, .navbar.is-light .navbar-brand > a.navbar-item.is-active, .navbar.is-light .navbar-brand .navbar-link:hover, .navbar.is-light .navbar-brand .navbar-link.is-active { background-color: #e8e8e8; color: #363636; } .navbar.is-light .navbar-brand .navbar-link::after { border-color: #363636; } @media screen and (min-width: 1024px) { .navbar.is-light .navbar-start > .navbar-item, .navbar.is-light .navbar-start .navbar-link, .navbar.is-light .navbar-end > .navbar-item, .navbar.is-light .navbar-end .navbar-link { color: #363636; } .navbar.is-light .navbar-start > a.navbar-item:hover, .navbar.is-light .navbar-start > a.navbar-item.is-active, .navbar.is-light .navbar-start .navbar-link:hover, .navbar.is-light .navbar-start .navbar-link.is-active, .navbar.is-light .navbar-end > a.navbar-item:hover, .navbar.is-light .navbar-end > a.navbar-item.is-active, .navbar.is-light .navbar-end .navbar-link:hover, .navbar.is-light .navbar-end .navbar-link.is-active { background-color: #e8e8e8; color: #363636; } .navbar.is-light .navbar-start .navbar-link::after, .navbar.is-light .navbar-end .navbar-link::after { border-color: #363636; } .navbar.is-light .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-light .navbar-item.has-dropdown.is-active .navbar-link { background-color: #e8e8e8; color: #363636; } .navbar.is-light .navbar-dropdown a.navbar-item.is-active { background-color: whitesmoke; color: #363636; } } .navbar.is-dark { background-color: #363636; color: whitesmoke; } .navbar.is-dark .navbar-brand > .navbar-item, .navbar.is-dark .navbar-brand .navbar-link { color: whitesmoke; } .navbar.is-dark .navbar-brand > a.navbar-item:hover, .navbar.is-dark .navbar-brand > a.navbar-item.is-active, .navbar.is-dark .navbar-brand .navbar-link:hover, .navbar.is-dark .navbar-brand .navbar-link.is-active { background-color: #292929; color: whitesmoke; } .navbar.is-dark .navbar-brand .navbar-link::after { border-color: whitesmoke; } @media screen and (min-width: 1024px) { .navbar.is-dark .navbar-start > .navbar-item, .navbar.is-dark .navbar-start .navbar-link, .navbar.is-dark .navbar-end > .navbar-item, .navbar.is-dark .navbar-end .navbar-link { color: whitesmoke; } .navbar.is-dark .navbar-start > a.navbar-item:hover, .navbar.is-dark .navbar-start > a.navbar-item.is-active, .navbar.is-dark .navbar-start .navbar-link:hover, .navbar.is-dark .navbar-start .navbar-link.is-active, .navbar.is-dark .navbar-end > a.navbar-item:hover, .navbar.is-dark .navbar-end > a.navbar-item.is-active, .navbar.is-dark .navbar-end .navbar-link:hover, .navbar.is-dark .navbar-end .navbar-link.is-active { background-color: #292929; color: whitesmoke; } .navbar.is-dark .navbar-start .navbar-link::after, .navbar.is-dark .navbar-end .navbar-link::after { border-color: whitesmoke; } .navbar.is-dark .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-dark .navbar-item.has-dropdown.is-active .navbar-link { background-color: #292929; color: whitesmoke; } .navbar.is-dark .navbar-dropdown a.navbar-item.is-active { background-color: #363636; color: whitesmoke; } } .navbar.is-primary { background-color: #00d1b2; color: #fff; } .navbar.is-primary .navbar-brand > .navbar-item, .navbar.is-primary .navbar-brand .navbar-link { color: #fff; } .navbar.is-primary .navbar-brand > a.navbar-item:hover, .navbar.is-primary .navbar-brand > a.navbar-item.is-active, .navbar.is-primary .navbar-brand .navbar-link:hover, .navbar.is-primary .navbar-brand .navbar-link.is-active { background-color: #00b89c; color: #fff; } .navbar.is-primary .navbar-brand .navbar-link::after { border-color: #fff; } @media screen and (min-width: 1024px) { .navbar.is-primary .navbar-start > .navbar-item, .navbar.is-primary .navbar-start .navbar-link, .navbar.is-primary .navbar-end > .navbar-item, .navbar.is-primary .navbar-end .navbar-link { color: #fff; } .navbar.is-primary .navbar-start > a.navbar-item:hover, .navbar.is-primary .navbar-start > a.navbar-item.is-active, .navbar.is-primary .navbar-start .navbar-link:hover, .navbar.is-primary .navbar-start .navbar-link.is-active, .navbar.is-primary .navbar-end > a.navbar-item:hover, .navbar.is-primary .navbar-end > a.navbar-item.is-active, .navbar.is-primary .navbar-end .navbar-link:hover, .navbar.is-primary .navbar-end .navbar-link.is-active { background-color: #00b89c; color: #fff; } .navbar.is-primary .navbar-start .navbar-link::after, .navbar.is-primary .navbar-end .navbar-link::after { border-color: #fff; } .navbar.is-primary .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-primary .navbar-item.has-dropdown.is-active .navbar-link { background-color: #00b89c; color: #fff; } .navbar.is-primary .navbar-dropdown a.navbar-item.is-active { background-color: #00d1b2; color: #fff; } } .navbar.is-link { background-color: #7a91c1; color: #fff; } .navbar.is-link .navbar-brand > .navbar-item, .navbar.is-link .navbar-brand .navbar-link { color: #fff; } .navbar.is-link .navbar-brand > a.navbar-item:hover, .navbar.is-link .navbar-brand > a.navbar-item.is-active, .navbar.is-link .navbar-brand .navbar-link:hover, .navbar.is-link .navbar-brand .navbar-link.is-active { background-color: #2366d1; color: #fff; } .navbar.is-link .navbar-brand .navbar-link::after { border-color: #fff; } @media screen and (min-width: 1024px) { .navbar.is-link .navbar-start > .navbar-item, .navbar.is-link .navbar-start .navbar-link, .navbar.is-link .navbar-end > .navbar-item, .navbar.is-link .navbar-end .navbar-link { color: #fff; } .navbar.is-link .navbar-start > a.navbar-item:hover, .navbar.is-link .navbar-start > a.navbar-item.is-active, .navbar.is-link .navbar-start .navbar-link:hover, .navbar.is-link .navbar-start .navbar-link.is-active, .navbar.is-link .navbar-end > a.navbar-item:hover, .navbar.is-link .navbar-end > a.navbar-item.is-active, .navbar.is-link .navbar-end .navbar-link:hover, .navbar.is-link .navbar-end .navbar-link.is-active { background-color: #2366d1; color: #fff; } .navbar.is-link .navbar-start .navbar-link::after, .navbar.is-link .navbar-end .navbar-link::after { border-color: #fff; } .navbar.is-link .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-link .navbar-item.has-dropdown.is-active .navbar-link { background-color: #2366d1; color: #fff; } .navbar.is-link .navbar-dropdown a.navbar-item.is-active { background-color: #7a91c1; color: #fff; } } .navbar.is-info { background-color: #209cee; color: #fff; } .navbar.is-info .navbar-brand > .navbar-item, .navbar.is-info .navbar-brand .navbar-link { color: #fff; } .navbar.is-info .navbar-brand > a.navbar-item:hover, .navbar.is-info .navbar-brand > a.navbar-item.is-active, .navbar.is-info .navbar-brand .navbar-link:hover, .navbar.is-info .navbar-brand .navbar-link.is-active { background-color: #118fe4; color: #fff; } .navbar.is-info .navbar-brand .navbar-link::after { border-color: #fff; } @media screen and (min-width: 1024px) { .navbar.is-info .navbar-start > .navbar-item, .navbar.is-info .navbar-start .navbar-link, .navbar.is-info .navbar-end > .navbar-item, .navbar.is-info .navbar-end .navbar-link { color: #fff; } .navbar.is-info .navbar-start > a.navbar-item:hover, .navbar.is-info .navbar-start > a.navbar-item.is-active, .navbar.is-info .navbar-start .navbar-link:hover, .navbar.is-info .navbar-start .navbar-link.is-active, .navbar.is-info .navbar-end > a.navbar-item:hover, .navbar.is-info .navbar-end > a.navbar-item.is-active, .navbar.is-info .navbar-end .navbar-link:hover, .navbar.is-info .navbar-end .navbar-link.is-active { background-color: #118fe4; color: #fff; } .navbar.is-info .navbar-start .navbar-link::after, .navbar.is-info .navbar-end .navbar-link::after { border-color: #fff; } .navbar.is-info .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-info .navbar-item.has-dropdown.is-active .navbar-link { background-color: #118fe4; color: #fff; } .navbar.is-info .navbar-dropdown a.navbar-item.is-active { background-color: #209cee; color: #fff; } } .navbar.is-success { background-color: #23d160; color: #fff; } .navbar.is-success .navbar-brand > .navbar-item, .navbar.is-success .navbar-brand .navbar-link { color: #fff; } .navbar.is-success .navbar-brand > a.navbar-item:hover, .navbar.is-success .navbar-brand > a.navbar-item.is-active, .navbar.is-success .navbar-brand .navbar-link:hover, .navbar.is-success .navbar-brand .navbar-link.is-active { background-color: #20bc56; color: #fff; } .navbar.is-success .navbar-brand .navbar-link::after { border-color: #fff; } @media screen and (min-width: 1024px) { .navbar.is-success .navbar-start > .navbar-item, .navbar.is-success .navbar-start .navbar-link, .navbar.is-success .navbar-end > .navbar-item, .navbar.is-success .navbar-end .navbar-link { color: #fff; } .navbar.is-success .navbar-start > a.navbar-item:hover, .navbar.is-success .navbar-start > a.navbar-item.is-active, .navbar.is-success .navbar-start .navbar-link:hover, .navbar.is-success .navbar-start .navbar-link.is-active, .navbar.is-success .navbar-end > a.navbar-item:hover, .navbar.is-success .navbar-end > a.navbar-item.is-active, .navbar.is-success .navbar-end .navbar-link:hover, .navbar.is-success .navbar-end .navbar-link.is-active { background-color: #20bc56; color: #fff; } .navbar.is-success .navbar-start .navbar-link::after, .navbar.is-success .navbar-end .navbar-link::after { border-color: #fff; } .navbar.is-success .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-success .navbar-item.has-dropdown.is-active .navbar-link { background-color: #20bc56; color: #fff; } .navbar.is-success .navbar-dropdown a.navbar-item.is-active { background-color: #23d160; color: #fff; } } .navbar.is-warning { background-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-brand > .navbar-item, .navbar.is-warning .navbar-brand .navbar-link { color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-brand > a.navbar-item:hover, .navbar.is-warning .navbar-brand > a.navbar-item.is-active, .navbar.is-warning .navbar-brand .navbar-link:hover, .navbar.is-warning .navbar-brand .navbar-link.is-active { background-color: #ffd83d; color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-brand .navbar-link::after { border-color: rgba(0, 0, 0, 0.7); } @media screen and (min-width: 1024px) { .navbar.is-warning .navbar-start > .navbar-item, .navbar.is-warning .navbar-start .navbar-link, .navbar.is-warning .navbar-end > .navbar-item, .navbar.is-warning .navbar-end .navbar-link { color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-start > a.navbar-item:hover, .navbar.is-warning .navbar-start > a.navbar-item.is-active, .navbar.is-warning .navbar-start .navbar-link:hover, .navbar.is-warning .navbar-start .navbar-link.is-active, .navbar.is-warning .navbar-end > a.navbar-item:hover, .navbar.is-warning .navbar-end > a.navbar-item.is-active, .navbar.is-warning .navbar-end .navbar-link:hover, .navbar.is-warning .navbar-end .navbar-link.is-active { background-color: #ffd83d; color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-start .navbar-link::after, .navbar.is-warning .navbar-end .navbar-link::after { border-color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-warning .navbar-item.has-dropdown.is-active .navbar-link { background-color: #ffd83d; color: rgba(0, 0, 0, 0.7); } .navbar.is-warning .navbar-dropdown a.navbar-item.is-active { background-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } } .navbar.is-danger { background-color: #ff3860; color: #fff; } .navbar.is-danger .navbar-brand > .navbar-item, .navbar.is-danger .navbar-brand .navbar-link { color: #fff; } .navbar.is-danger .navbar-brand > a.navbar-item:hover, .navbar.is-danger .navbar-brand > a.navbar-item.is-active, .navbar.is-danger .navbar-brand .navbar-link:hover, .navbar.is-danger .navbar-brand .navbar-link.is-active { background-color: #ff1f4b; color: #fff; } .navbar.is-danger .navbar-brand .navbar-link::after { border-color: #fff; } @media screen and (min-width: 1024px) { .navbar.is-danger .navbar-start > .navbar-item, .navbar.is-danger .navbar-start .navbar-link, .navbar.is-danger .navbar-end > .navbar-item, .navbar.is-danger .navbar-end .navbar-link { color: #fff; } .navbar.is-danger .navbar-start > a.navbar-item:hover, .navbar.is-danger .navbar-start > a.navbar-item.is-active, .navbar.is-danger .navbar-start .navbar-link:hover, .navbar.is-danger .navbar-start .navbar-link.is-active, .navbar.is-danger .navbar-end > a.navbar-item:hover, .navbar.is-danger .navbar-end > a.navbar-item.is-active, .navbar.is-danger .navbar-end .navbar-link:hover, .navbar.is-danger .navbar-end .navbar-link.is-active { background-color: #ff1f4b; color: #fff; } .navbar.is-danger .navbar-start .navbar-link::after, .navbar.is-danger .navbar-end .navbar-link::after { border-color: #fff; } .navbar.is-danger .navbar-item.has-dropdown:hover .navbar-link, .navbar.is-danger .navbar-item.has-dropdown.is-active .navbar-link { background-color: #ff1f4b; color: #fff; } .navbar.is-danger .navbar-dropdown a.navbar-item.is-active { background-color: #ff3860; color: #fff; } } .navbar > .container { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; min-height: 3.25rem; width: 100%; } .navbar.has-shadow { -webkit-box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1); box-shadow: 0 2px 3px rgba(10, 10, 10, 0.1); } .navbar.is-fixed-bottom, .navbar.is-fixed-top { left: 0; position: fixed; right: 0; z-index: 30; } .navbar.is-fixed-bottom { bottom: 0; } .navbar.is-fixed-bottom.has-shadow { -webkit-box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); } .navbar.is-fixed-top { top: 0; } html.has-navbar-fixed-top { padding-top: 3.25rem; } html.has-navbar-fixed-bottom { padding-bottom: 3.25rem; } .navbar-brand, .navbar-tabs { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; -ms-flex-negative: 0; flex-shrink: 0; min-height: 3.25rem; } .navbar-tabs { -webkit-overflow-scrolling: touch; max-width: 100vw; overflow-x: auto; overflow-y: hidden; } .navbar-burger { cursor: pointer; display: block; height: 3.25rem; position: relative; width: 3.25rem; margin-left: auto; } .navbar-burger span { background-color: currentColor; display: block; height: 1px; left: calc(50% - 8px); position: absolute; -webkit-transform-origin: center; transform-origin: center; -webkit-transition-duration: 86ms; transition-duration: 86ms; -webkit-transition-property: background-color, opacity, -webkit-transform; transition-property: background-color, opacity, -webkit-transform; transition-property: background-color, opacity, transform; transition-property: background-color, opacity, transform, -webkit-transform; -webkit-transition-timing-function: ease-out; transition-timing-function: ease-out; width: 16px; } .navbar-burger span:nth-child(1) { top: calc(50% - 6px); } .navbar-burger span:nth-child(2) { top: calc(50% - 1px); } .navbar-burger span:nth-child(3) { top: calc(50% + 4px); } .navbar-burger:hover { background-color: rgba(0, 0, 0, 0.05); } .navbar-burger.is-active span:nth-child(1) { -webkit-transform: translateY(5px) rotate(45deg); transform: translateY(5px) rotate(45deg); } .navbar-burger.is-active span:nth-child(2) { opacity: 0; } .navbar-burger.is-active span:nth-child(3) { -webkit-transform: translateY(-5px) rotate(-45deg); transform: translateY(-5px) rotate(-45deg); } .navbar-menu { display: none; } .navbar-item, .navbar-link { color: #4a4a4a; display: block; line-height: 1.5; padding: 0.5rem 1rem; position: relative; } a.navbar-item:hover, a.navbar-item.is-active, a.navbar-link:hover, a.navbar-link.is-active { background-color: whitesmoke; color: #7a91c1; } .navbar-item { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; } .navbar-item img { max-height: 1.75rem; } .navbar-item.has-dropdown { padding: 0; } .navbar-item.is-expanded { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; } .navbar-item.is-tab { border-bottom: 1px solid transparent; min-height: 3.25rem; padding-bottom: calc(0.5rem - 1px); } .navbar-item.is-tab:hover { background-color: transparent; border-bottom-color: #7a91c1; } .navbar-item.is-tab.is-active { background-color: transparent; border-bottom-color: #7a91c1; border-bottom-style: solid; border-bottom-width: 3px; color: #7a91c1; padding-bottom: calc(0.5rem - 3px); } .navbar-content { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; } .navbar-link { padding-right: 2.5em; } .navbar-dropdown { font-size: 0.875rem; padding-bottom: 0.5rem; padding-top: 0.5rem; } .navbar-dropdown .navbar-item { padding-left: 1.5rem; padding-right: 1.5rem; } .navbar-divider { background-color: #dbdbdb; border: none; display: none; height: 1px; margin: 0.5rem 0; } @media screen and (max-width: 1023px) { .navbar > .container { display: block; } .navbar-brand .navbar-item, .navbar-tabs .navbar-item { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; } .navbar-menu { background-color: white; -webkit-box-shadow: 0 8px 16px rgba(10, 10, 10, 0.1); box-shadow: 0 8px 16px rgba(10, 10, 10, 0.1); padding: 0.5rem 0; } .navbar-menu.is-active { display: block; } .navbar.is-fixed-bottom-touch, .navbar.is-fixed-top-touch { left: 0; position: fixed; right: 0; z-index: 30; } .navbar.is-fixed-bottom-touch { bottom: 0; } .navbar.is-fixed-bottom-touch.has-shadow { -webkit-box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); } .navbar.is-fixed-top-touch { top: 0; } .navbar.is-fixed-top .navbar-menu, .navbar.is-fixed-top-touch .navbar-menu { -webkit-overflow-scrolling: touch; max-height: calc(100vh - 3.25rem); overflow: auto; } html.has-navbar-fixed-top-touch { padding-top: 3.25rem; } html.has-navbar-fixed-bottom-touch { padding-bottom: 3.25rem; } } @media screen and (min-width: 1024px) { .navbar, .navbar-menu, .navbar-start, .navbar-end { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; } .navbar { min-height: 3.25rem; } .navbar.is-transparent a.navbar-item:hover, .navbar.is-transparent a.navbar-item.is-active, .navbar.is-transparent a.navbar-link:hover, .navbar.is-transparent a.navbar-link.is-active { background-color: transparent !important; } .navbar.is-transparent .navbar-item.has-dropdown.is-active .navbar-link, .navbar.is-transparent .navbar-item.has-dropdown.is-hoverable:hover .navbar-link { background-color: transparent !important; } .navbar.is-transparent .navbar-dropdown a.navbar-item:hover { background-color: whitesmoke; color: #0a0a0a; } .navbar.is-transparent .navbar-dropdown a.navbar-item.is-active { background-color: whitesmoke; color: #7a91c1; } .navbar-burger { display: none; } .navbar-item, .navbar-link { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; } .navbar-item.has-dropdown { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; } .navbar-item.has-dropdown-up .navbar-link::after { -webkit-transform: rotate(135deg) translate(0.25em, -0.25em); transform: rotate(135deg) translate(0.25em, -0.25em); } .navbar-item.has-dropdown-up .navbar-dropdown { border-bottom: 1px solid #dbdbdb; border-radius: 5px 5px 0 0; border-top: none; bottom: 100%; -webkit-box-shadow: 0 -8px 8px rgba(10, 10, 10, 0.1); box-shadow: 0 -8px 8px rgba(10, 10, 10, 0.1); top: auto; } .navbar-item.is-active .navbar-dropdown, .navbar-item.is-hoverable:hover .navbar-dropdown { display: block; } .navbar-item.is-active .navbar-dropdown.is-boxed, .navbar-item.is-hoverable:hover .navbar-dropdown.is-boxed { opacity: 1; pointer-events: auto; -webkit-transform: translateY(0); transform: translateY(0); } .navbar-link::after { border: 1px solid #7a91c1; border-right: 0; border-top: 0; content: " "; display: block; height: 0.5em; pointer-events: none; position: absolute; -webkit-transform: rotate(-45deg); transform: rotate(-45deg); -webkit-transform-origin: center; transform-origin: center; width: 0.5em; margin-top: -0.375em; right: 1.125em; top: 50%; } .navbar-menu { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; } .navbar-start { -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; margin-right: auto; } .navbar-end { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; margin-left: auto; } .navbar-dropdown { background-color: white; border-bottom-left-radius: 5px; border-bottom-right-radius: 5px; border-top: 1px solid #dbdbdb; -webkit-box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1); box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1); display: none; font-size: 0.875rem; left: 0; min-width: 100%; position: absolute; top: 100%; z-index: 20; } .navbar-dropdown .navbar-item { padding: 0.375rem 1rem; white-space: nowrap; } .navbar-dropdown a.navbar-item { padding-right: 3rem; } .navbar-dropdown a.navbar-item:hover { background-color: whitesmoke; color: #0a0a0a; } .navbar-dropdown a.navbar-item.is-active { background-color: whitesmoke; color: #7a91c1; } .navbar-dropdown.is-boxed { border-radius: 5px; border-top: none; -webkit-box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); box-shadow: 0 8px 8px rgba(10, 10, 10, 0.1), 0 0 0 1px rgba(10, 10, 10, 0.1); display: block; opacity: 0; pointer-events: none; top: calc(100% + (-4px)); -webkit-transform: translateY(-5px); transform: translateY(-5px); -webkit-transition-duration: 86ms; transition-duration: 86ms; -webkit-transition-property: opacity, -webkit-transform; transition-property: opacity, -webkit-transform; transition-property: opacity, transform; transition-property: opacity, transform, -webkit-transform; } .navbar-dropdown.is-right { left: auto; right: 0; } .navbar-divider { display: block; } .navbar > .container .navbar-brand, .container > .navbar .navbar-brand { margin-left: -1rem; } .navbar > .container .navbar-menu, .container > .navbar .navbar-menu { margin-right: -1rem; } .navbar.is-fixed-bottom-desktop, .navbar.is-fixed-top-desktop { left: 0; position: fixed; right: 0; z-index: 30; } .navbar.is-fixed-bottom-desktop { bottom: 0; } .navbar.is-fixed-bottom-desktop.has-shadow { -webkit-box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); box-shadow: 0 -2px 3px rgba(10, 10, 10, 0.1); } .navbar.is-fixed-top-desktop { top: 0; } html.has-navbar-fixed-top-desktop { padding-top: 3.25rem; } html.has-navbar-fixed-bottom-desktop { padding-bottom: 3.25rem; } a.navbar-item.is-active, a.navbar-link.is-active { color: #0a0a0a; } a.navbar-item.is-active:not(:hover), a.navbar-link.is-active:not(:hover) { background-color: transparent; } .navbar-item.has-dropdown:hover .navbar-link, .navbar-item.has-dropdown.is-active .navbar-link { background-color: whitesmoke; } } .pagination { font-size: 1rem; margin: -0.25rem; } .pagination.is-small { font-size: 0.75rem; } .pagination.is-medium { font-size: 1.25rem; } .pagination.is-large { font-size: 1.5rem; } .pagination, .pagination-list { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; text-align: center; } .pagination-previous, .pagination-next, .pagination-link, .pagination-ellipsis { -moz-appearance: none; -webkit-appearance: none; -webkit-box-align: center; -ms-flex-align: center; align-items: center; border: 1px solid transparent; border-radius: 3px; -webkit-box-shadow: none; box-shadow: none; display: -webkit-inline-box; display: -ms-inline-flexbox; display: inline-flex; font-size: 1rem; height: 2.25em; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; line-height: 1.5; padding-bottom: calc(0.375em - 1px); padding-left: calc(0.625em - 1px); padding-right: calc(0.625em - 1px); padding-top: calc(0.375em - 1px); position: relative; vertical-align: top; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; font-size: 1em; padding-left: 0.5em; padding-right: 0.5em; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; margin: 0.25rem; text-align: center; } .pagination-previous:focus, .pagination-previous.is-focused, .pagination-previous:active, .pagination-previous.is-active, .pagination-next:focus, .pagination-next.is-focused, .pagination-next:active, .pagination-next.is-active, .pagination-link:focus, .pagination-link.is-focused, .pagination-link:active, .pagination-link.is-active, .pagination-ellipsis:focus, .pagination-ellipsis.is-focused, .pagination-ellipsis:active, .pagination-ellipsis.is-active { outline: none; } .pagination-previous[disabled], .pagination-next[disabled], .pagination-link[disabled], .pagination-ellipsis[disabled] { cursor: not-allowed; } .pagination-previous, .pagination-next, .pagination-link { border-color: #dbdbdb; min-width: 2.25em; } .pagination-previous:hover, .pagination-next:hover, .pagination-link:hover { border-color: #b5b5b5; color: #363636; } .pagination-previous:focus, .pagination-next:focus, .pagination-link:focus { border-color: #7a91c1; } .pagination-previous:active, .pagination-next:active, .pagination-link:active { -webkit-box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2); box-shadow: inset 0 1px 2px rgba(10, 10, 10, 0.2); } .pagination-previous[disabled], .pagination-next[disabled], .pagination-link[disabled] { background-color: #dbdbdb; border-color: #dbdbdb; -webkit-box-shadow: none; box-shadow: none; color: #7a7a7a; opacity: 0.5; } .pagination-previous, .pagination-next { padding-left: 0.75em; padding-right: 0.75em; white-space: nowrap; } .pagination-link.is-current { background-color: #7a91c1; border-color: #7a91c1; color: #fff; } .pagination-ellipsis { color: #b5b5b5; pointer-events: none; } .pagination-list { -ms-flex-wrap: wrap; flex-wrap: wrap; } @media screen and (max-width: 768px) { .pagination { -ms-flex-wrap: wrap; flex-wrap: wrap; } .pagination-previous, .pagination-next { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; } .pagination-list li { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; } } @media screen and (min-width: 769px), print { .pagination-list { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } .pagination-previous { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } .pagination-next { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } .pagination { -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } .pagination.is-centered .pagination-previous { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } .pagination.is-centered .pagination-list { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } .pagination.is-centered .pagination-next { -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } .pagination.is-right .pagination-previous { -webkit-box-ordinal-group: 2; -ms-flex-order: 1; order: 1; } .pagination.is-right .pagination-next { -webkit-box-ordinal-group: 3; -ms-flex-order: 2; order: 2; } .pagination.is-right .pagination-list { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; -webkit-box-ordinal-group: 4; -ms-flex-order: 3; order: 3; } } .panel { font-size: 1rem; } .panel:not(:last-child) { margin-bottom: 1.5rem; } .panel-heading, .panel-tabs, .panel-block { border-bottom: 1px solid #dbdbdb; border-left: 1px solid #dbdbdb; border-right: 1px solid #dbdbdb; } .panel-heading:first-child, .panel-tabs:first-child, .panel-block:first-child { border-top: 1px solid #dbdbdb; } .panel-heading { background-color: whitesmoke; border-radius: 3px 3px 0 0; color: #363636; font-size: 1.25em; font-weight: 300; line-height: 1.25; padding: 0.5em 0.75em; } .panel-tabs { -webkit-box-align: end; -ms-flex-align: end; align-items: flex-end; display: -webkit-box; display: -ms-flexbox; display: flex; font-size: 0.875em; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .panel-tabs a { border-bottom: 1px solid #dbdbdb; margin-bottom: -1px; padding: 0.5em; } .panel-tabs a.is-active { border-bottom-color: #4a4a4a; color: #363636; } .panel-list a { color: #4a4a4a; } .panel-list a:hover { color: #7a91c1; } .panel-block { -webkit-box-align: center; -ms-flex-align: center; align-items: center; color: #363636; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; padding: 0.5em 0.75em; } .panel-block input[type="checkbox"] { margin-right: 0.75em; } .panel-block > .control { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; width: 100%; } .panel-block.is-wrapped { -ms-flex-wrap: wrap; flex-wrap: wrap; } .panel-block.is-active { border-left-color: #7a91c1; color: #363636; } .panel-block.is-active .panel-icon { color: #7a91c1; } a.panel-block, label.panel-block { cursor: pointer; } a.panel-block:hover, label.panel-block:hover { background-color: whitesmoke; } .panel-icon { display: inline-block; font-size: 14px; height: 1em; line-height: 1em; text-align: center; vertical-align: top; width: 1em; color: #7a7a7a; margin-right: 0.75em; } .panel-icon .fa { font-size: inherit; line-height: inherit; } .tabs { -webkit-overflow-scrolling: touch; -webkit-touch-callout: none; -webkit-user-select: none; -moz-user-select: none; -ms-user-select: none; user-select: none; -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; font-size: 1rem; -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; overflow: hidden; overflow-x: auto; white-space: nowrap; } .tabs:not(:last-child) { margin-bottom: 1.5rem; } .tabs a { -webkit-box-align: center; -ms-flex-align: center; align-items: center; border-bottom-color: #dbdbdb; border-bottom-style: solid; border-bottom-width: 1px; color: #4a4a4a; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; margin-bottom: -1px; padding: 0.5em 1em; vertical-align: top; } .tabs a:hover { border-bottom-color: #363636; color: #363636; } .tabs li { display: block; } .tabs li.is-active a { border-bottom-color: #7a91c1; color: #7a91c1; } .tabs ul { -webkit-box-align: center; -ms-flex-align: center; align-items: center; border-bottom-color: #dbdbdb; border-bottom-style: solid; border-bottom-width: 1px; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; -webkit-box-pack: start; -ms-flex-pack: start; justify-content: flex-start; } .tabs ul.is-left { padding-right: 0.75em; } .tabs ul.is-center { -webkit-box-flex: 0; -ms-flex: none; flex: none; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; padding-left: 0.75em; padding-right: 0.75em; } .tabs ul.is-right { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; padding-left: 0.75em; } .tabs .icon:first-child { margin-right: 0.5em; } .tabs .icon:last-child { margin-left: 0.5em; } .tabs.is-centered ul { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .tabs.is-right ul { -webkit-box-pack: end; -ms-flex-pack: end; justify-content: flex-end; } .tabs.is-boxed a { border: 1px solid transparent; border-radius: 3px 3px 0 0; } .tabs.is-boxed a:hover { background-color: whitesmoke; border-bottom-color: #dbdbdb; } .tabs.is-boxed li.is-active a { background-color: white; border-color: #dbdbdb; border-bottom-color: transparent !important; } .tabs.is-fullwidth li { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; } .tabs.is-toggle a { border-color: #dbdbdb; border-style: solid; border-width: 1px; margin-bottom: 0; position: relative; } .tabs.is-toggle a:hover { background-color: whitesmoke; border-color: #b5b5b5; z-index: 2; } .tabs.is-toggle li + li { margin-left: -1px; } .tabs.is-toggle li:first-child a { border-radius: 3px 0 0 3px; } .tabs.is-toggle li:last-child a { border-radius: 0 3px 3px 0; } .tabs.is-toggle li.is-active a { background-color: #7a91c1; border-color: #7a91c1; color: #fff; z-index: 1; } .tabs.is-toggle ul { border-bottom: none; } .tabs.is-small { font-size: 0.75rem; } .tabs.is-medium { font-size: 1.25rem; } .tabs.is-large { font-size: 1.5rem; } .column { display: block; -ms-flex-preferred-size: 0; flex-basis: 0; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; padding: 0.75rem; } .columns.is-mobile > .column.is-narrow { -webkit-box-flex: 0; -ms-flex: none; flex: none; } .columns.is-mobile > .column.is-full { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .columns.is-mobile > .column.is-three-quarters { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .columns.is-mobile > .column.is-two-thirds { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.6666%; } .columns.is-mobile > .column.is-half { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .columns.is-mobile > .column.is-one-third { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.3333%; } .columns.is-mobile > .column.is-one-quarter { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .columns.is-mobile > .column.is-one-fifth { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 20%; } .columns.is-mobile > .column.is-two-fifths { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 40%; } .columns.is-mobile > .column.is-three-fifths { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 60%; } .columns.is-mobile > .column.is-four-fifths { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 80%; } .columns.is-mobile > .column.is-offset-three-quarters { margin-left: 75%; } .columns.is-mobile > .column.is-offset-two-thirds { margin-left: 66.6666%; } .columns.is-mobile > .column.is-offset-half { margin-left: 50%; } .columns.is-mobile > .column.is-offset-one-third { margin-left: 33.3333%; } .columns.is-mobile > .column.is-offset-one-quarter { margin-left: 25%; } .columns.is-mobile > .column.is-offset-one-fifth { margin-left: 20%; } .columns.is-mobile > .column.is-offset-two-fifths { margin-left: 40%; } .columns.is-mobile > .column.is-offset-three-fifths { margin-left: 60%; } .columns.is-mobile > .column.is-offset-four-fifths { margin-left: 80%; } .columns.is-mobile > .column.is-1 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .columns.is-mobile > .column.is-offset-1 { margin-left: 8.33333%; } .columns.is-mobile > .column.is-2 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .columns.is-mobile > .column.is-offset-2 { margin-left: 16.66667%; } .columns.is-mobile > .column.is-3 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .columns.is-mobile > .column.is-offset-3 { margin-left: 25%; } .columns.is-mobile > .column.is-4 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .columns.is-mobile > .column.is-offset-4 { margin-left: 33.33333%; } .columns.is-mobile > .column.is-5 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .columns.is-mobile > .column.is-offset-5 { margin-left: 41.66667%; } .columns.is-mobile > .column.is-6 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .columns.is-mobile > .column.is-offset-6 { margin-left: 50%; } .columns.is-mobile > .column.is-7 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .columns.is-mobile > .column.is-offset-7 { margin-left: 58.33333%; } .columns.is-mobile > .column.is-8 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .columns.is-mobile > .column.is-offset-8 { margin-left: 66.66667%; } .columns.is-mobile > .column.is-9 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .columns.is-mobile > .column.is-offset-9 { margin-left: 75%; } .columns.is-mobile > .column.is-10 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .columns.is-mobile > .column.is-offset-10 { margin-left: 83.33333%; } .columns.is-mobile > .column.is-11 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .columns.is-mobile > .column.is-offset-11 { margin-left: 91.66667%; } .columns.is-mobile > .column.is-12 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .columns.is-mobile > .column.is-offset-12 { margin-left: 100%; } @media screen and (max-width: 768px) { .column.is-narrow-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; } .column.is-full-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-three-quarters-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-two-thirds-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.6666%; } .column.is-half-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-one-third-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.3333%; } .column.is-one-quarter-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-one-fifth-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 20%; } .column.is-two-fifths-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 40%; } .column.is-three-fifths-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 60%; } .column.is-four-fifths-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 80%; } .column.is-offset-three-quarters-mobile { margin-left: 75%; } .column.is-offset-two-thirds-mobile { margin-left: 66.6666%; } .column.is-offset-half-mobile { margin-left: 50%; } .column.is-offset-one-third-mobile { margin-left: 33.3333%; } .column.is-offset-one-quarter-mobile { margin-left: 25%; } .column.is-offset-one-fifth-mobile { margin-left: 20%; } .column.is-offset-two-fifths-mobile { margin-left: 40%; } .column.is-offset-three-fifths-mobile { margin-left: 60%; } .column.is-offset-four-fifths-mobile { margin-left: 80%; } .column.is-1-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .column.is-offset-1-mobile { margin-left: 8.33333%; } .column.is-2-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .column.is-offset-2-mobile { margin-left: 16.66667%; } .column.is-3-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-offset-3-mobile { margin-left: 25%; } .column.is-4-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .column.is-offset-4-mobile { margin-left: 33.33333%; } .column.is-5-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .column.is-offset-5-mobile { margin-left: 41.66667%; } .column.is-6-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-offset-6-mobile { margin-left: 50%; } .column.is-7-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .column.is-offset-7-mobile { margin-left: 58.33333%; } .column.is-8-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .column.is-offset-8-mobile { margin-left: 66.66667%; } .column.is-9-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-offset-9-mobile { margin-left: 75%; } .column.is-10-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .column.is-offset-10-mobile { margin-left: 83.33333%; } .column.is-11-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .column.is-offset-11-mobile { margin-left: 91.66667%; } .column.is-12-mobile { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-offset-12-mobile { margin-left: 100%; } } @media screen and (min-width: 769px), print { .column.is-narrow, .column.is-narrow-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; } .column.is-full, .column.is-full-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-three-quarters, .column.is-three-quarters-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-two-thirds, .column.is-two-thirds-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.6666%; } .column.is-half, .column.is-half-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-one-third, .column.is-one-third-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.3333%; } .column.is-one-quarter, .column.is-one-quarter-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-one-fifth, .column.is-one-fifth-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 20%; } .column.is-two-fifths, .column.is-two-fifths-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 40%; } .column.is-three-fifths, .column.is-three-fifths-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 60%; } .column.is-four-fifths, .column.is-four-fifths-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 80%; } .column.is-offset-three-quarters, .column.is-offset-three-quarters-tablet { margin-left: 75%; } .column.is-offset-two-thirds, .column.is-offset-two-thirds-tablet { margin-left: 66.6666%; } .column.is-offset-half, .column.is-offset-half-tablet { margin-left: 50%; } .column.is-offset-one-third, .column.is-offset-one-third-tablet { margin-left: 33.3333%; } .column.is-offset-one-quarter, .column.is-offset-one-quarter-tablet { margin-left: 25%; } .column.is-offset-one-fifth, .column.is-offset-one-fifth-tablet { margin-left: 20%; } .column.is-offset-two-fifths, .column.is-offset-two-fifths-tablet { margin-left: 40%; } .column.is-offset-three-fifths, .column.is-offset-three-fifths-tablet { margin-left: 60%; } .column.is-offset-four-fifths, .column.is-offset-four-fifths-tablet { margin-left: 80%; } .column.is-1, .column.is-1-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .column.is-offset-1, .column.is-offset-1-tablet { margin-left: 8.33333%; } .column.is-2, .column.is-2-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .column.is-offset-2, .column.is-offset-2-tablet { margin-left: 16.66667%; } .column.is-3, .column.is-3-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-offset-3, .column.is-offset-3-tablet { margin-left: 25%; } .column.is-4, .column.is-4-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .column.is-offset-4, .column.is-offset-4-tablet { margin-left: 33.33333%; } .column.is-5, .column.is-5-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .column.is-offset-5, .column.is-offset-5-tablet { margin-left: 41.66667%; } .column.is-6, .column.is-6-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-offset-6, .column.is-offset-6-tablet { margin-left: 50%; } .column.is-7, .column.is-7-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .column.is-offset-7, .column.is-offset-7-tablet { margin-left: 58.33333%; } .column.is-8, .column.is-8-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .column.is-offset-8, .column.is-offset-8-tablet { margin-left: 66.66667%; } .column.is-9, .column.is-9-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-offset-9, .column.is-offset-9-tablet { margin-left: 75%; } .column.is-10, .column.is-10-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .column.is-offset-10, .column.is-offset-10-tablet { margin-left: 83.33333%; } .column.is-11, .column.is-11-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .column.is-offset-11, .column.is-offset-11-tablet { margin-left: 91.66667%; } .column.is-12, .column.is-12-tablet { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-offset-12, .column.is-offset-12-tablet { margin-left: 100%; } } @media screen and (max-width: 1023px) { .column.is-narrow-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; } .column.is-full-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-three-quarters-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-two-thirds-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.6666%; } .column.is-half-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-one-third-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.3333%; } .column.is-one-quarter-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-one-fifth-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 20%; } .column.is-two-fifths-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 40%; } .column.is-three-fifths-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 60%; } .column.is-four-fifths-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 80%; } .column.is-offset-three-quarters-touch { margin-left: 75%; } .column.is-offset-two-thirds-touch { margin-left: 66.6666%; } .column.is-offset-half-touch { margin-left: 50%; } .column.is-offset-one-third-touch { margin-left: 33.3333%; } .column.is-offset-one-quarter-touch { margin-left: 25%; } .column.is-offset-one-fifth-touch { margin-left: 20%; } .column.is-offset-two-fifths-touch { margin-left: 40%; } .column.is-offset-three-fifths-touch { margin-left: 60%; } .column.is-offset-four-fifths-touch { margin-left: 80%; } .column.is-1-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .column.is-offset-1-touch { margin-left: 8.33333%; } .column.is-2-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .column.is-offset-2-touch { margin-left: 16.66667%; } .column.is-3-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-offset-3-touch { margin-left: 25%; } .column.is-4-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .column.is-offset-4-touch { margin-left: 33.33333%; } .column.is-5-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .column.is-offset-5-touch { margin-left: 41.66667%; } .column.is-6-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-offset-6-touch { margin-left: 50%; } .column.is-7-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .column.is-offset-7-touch { margin-left: 58.33333%; } .column.is-8-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .column.is-offset-8-touch { margin-left: 66.66667%; } .column.is-9-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-offset-9-touch { margin-left: 75%; } .column.is-10-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .column.is-offset-10-touch { margin-left: 83.33333%; } .column.is-11-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .column.is-offset-11-touch { margin-left: 91.66667%; } .column.is-12-touch { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-offset-12-touch { margin-left: 100%; } } @media screen and (min-width: 1024px) { .column.is-narrow-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; } .column.is-full-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-three-quarters-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-two-thirds-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.6666%; } .column.is-half-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-one-third-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.3333%; } .column.is-one-quarter-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-one-fifth-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 20%; } .column.is-two-fifths-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 40%; } .column.is-three-fifths-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 60%; } .column.is-four-fifths-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 80%; } .column.is-offset-three-quarters-desktop { margin-left: 75%; } .column.is-offset-two-thirds-desktop { margin-left: 66.6666%; } .column.is-offset-half-desktop { margin-left: 50%; } .column.is-offset-one-third-desktop { margin-left: 33.3333%; } .column.is-offset-one-quarter-desktop { margin-left: 25%; } .column.is-offset-one-fifth-desktop { margin-left: 20%; } .column.is-offset-two-fifths-desktop { margin-left: 40%; } .column.is-offset-three-fifths-desktop { margin-left: 60%; } .column.is-offset-four-fifths-desktop { margin-left: 80%; } .column.is-1-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .column.is-offset-1-desktop { margin-left: 8.33333%; } .column.is-2-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .column.is-offset-2-desktop { margin-left: 16.66667%; } .column.is-3-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-offset-3-desktop { margin-left: 25%; } .column.is-4-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .column.is-offset-4-desktop { margin-left: 33.33333%; } .column.is-5-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .column.is-offset-5-desktop { margin-left: 41.66667%; } .column.is-6-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-offset-6-desktop { margin-left: 50%; } .column.is-7-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .column.is-offset-7-desktop { margin-left: 58.33333%; } .column.is-8-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .column.is-offset-8-desktop { margin-left: 66.66667%; } .column.is-9-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-offset-9-desktop { margin-left: 75%; } .column.is-10-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .column.is-offset-10-desktop { margin-left: 83.33333%; } .column.is-11-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .column.is-offset-11-desktop { margin-left: 91.66667%; } .column.is-12-desktop { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-offset-12-desktop { margin-left: 100%; } } @media screen and (min-width: 1216px) { .column.is-narrow-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; } .column.is-full-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-three-quarters-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-two-thirds-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.6666%; } .column.is-half-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-one-third-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.3333%; } .column.is-one-quarter-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-one-fifth-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 20%; } .column.is-two-fifths-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 40%; } .column.is-three-fifths-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 60%; } .column.is-four-fifths-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 80%; } .column.is-offset-three-quarters-widescreen { margin-left: 75%; } .column.is-offset-two-thirds-widescreen { margin-left: 66.6666%; } .column.is-offset-half-widescreen { margin-left: 50%; } .column.is-offset-one-third-widescreen { margin-left: 33.3333%; } .column.is-offset-one-quarter-widescreen { margin-left: 25%; } .column.is-offset-one-fifth-widescreen { margin-left: 20%; } .column.is-offset-two-fifths-widescreen { margin-left: 40%; } .column.is-offset-three-fifths-widescreen { margin-left: 60%; } .column.is-offset-four-fifths-widescreen { margin-left: 80%; } .column.is-1-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .column.is-offset-1-widescreen { margin-left: 8.33333%; } .column.is-2-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .column.is-offset-2-widescreen { margin-left: 16.66667%; } .column.is-3-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-offset-3-widescreen { margin-left: 25%; } .column.is-4-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .column.is-offset-4-widescreen { margin-left: 33.33333%; } .column.is-5-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .column.is-offset-5-widescreen { margin-left: 41.66667%; } .column.is-6-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-offset-6-widescreen { margin-left: 50%; } .column.is-7-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .column.is-offset-7-widescreen { margin-left: 58.33333%; } .column.is-8-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .column.is-offset-8-widescreen { margin-left: 66.66667%; } .column.is-9-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-offset-9-widescreen { margin-left: 75%; } .column.is-10-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .column.is-offset-10-widescreen { margin-left: 83.33333%; } .column.is-11-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .column.is-offset-11-widescreen { margin-left: 91.66667%; } .column.is-12-widescreen { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-offset-12-widescreen { margin-left: 100%; } } @media screen and (min-width: 1408px) { .column.is-narrow-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; } .column.is-full-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-three-quarters-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-two-thirds-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.6666%; } .column.is-half-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-one-third-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.3333%; } .column.is-one-quarter-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-one-fifth-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 20%; } .column.is-two-fifths-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 40%; } .column.is-three-fifths-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 60%; } .column.is-four-fifths-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 80%; } .column.is-offset-three-quarters-fullhd { margin-left: 75%; } .column.is-offset-two-thirds-fullhd { margin-left: 66.6666%; } .column.is-offset-half-fullhd { margin-left: 50%; } .column.is-offset-one-third-fullhd { margin-left: 33.3333%; } .column.is-offset-one-quarter-fullhd { margin-left: 25%; } .column.is-offset-one-fifth-fullhd { margin-left: 20%; } .column.is-offset-two-fifths-fullhd { margin-left: 40%; } .column.is-offset-three-fifths-fullhd { margin-left: 60%; } .column.is-offset-four-fifths-fullhd { margin-left: 80%; } .column.is-1-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .column.is-offset-1-fullhd { margin-left: 8.33333%; } .column.is-2-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .column.is-offset-2-fullhd { margin-left: 16.66667%; } .column.is-3-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .column.is-offset-3-fullhd { margin-left: 25%; } .column.is-4-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .column.is-offset-4-fullhd { margin-left: 33.33333%; } .column.is-5-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .column.is-offset-5-fullhd { margin-left: 41.66667%; } .column.is-6-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .column.is-offset-6-fullhd { margin-left: 50%; } .column.is-7-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .column.is-offset-7-fullhd { margin-left: 58.33333%; } .column.is-8-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .column.is-offset-8-fullhd { margin-left: 66.66667%; } .column.is-9-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .column.is-offset-9-fullhd { margin-left: 75%; } .column.is-10-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .column.is-offset-10-fullhd { margin-left: 83.33333%; } .column.is-11-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .column.is-offset-11-fullhd { margin-left: 91.66667%; } .column.is-12-fullhd { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } .column.is-offset-12-fullhd { margin-left: 100%; } } .columns { margin-left: -0.75rem; margin-right: -0.75rem; margin-top: -0.75rem; } .columns:last-child { margin-bottom: -0.75rem; } .columns:not(:last-child) { margin-bottom: calc(1.5rem - 0.75rem); } .columns.is-centered { -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .columns.is-gapless { margin-left: 0; margin-right: 0; margin-top: 0; } .columns.is-gapless > .column { margin: 0; padding: 0 !important; } .columns.is-gapless:not(:last-child) { margin-bottom: 1.5rem; } .columns.is-gapless:last-child { margin-bottom: 0; } .columns.is-mobile { display: -webkit-box; display: -ms-flexbox; display: flex; } .columns.is-multiline { -ms-flex-wrap: wrap; flex-wrap: wrap; } .columns.is-vcentered { -webkit-box-align: center; -ms-flex-align: center; align-items: center; } @media screen and (min-width: 769px), print { .columns:not(.is-desktop) { display: -webkit-box; display: -ms-flexbox; display: flex; } } @media screen and (min-width: 1024px) { .columns.is-desktop { display: -webkit-box; display: -ms-flexbox; display: flex; } } .columns.is-variable { --columnGap: 0.75rem; margin-left: calc(-1 * var(--columnGap)); margin-right: calc(-1 * var(--columnGap)); } .columns.is-variable .column { padding-left: var(--columnGap); padding-right: var(--columnGap); } .columns.is-variable.is-0 { --columnGap: 0rem; } .columns.is-variable.is-1 { --columnGap: 0.25rem; } .columns.is-variable.is-2 { --columnGap: 0.5rem; } .columns.is-variable.is-3 { --columnGap: 0.75rem; } .columns.is-variable.is-4 { --columnGap: 1rem; } .columns.is-variable.is-5 { --columnGap: 1.25rem; } .columns.is-variable.is-6 { --columnGap: 1.5rem; } .columns.is-variable.is-7 { --columnGap: 1.75rem; } .columns.is-variable.is-8 { --columnGap: 2rem; } .tile { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: block; -ms-flex-preferred-size: 0; flex-basis: 0; -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; min-height: -webkit-min-content; min-height: -moz-min-content; min-height: min-content; } .tile.is-ancestor { margin-left: -0.75rem; margin-right: -0.75rem; margin-top: -0.75rem; } .tile.is-ancestor:last-child { margin-bottom: -0.75rem; } .tile.is-ancestor:not(:last-child) { margin-bottom: 0.75rem; } .tile.is-child { margin: 0 !important; } .tile.is-parent { padding: 0.75rem; } .tile.is-vertical { -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; } .tile.is-vertical > .tile.is-child:not(:last-child) { margin-bottom: 1.5rem !important; } @media screen and (min-width: 769px), print { .tile:not(.is-child) { display: -webkit-box; display: -ms-flexbox; display: flex; } .tile.is-1 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 8.33333%; } .tile.is-2 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 16.66667%; } .tile.is-3 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 25%; } .tile.is-4 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 33.33333%; } .tile.is-5 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 41.66667%; } .tile.is-6 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 50%; } .tile.is-7 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 58.33333%; } .tile.is-8 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 66.66667%; } .tile.is-9 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 75%; } .tile.is-10 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 83.33333%; } .tile.is-11 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 91.66667%; } .tile.is-12 { -webkit-box-flex: 0; -ms-flex: none; flex: none; width: 100%; } } .hero { -webkit-box-align: stretch; -ms-flex-align: stretch; align-items: stretch; display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-orient: vertical; -webkit-box-direction: normal; -ms-flex-direction: column; flex-direction: column; -webkit-box-pack: justify; -ms-flex-pack: justify; justify-content: space-between; } .hero .navbar { background: none; } .hero .tabs ul { border-bottom: none; } .hero.is-white { background-color: white; color: #0a0a0a; } .hero.is-white a:not(.button), .hero.is-white strong { color: inherit; } .hero.is-white .title { color: #0a0a0a; } .hero.is-white .subtitle { color: rgba(10, 10, 10, 0.9); } .hero.is-white .subtitle a:not(.button), .hero.is-white .subtitle strong { color: #0a0a0a; } @media screen and (max-width: 1023px) { .hero.is-white .navbar-menu { background-color: white; } } .hero.is-white .navbar-item, .hero.is-white .navbar-link { color: rgba(10, 10, 10, 0.7); } .hero.is-white a.navbar-item:hover, .hero.is-white a.navbar-item.is-active, .hero.is-white .navbar-link:hover, .hero.is-white .navbar-link.is-active { background-color: #f2f2f2; color: #0a0a0a; } .hero.is-white .tabs a { color: #0a0a0a; opacity: 0.9; } .hero.is-white .tabs a:hover { opacity: 1; } .hero.is-white .tabs li.is-active a { opacity: 1; } .hero.is-white .tabs.is-boxed a, .hero.is-white .tabs.is-toggle a { color: #0a0a0a; } .hero.is-white .tabs.is-boxed a:hover, .hero.is-white .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: #0a0a0a; border-color: #0a0a0a; color: white; } .hero.is-white.is-bold { background-image: linear-gradient(141deg, #e6e6e6 0%, white 71%, white 100%); } @media screen and (max-width: 768px) { .hero.is-white.is-bold .navbar-menu { background-image: linear-gradient(141deg, #e6e6e6 0%, white 71%, white 100%); } } .hero.is-black { background-color: #0a0a0a; color: white; } .hero.is-black a:not(.button), .hero.is-black strong { color: inherit; } .hero.is-black .title { color: white; } .hero.is-black .subtitle { color: rgba(255, 255, 255, 0.9); } .hero.is-black .subtitle a:not(.button), .hero.is-black .subtitle strong { color: white; } @media screen and (max-width: 1023px) { .hero.is-black .navbar-menu { background-color: #0a0a0a; } } .hero.is-black .navbar-item, .hero.is-black .navbar-link { color: rgba(255, 255, 255, 0.7); } .hero.is-black a.navbar-item:hover, .hero.is-black a.navbar-item.is-active, .hero.is-black .navbar-link:hover, .hero.is-black .navbar-link.is-active { background-color: black; color: white; } .hero.is-black .tabs a { color: white; opacity: 0.9; } .hero.is-black .tabs a:hover { opacity: 1; } .hero.is-black .tabs li.is-active a { opacity: 1; } .hero.is-black .tabs.is-boxed a, .hero.is-black .tabs.is-toggle a { color: white; } .hero.is-black .tabs.is-boxed a:hover, .hero.is-black .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: white; border-color: white; color: #0a0a0a; } .hero.is-black.is-bold { background-image: linear-gradient(141deg, black 0%, #0a0a0a 71%, #181616 100%); } @media screen and (max-width: 768px) { .hero.is-black.is-bold .navbar-menu { background-image: linear-gradient(141deg, black 0%, #0a0a0a 71%, #181616 100%); } } .hero.is-light { background-color: whitesmoke; color: #363636; } .hero.is-light a:not(.button), .hero.is-light strong { color: inherit; } .hero.is-light .title { color: #363636; } .hero.is-light .subtitle { color: rgba(54, 54, 54, 0.9); } .hero.is-light .subtitle a:not(.button), .hero.is-light .subtitle strong { color: #363636; } @media screen and (max-width: 1023px) { .hero.is-light .navbar-menu { background-color: whitesmoke; } } .hero.is-light .navbar-item, .hero.is-light .navbar-link { color: rgba(54, 54, 54, 0.7); } .hero.is-light a.navbar-item:hover, .hero.is-light a.navbar-item.is-active, .hero.is-light .navbar-link:hover, .hero.is-light .navbar-link.is-active { background-color: #e8e8e8; color: #363636; } .hero.is-light .tabs a { color: #363636; opacity: 0.9; } .hero.is-light .tabs a:hover { opacity: 1; } .hero.is-light .tabs li.is-active a { opacity: 1; } .hero.is-light .tabs.is-boxed a, .hero.is-light .tabs.is-toggle a { color: #363636; } .hero.is-light .tabs.is-boxed a:hover, .hero.is-light .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: #363636; border-color: #363636; color: whitesmoke; } .hero.is-light.is-bold { background-image: linear-gradient(141deg, #dfd8d9 0%, whitesmoke 71%, white 100%); } @media screen and (max-width: 768px) { .hero.is-light.is-bold .navbar-menu { background-image: linear-gradient(141deg, #dfd8d9 0%, whitesmoke 71%, white 100%); } } .hero.is-dark { background-color: #363636; color: whitesmoke; } .hero.is-dark a:not(.button), .hero.is-dark strong { color: inherit; } .hero.is-dark .title { color: whitesmoke; } .hero.is-dark .subtitle { color: rgba(245, 245, 245, 0.9); } .hero.is-dark .subtitle a:not(.button), .hero.is-dark .subtitle strong { color: whitesmoke; } @media screen and (max-width: 1023px) { .hero.is-dark .navbar-menu { background-color: #363636; } } .hero.is-dark .navbar-item, .hero.is-dark .navbar-link { color: rgba(245, 245, 245, 0.7); } .hero.is-dark a.navbar-item:hover, .hero.is-dark a.navbar-item.is-active, .hero.is-dark .navbar-link:hover, .hero.is-dark .navbar-link.is-active { background-color: #292929; color: whitesmoke; } .hero.is-dark .tabs a { color: whitesmoke; opacity: 0.9; } .hero.is-dark .tabs a:hover { opacity: 1; } .hero.is-dark .tabs li.is-active a { opacity: 1; } .hero.is-dark .tabs.is-boxed a, .hero.is-dark .tabs.is-toggle a { color: whitesmoke; } .hero.is-dark .tabs.is-boxed a:hover, .hero.is-dark .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: whitesmoke; border-color: whitesmoke; color: #363636; } .hero.is-dark.is-bold { background-image: linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%); } @media screen and (max-width: 768px) { .hero.is-dark.is-bold .navbar-menu { background-image: linear-gradient(141deg, #1f191a 0%, #363636 71%, #46403f 100%); } } .hero.is-primary { background-color: #00d1b2; color: #fff; } .hero.is-primary a:not(.button), .hero.is-primary strong { color: inherit; } .hero.is-primary .title { color: #fff; } .hero.is-primary .subtitle { color: rgba(255, 255, 255, 0.9); } .hero.is-primary .subtitle a:not(.button), .hero.is-primary .subtitle strong { color: #fff; } @media screen and (max-width: 1023px) { .hero.is-primary .navbar-menu { background-color: #00d1b2; } } .hero.is-primary .navbar-item, .hero.is-primary .navbar-link { color: rgba(255, 255, 255, 0.7); } .hero.is-primary a.navbar-item:hover, .hero.is-primary a.navbar-item.is-active, .hero.is-primary .navbar-link:hover, .hero.is-primary .navbar-link.is-active { background-color: #00b89c; color: #fff; } .hero.is-primary .tabs a { color: #fff; opacity: 0.9; } .hero.is-primary .tabs a:hover { opacity: 1; } .hero.is-primary .tabs li.is-active a { opacity: 1; } .hero.is-primary .tabs.is-boxed a, .hero.is-primary .tabs.is-toggle a { color: #fff; } .hero.is-primary .tabs.is-boxed a:hover, .hero.is-primary .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: #fff; border-color: #fff; color: #00d1b2; } .hero.is-primary.is-bold { background-image: linear-gradient(141deg, #009e6c 0%, #00d1b2 71%, #00e7eb 100%); } @media screen and (max-width: 768px) { .hero.is-primary.is-bold .navbar-menu { background-image: linear-gradient(141deg, #009e6c 0%, #00d1b2 71%, #00e7eb 100%); } } .hero.is-link { background-color: #7a91c1; color: #fff; } .hero.is-link a:not(.button), .hero.is-link strong { color: inherit; } .hero.is-link .title { color: #fff; } .hero.is-link .subtitle { color: rgba(255, 255, 255, 0.9); } .hero.is-link .subtitle a:not(.button), .hero.is-link .subtitle strong { color: #fff; } @media screen and (max-width: 1023px) { .hero.is-link .navbar-menu { background-color: #7a91c1; } } .hero.is-link .navbar-item, .hero.is-link .navbar-link { color: rgba(255, 255, 255, 0.7); } .hero.is-link a.navbar-item:hover, .hero.is-link a.navbar-item.is-active, .hero.is-link .navbar-link:hover, .hero.is-link .navbar-link.is-active { background-color: #2366d1; color: #fff; } .hero.is-link .tabs a { color: #fff; opacity: 0.9; } .hero.is-link .tabs a:hover { opacity: 1; } .hero.is-link .tabs li.is-active a { opacity: 1; } .hero.is-link .tabs.is-boxed a, .hero.is-link .tabs.is-toggle a { color: #fff; } .hero.is-link .tabs.is-boxed a:hover, .hero.is-link .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: #fff; border-color: #fff; color: #7a91c1; } .hero.is-link.is-bold { background-image: linear-gradient(141deg, #1577c6 0%, #7a91c1 71%, #4366e5 100%); } @media screen and (max-width: 768px) { .hero.is-link.is-bold .navbar-menu { background-image: linear-gradient(141deg, #1577c6 0%, #7a91c1 71%, #4366e5 100%); } } .hero.is-info { background-color: #209cee; color: #fff; } .hero.is-info a:not(.button), .hero.is-info strong { color: inherit; } .hero.is-info .title { color: #fff; } .hero.is-info .subtitle { color: rgba(255, 255, 255, 0.9); } .hero.is-info .subtitle a:not(.button), .hero.is-info .subtitle strong { color: #fff; } @media screen and (max-width: 1023px) { .hero.is-info .navbar-menu { background-color: #209cee; } } .hero.is-info .navbar-item, .hero.is-info .navbar-link { color: rgba(255, 255, 255, 0.7); } .hero.is-info a.navbar-item:hover, .hero.is-info a.navbar-item.is-active, .hero.is-info .navbar-link:hover, .hero.is-info .navbar-link.is-active { background-color: #118fe4; color: #fff; } .hero.is-info .tabs a { color: #fff; opacity: 0.9; } .hero.is-info .tabs a:hover { opacity: 1; } .hero.is-info .tabs li.is-active a { opacity: 1; } .hero.is-info .tabs.is-boxed a, .hero.is-info .tabs.is-toggle a { color: #fff; } .hero.is-info .tabs.is-boxed a:hover, .hero.is-info .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: #fff; border-color: #fff; color: #209cee; } .hero.is-info.is-bold { background-image: linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%); } @media screen and (max-width: 768px) { .hero.is-info.is-bold .navbar-menu { background-image: linear-gradient(141deg, #04a6d7 0%, #209cee 71%, #3287f5 100%); } } .hero.is-success { background-color: #23d160; color: #fff; } .hero.is-success a:not(.button), .hero.is-success strong { color: inherit; } .hero.is-success .title { color: #fff; } .hero.is-success .subtitle { color: rgba(255, 255, 255, 0.9); } .hero.is-success .subtitle a:not(.button), .hero.is-success .subtitle strong { color: #fff; } @media screen and (max-width: 1023px) { .hero.is-success .navbar-menu { background-color: #23d160; } } .hero.is-success .navbar-item, .hero.is-success .navbar-link { color: rgba(255, 255, 255, 0.7); } .hero.is-success a.navbar-item:hover, .hero.is-success a.navbar-item.is-active, .hero.is-success .navbar-link:hover, .hero.is-success .navbar-link.is-active { background-color: #20bc56; color: #fff; } .hero.is-success .tabs a { color: #fff; opacity: 0.9; } .hero.is-success .tabs a:hover { opacity: 1; } .hero.is-success .tabs li.is-active a { opacity: 1; } .hero.is-success .tabs.is-boxed a, .hero.is-success .tabs.is-toggle a { color: #fff; } .hero.is-success .tabs.is-boxed a:hover, .hero.is-success .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: #fff; border-color: #fff; color: #23d160; } .hero.is-success.is-bold { background-image: linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%); } @media screen and (max-width: 768px) { .hero.is-success.is-bold .navbar-menu { background-image: linear-gradient(141deg, #12af2f 0%, #23d160 71%, #2ce28a 100%); } } .hero.is-warning { background-color: #ffdd57; color: rgba(0, 0, 0, 0.7); } .hero.is-warning a:not(.button), .hero.is-warning strong { color: inherit; } .hero.is-warning .title { color: rgba(0, 0, 0, 0.7); } .hero.is-warning .subtitle { color: rgba(0, 0, 0, 0.9); } .hero.is-warning .subtitle a:not(.button), .hero.is-warning .subtitle strong { color: rgba(0, 0, 0, 0.7); } @media screen and (max-width: 1023px) { .hero.is-warning .navbar-menu { background-color: #ffdd57; } } .hero.is-warning .navbar-item, .hero.is-warning .navbar-link { color: rgba(0, 0, 0, 0.7); } .hero.is-warning a.navbar-item:hover, .hero.is-warning a.navbar-item.is-active, .hero.is-warning .navbar-link:hover, .hero.is-warning .navbar-link.is-active { background-color: #ffd83d; color: rgba(0, 0, 0, 0.7); } .hero.is-warning .tabs a { color: rgba(0, 0, 0, 0.7); opacity: 0.9; } .hero.is-warning .tabs a:hover { opacity: 1; } .hero.is-warning .tabs li.is-active a { opacity: 1; } .hero.is-warning .tabs.is-boxed a, .hero.is-warning .tabs.is-toggle a { color: rgba(0, 0, 0, 0.7); } .hero.is-warning .tabs.is-boxed a:hover, .hero.is-warning .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: rgba(0, 0, 0, 0.7); border-color: rgba(0, 0, 0, 0.7); color: #ffdd57; } .hero.is-warning.is-bold { background-image: linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%); } @media screen and (max-width: 768px) { .hero.is-warning.is-bold .navbar-menu { background-image: linear-gradient(141deg, #ffaf24 0%, #ffdd57 71%, #fffa70 100%); } } .hero.is-danger { background-color: #ff3860; color: #fff; } .hero.is-danger a:not(.button), .hero.is-danger strong { color: inherit; } .hero.is-danger .title { color: #fff; } .hero.is-danger .subtitle { color: rgba(255, 255, 255, 0.9); } .hero.is-danger .subtitle a:not(.button), .hero.is-danger .subtitle strong { color: #fff; } @media screen and (max-width: 1023px) { .hero.is-danger .navbar-menu { background-color: #ff3860; } } .hero.is-danger .navbar-item, .hero.is-danger .navbar-link { color: rgba(255, 255, 255, 0.7); } .hero.is-danger a.navbar-item:hover, .hero.is-danger a.navbar-item.is-active, .hero.is-danger .navbar-link:hover, .hero.is-danger .navbar-link.is-active { background-color: #ff1f4b; color: #fff; } .hero.is-danger .tabs a { color: #fff; opacity: 0.9; } .hero.is-danger .tabs a:hover { opacity: 1; } .hero.is-danger .tabs li.is-active a { opacity: 1; } .hero.is-danger .tabs.is-boxed a, .hero.is-danger .tabs.is-toggle a { color: #fff; } .hero.is-danger .tabs.is-boxed a:hover, .hero.is-danger .tabs.is-toggle a:hover { background-color: rgba(10, 10, 10, 0.1); } .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 { background-color: #fff; border-color: #fff; color: #ff3860; } .hero.is-danger.is-bold { background-image: linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%); } @media screen and (max-width: 768px) { .hero.is-danger.is-bold .navbar-menu { background-image: linear-gradient(141deg, #ff0561 0%, #ff3860 71%, #ff5257 100%); } } .hero.is-small .hero-body { padding-bottom: 1.5rem; padding-top: 1.5rem; } @media screen and (min-width: 769px), print { .hero.is-medium .hero-body { padding-bottom: 9rem; padding-top: 9rem; } } @media screen and (min-width: 769px), print { .hero.is-large .hero-body { padding-bottom: 18rem; padding-top: 18rem; } } .hero.is-halfheight .hero-body, .hero.is-fullheight .hero-body { -webkit-box-align: center; -ms-flex-align: center; align-items: center; display: -webkit-box; display: -ms-flexbox; display: flex; } .hero.is-halfheight .hero-body > .container, .hero.is-fullheight .hero-body > .container { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 1; flex-shrink: 1; } .hero.is-halfheight { min-height: 50vh; } .hero.is-fullheight { min-height: 100vh; } .hero-video { bottom: 0; left: 0; position: absolute; right: 0; top: 0; overflow: hidden; } .hero-video video { left: 50%; min-height: 100%; min-width: 100%; position: absolute; top: 50%; -webkit-transform: translate3d(-50%, -50%, 0); transform: translate3d(-50%, -50%, 0); } .hero-video.is-transparent { opacity: 0.3; } @media screen and (max-width: 768px) { .hero-video { display: none; } } .hero-buttons { margin-top: 1.5rem; } @media screen and (max-width: 768px) { .hero-buttons .button { display: -webkit-box; display: -ms-flexbox; display: flex; } .hero-buttons .button:not(:last-child) { margin-bottom: 0.75rem; } } @media screen and (min-width: 769px), print { .hero-buttons { display: -webkit-box; display: -ms-flexbox; display: flex; -webkit-box-pack: center; -ms-flex-pack: center; justify-content: center; } .hero-buttons .button:not(:last-child) { margin-right: 1.5rem; } } .hero-head, .hero-foot { -webkit-box-flex: 0; -ms-flex-positive: 0; flex-grow: 0; -ms-flex-negative: 0; flex-shrink: 0; } .hero-body { -webkit-box-flex: 1; -ms-flex-positive: 1; flex-grow: 1; -ms-flex-negative: 0; flex-shrink: 0; padding: 3rem 1.5rem; } .section { padding: 1.5rem 1.5rem; } @media screen and (min-width: 1024px) { .section.is-small { padding: 1.5rem 1.5rem; } .section.is-medium { padding: 9rem 1.5rem; } .section.is-large { padding: 18rem 1.5rem; } } .footer { background-color: whitesmoke; padding: 3rem 1.5rem 6rem; } /*# sourceMappingURL=bulma.css.map */ mosquitto-2.0.18/www/themes/mosquitto/assets/css/local.css000066400000000000000000000031771450213760600237350ustar00rootroot00000000000000.title { color: #363636; font-size: 2rem; font-weight: 300; line-height: 1.125; } finalfooter { display: block; } .finalfooter { background-color: black; color: whitesmoke; padding: 1rem 1.5rem 1rem; margin-top: 1rem; } div.footerlink a { color: whitesmoke; cursor: pointer; text-decoration: none; -webkit-transition: none 86ms ease-out; transition: none 86ms ease-out; padding-right: 1.5rem; } div.footerlink a:hover { color: gray; } .footer { background-color: whitesmoke; padding: 2rem 1.5rem 1rem; } .column-justify { text-align: justify; } .column-justify h1 { text-align: center; } img.center { display: block; margin-left: auto; margin-right: auto; } /* Blog */ h1.p-name, .authorpage h1, .tagpage h1 /* Tag page */{ font-size: 36px; margin-top: 20px; margin-bottom: 10px; } .e-content.entry-content h2 { font-size: 150%; } div.e-content { margin-top: 14px; } div.e-content.entry-content p, div.e-content.entry-content div p, div.e-content.entry-content div { margin-bottom: 10px; } div.e-content.entry-content ul { padding-left: 40px; margin-bottom: 10px; } div.e-content.entry-content ul li { list-style-type: disc; } div.e-content.entry-content div blockquote { background-color: whitesmoke; padding: 1.5rem; margin: 0.5rem; } .pager::before, .pager::after { clear: both; display: table; content: " "; } .pager .previous a { float: left; } .pager .next a { float: right; } .pager li { display: inline; } .pager li a { display: inline-block; padding: 5px 14px; border: 1px solid #ddd; border-radius: 15px; } .dateline { margin-top: -1rem; } /* Tag Page */ .listdate { margin-right: 3rem; } mosquitto-2.0.18/www/themes/mosquitto/assets/css/man.css000066400000000000000000000043171450213760600234130ustar00rootroot00000000000000/* DocBook Man Page */ .refentry { } .refsynopsisdiv h3 { font-size: 1.5rem; font-weight: 350; line-height: 1.125; margin-top: 1rem; margin-bottom: 0.5rem; } .refnamediv h3 { font-size: 1.5rem; font-weight: 350; line-height: 1.125; } .refnamediv p { } .refsect1 { } .refsect1 h3 { margin-top: 1rem; margin-bottom: 0.5rem; font-weight: 350; font-size: 1.5rem; } .refsect1 p { margin-top: 1rem; } .refsect2 { } .refsect2 h4 { font-size: 1.25rem; line-height: 1.5; margin-top: 1rem; margin-bottom: 1rem; } .note h4 { font-size: 1.25rem; line-height: 1.5; margin-top: 1rem; margin-bottom: 0; } .note { margin-top: 1rem; } .funcsynopsis { } .funcprototype-table { } .funcdef { color: #d73a49; } .funcprototype-table tr td { font-size: 12px; color: #d73a49; background-color: whitesmoke; font-family: monospace; } .fsfunc { font-size: 12px; color: #6f42c1; font-weight: normal; } .pdparam { font-size: 12px; color: black; } .email { color: black; background-color: inherit; } .command { color: black; } .uri, .literal { background-color: inherit; color: inherit; } .filename { color: inherit; } .option { font-family: monospace; color: inherit; } .listitem { margin: 1rem 0 1rem 2rem; } dl.variablelist dt { margin-top: 1rem; } dl.variablelist dd { margin-left: 1rem; } .term { background-color: whitesmoke; font-size: 0.875rem; padding: 0.25em 0.5em 0.25em 0; } .term .option, dd p .code, .cmdsynopsis, .cmdsynopsis p .command, .cmdsynopsis p .option, .replaceable code { font-family: monospace; background-color: whitesmoke; color: black; font-size: 0.875rem; } .cmdsynopsis { padding-top: 0.25rem; padding-bottom: 0.25rem; text-indent: -2rem; padding-left: 2rem; } .informaltable { padding: 0.5rem; } .informaltable thead tr th, .informaltable tbody tr td{ padding: 0.5rem; } .itemizedlist .listitem p { display: inline-block; padding: 0.25rem; background-color: whitesmoke; font-family: monospace; font-size: 0.875rem; list-style-type: none; margin-top: 0; margin-bottom: 0.5rem; } .itemizedlist .listitem, li.listitem { margin-top: 0; margin-bottom: 0; list-style-type: none; } div.itemizedlist, ul.itemizedlist { margin-top: 0; margin-bottom: 1rem; } mosquitto-2.0.18/www/themes/mosquitto/engine000066400000000000000000000000051450213760600212120ustar00rootroot00000000000000mako mosquitto-2.0.18/www/themes/mosquitto/parent000066400000000000000000000000051450213760600212360ustar00rootroot00000000000000base mosquitto-2.0.18/www/themes/mosquitto/templates/000077500000000000000000000000001450213760600220255ustar00rootroot00000000000000mosquitto-2.0.18/www/themes/mosquitto/templates/base.tmpl000066400000000000000000000014301450213760600236330ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="base" file="base_helper.tmpl" import="*"/> <%namespace name="header" file="base_header.tmpl" import="*"/> <%namespace name="footer" file="base_footer.tmpl" import="*"/> <%namespace name="annotations" file="annotation_helper.tmpl"/> ${set_locale(lang)} ${base.html_headstart()} <%block name="extra_head"> ### Leave this block alone. ${template_hooks['extra_head']()} ${header.html_header()}
<%block name="content">
${footer.html_footer()} ${base.late_load_js()} <%block name="extra_js"> ${body_end} ${template_hooks['body_end']()} mosquitto-2.0.18/www/themes/mosquitto/templates/base_footer.tmpl000066400000000000000000000016541450213760600252210ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="base" file="base_helper.tmpl" import="*"/> <%def name="html_footer()"> %if content_footer: %endif mosquitto-2.0.18/www/themes/mosquitto/templates/base_header.tmpl000066400000000000000000000072221450213760600251500ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="base" file="base_helper.tmpl" import="*"/> <%def name="html_header()"> ${template_hooks['page_header']()} <%def name="html_site_title()"> % if (post and post.title):
% if title == blog_title:

Eclipse Mosquitto™

An open source MQTT broker

% elif post and post.title:

${post.title()|h}

% endif

% endif <%def name="html_navigation_links()">

<%def name="html_translation_header()"> %if len(translations) > 1:

${messages("Languages:")}

${base.html_translations()}
%endif mosquitto-2.0.18/www/themes/mosquitto/templates/base_helper.tmpl000066400000000000000000000101651450213760600251770ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%def name="html_headstart()"> % if use_base_tag: % endif %if description: %endif %if title == blog_title: ${blog_title|h} %else: ${title|h} | ${blog_title|h} %endif ${html_stylesheets()} % if meta_generator_tag: % endif ${html_feedlinks()} %if favicons: %for name, file, size in favicons: %endfor %endif % if comment_system == 'facebook': % endif %if prevlink: %endif %if nextlink: %endif %if use_cdn: %else: %endif ${extra_head_data} <%def name="late_load_js()"> ${social_buttons_code} <%def name="html_stylesheets()"> %if use_bundles: %if use_cdn: %else: %endif %else: %if has_custom_css: %endif %endif % if needs_ipython_css: % endif <%def name="html_feedlinks()"> %if rss_link: ${rss_link} %elif generate_rss: %if len(translations) > 1: %for language in sorted(translations): %endfor %else: %endif %endif %if generate_atom: %if len(translations) > 1: %for language in sorted(translations): %endfor %else: %endif %endif <%def name="html_translations()"> mosquitto-2.0.18/www/themes/mosquitto/templates/index.tmpl000066400000000000000000000041771450213760600240430ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="helper" file="index_helper.tmpl"/> <%namespace name="math" file="math_helper.tmpl"/> <%namespace name="comments" file="comments_helper.tmpl"/> <%namespace name="pagination" file="pagination_helper.tmpl"/> <%inherit file="base.tmpl"/> <%block name="extra_head"> ${parent.extra_head()} % if posts and (permalink == '/' or permalink == '/' + index_file): % endif ${math.math_styles_ifposts(posts)} <%block name="content"> <%block name="content_header"> % if 'main_index' in pagekind: ${front_index_header} % endif % if page_links: ${pagination.page_navigation(current_page, page_links, prevlink, nextlink, prev_next_links_reversed)} % endif
% for post in posts:

${post.title()|h}

%if index_teasers:
${post.text(teaser_only=True)} %else:
${post.text(teaser_only=False)} %endif

% endfor
${helper.html_pager()} ${comments.comment_link_script()} mosquitto-2.0.18/www/themes/mosquitto/templates/post.tmpl000066400000000000000000000033631450213760600237150ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="helper" file="post_helper.tmpl"/> <%namespace name="pheader" file="post_header.tmpl"/> <%namespace name="comments" file="comments_helper.tmpl"/> <%namespace name="math" file="math_helper.tmpl"/> <%inherit file="base.tmpl"/> <%block name="extra_head"> ${parent.extra_head()} % if post.meta('keywords'): % endif ## %if post.prev_post: %endif %if post.next_post: %endif % if post.is_draft: % endif ${helper.open_graph_metadata(post)} ${helper.twitter_card_information(post)} ${helper.meta_translations(post)} ${math.math_styles_ifpost(post)} <%block name="content">
${pheader.html_post_header()}
${post.text()}
% if not post.meta('nocomments') and site_has_comments:

${messages("Comments")}

${comments.comment_form(post.permalink(absolute=True), post.title(), post._base_path)}
% endif ${math.math_scripts_ifpost(post)}
${comments.comment_link_script()} mosquitto-2.0.18/www/themes/mosquitto/templates/post_header.tmpl000066400000000000000000000050701450213760600252220ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="helper" file="post_helper.tmpl"/> <%namespace name="comments" file="comments_helper.tmpl"/> <%def name="html_title()"> %if title and not post.meta('hidetitle'):

${post.title()|h}

%endif <%def name="html_translations(post)"> % if len(post.translated_to) > 1: % endif <%def name="html_sourcelink()"> % if show_sourcelink:

${messages("Source")}

% endif <%def name="html_post_header()">
##${html_title()} ${html_translations(post)}
mosquitto-2.0.18/www/themes/mosquitto/templates/story.tmpl000066400000000000000000000016101450213760600241010ustar00rootroot00000000000000## -*- coding: utf-8 -*- <%namespace name="helper" file="post_helper.tmpl"/> <%namespace name="pheader" file="post_header.tmpl"/> <%namespace name="comments" file="comments_helper.tmpl"/> <%namespace name="math" file="math_helper.tmpl"/> <%inherit file="post.tmpl"/> <%block name="content">
###${pheader.html_title()} ${pheader.html_translations(post)}
${post.text()}
%if site_has_comments and enable_comments and not post.meta('nocomments'):

${messages("Comments")}

${comments.comment_form(post.permalink(absolute=True), post.title(), post.base_path)}
%endif ${math.math_scripts_ifpost(post)}