diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..3ed1387 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,23 @@ +name: Build + +on: + push: + pull_request: + +permissions: + contents: read + +jobs: + compile-tests: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + environment: ["esp8266", "esp32"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - run: python -m pip install --upgrade platformio==6.1.19 + - run: pio test -e ${{ matrix.environment }} --without-uploading --without-testing diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..9126114 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,23 @@ +name: Publish release + +on: + push: + tags: + - 'v*' + +permissions: + contents: write + +jobs: + publish: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: '3.11' + - run: python -m pip install --upgrade platformio==6.1.19 + - run: ./scripts/prepare-release.sh "$GITHUB_REF_NAME" + - run: gh release create "$GITHUB_REF_NAME" --generate-notes --title "$GITHUB_REF_NAME" + env: + GH_TOKEN: ${{ github.token }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fbbee11 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,8 @@ +# Changelog + +## 3.0.0 + +- Establish `device-framework` as the independently maintained canonical branch. +- Add safe default parameter construction and allocation-failure handling. +- Pin the DFTE dependency used by PlatformIO builds. + diff --git a/lib/WiFiManager/include/WiFiManagerParameter.h b/lib/WiFiManager/include/WiFiManagerParameter.h index a49862a..0ed9d48 100644 --- a/lib/WiFiManager/include/WiFiManagerParameter.h +++ b/lib/WiFiManager/include/WiFiManagerParameter.h @@ -34,7 +34,9 @@ class WiFiManagerParameter { WiFiManagerParameter(const char *id, const char *label, const char *defaultValue, int length, const char *custom); WiFiManagerParameter(const char *id, const char *label, const char *defaultValue, int length, const char *custom, int labelPlacement); virtual ~WiFiManagerParameter(); - // WiFiManagerParameter& operator=(const WiFiManagerParameter& rhs); + + WiFiManagerParameter(const WiFiManagerParameter&) = delete; + WiFiManagerParameter& operator=(const WiFiManagerParameter&) = delete; const char *getID() const; const char *getValue() const; @@ -48,7 +50,6 @@ class WiFiManagerParameter { protected: void init(const char *id, const char *label, const char *defaultValue, int length, const char *custom, int labelPlacement); - WiFiManagerParameter& operator=(const WiFiManagerParameter&); const char *_id; const char *_label; char *_value; diff --git a/lib/WiFiManager/src/WiFiManager.cpp b/lib/WiFiManager/src/WiFiManager.cpp index 2bd1fc4..294fe8a 100644 --- a/lib/WiFiManager/src/WiFiManager.cpp +++ b/lib/WiFiManager/src/WiFiManager.cpp @@ -63,6 +63,9 @@ bool normalizeHostname(String& hostname) { * @return true if added successfully, false on error */ bool WiFiManager::portalAddParameter(WiFiManagerParameter *p) { + if (!p) { + return false; + } // check param id is valid, unless null if(p->getID()){ @@ -82,18 +85,25 @@ bool WiFiManager::portalAddParameter(WiFiManagerParameter *p) { log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("allocating params bytes:"),_max_params * sizeof(WiFiManagerParameter*)); #endif _params = (WiFiManagerParameter**)malloc(_max_params * sizeof(WiFiManagerParameter*)); + if (_params == NULL) { + #ifndef WM_NO_LOG + log(WiFiManagerLogLevel::Error, kWiFiMgrLogSubsystem,F("[ERROR] failed to allocate params")); + #endif + return false; + } } - // resize the params array by increment of WIFI_MANAGER_MAX_PARAMS + // Resize only after a successful allocation so _max_params always matches storage. if(_paramsCount == _max_params){ - _max_params += WIFI_MANAGER_MAX_PARAMS; + const size_t expandedMaxParams = _max_params + WIFI_MANAGER_MAX_PARAMS; #ifndef WM_NO_LOG - log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("Updated _max_params:"),_max_params); - log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("re-allocating params bytes:"),_max_params * sizeof(WiFiManagerParameter*)); + log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("Updated _max_params:"),expandedMaxParams); + log(WiFiManagerLogLevel::Trace, kWiFiMgrLogSubsystem,F("re-allocating params bytes:"),expandedMaxParams * sizeof(WiFiManagerParameter*)); #endif - WiFiManagerParameter** new_params = (WiFiManagerParameter**)realloc(_params, _max_params * sizeof(WiFiManagerParameter*)); + WiFiManagerParameter** new_params = (WiFiManagerParameter**)realloc(_params, expandedMaxParams * sizeof(WiFiManagerParameter*)); if (new_params != NULL) { _params = new_params; + _max_params = expandedMaxParams; } else { #ifndef WM_NO_LOG log(WiFiManagerLogLevel::Error, kWiFiMgrLogSubsystem,F("[ERROR] failed to realloc params, size not increased!")); diff --git a/lib/WiFiManager/src/WiFiManagerParameter.cpp b/lib/WiFiManager/src/WiFiManagerParameter.cpp index eab1ab1..a1ac9b7 100644 --- a/lib/WiFiManager/src/WiFiManagerParameter.cpp +++ b/lib/WiFiManager/src/WiFiManagerParameter.cpp @@ -8,9 +8,11 @@ #include "WiFiManagerParameter.h" #include +#include -WiFiManagerParameter::WiFiManagerParameter() { - WiFiManagerParameter(""); +WiFiManagerParameter::WiFiManagerParameter() + : WiFiManagerParameter("") +{ } WiFiManagerParameter::WiFiManagerParameter(const char *custom) { @@ -63,27 +65,24 @@ WiFiManagerParameter::~WiFiManagerParameter() { // @note debug is not available in wmparameter class void WiFiManagerParameter::setValue(const char *defaultValue, int length) { - if(!_id){ - // Serial.println("cannot set value of this parameter"); + if (!_id || length < 0) { return; } - // if(strlen(defaultValue) > length){ - // // Serial.println("defaultValue length mismatch"); - // // return false; //@todo bail - // } - - if(_length != length || _value == nullptr){ - _length = length; - if( _value != nullptr){ - delete[] _value; + if (_length != length || _value == nullptr) { + char* replacement = new (std::nothrow) char[static_cast(length) + 1]; + if (!replacement) { + return; } - _value = new char[_length + 1]; + + delete[] _value; + _value = replacement; + _length = length; } - memset(_value, 0, _length + 1); // explicit null + memset(_value, 0, static_cast(_length) + 1); - if (defaultValue != NULL) { + if (defaultValue != nullptr) { strncpy(_value, defaultValue, _length); } } diff --git a/library.json b/library.json index 1b018d4..561c943 100644 --- a/library.json +++ b/library.json @@ -1,6 +1,6 @@ { "name": "WiFiManager", - "version": "2.0.19", + "version": "3.0.0", "keywords": [ "wifi", "wi-fi", @@ -13,7 +13,7 @@ "wemos", "arduino" ], - "description": "WiFi Configuration manager with web configuration portal for ESP boards", + "description": "Maintained ESP8266/ESP32 WiFi configuration portal with asynchronous web support.", "authors": [ { "name": "tzapu", @@ -38,13 +38,10 @@ "espressif8266", "espressif32" ], - "dependencies": [ - { - "name": "ESP32Async/ESPAsyncWebServer", - "version": "^3.9.1" - }, - "https://github.com/alexhopeoconnor/DFTE.git" - ], + "dependencies": { + "ESP32Async/ESPAsyncWebServer": "3.9.1", + "DeviceFrameworkTemplateEngine": "https://github.com/alexhopeoconnor/DFTE.git#v1.0.1" + }, "build": { "includeDir": "lib/WiFiManager/include", "srcDir": "lib/WiFiManager/src" @@ -57,5 +54,7 @@ "README.md", "library.json" ] - } + }, + "$schema": "https://raw.githubusercontent.com/platformio/platformio-core/develop/platformio/assets/schema/library.json", + "homepage": "https://github.com/alexhopeoconnor/WiFiManager" } diff --git a/platformio.ini b/platformio.ini index d2096cc..407251b 100644 --- a/platformio.ini +++ b/platformio.ini @@ -15,11 +15,11 @@ build_flags = -DWM_LOG_LEVEL=5 -DUNIT_TEST lib_deps = - ESP32Async/ESPAsyncWebServer@^3.9.1 - symlink://../DFTE + ESP32Async/ESPAsyncWebServer@3.9.1 + DeviceFrameworkTemplateEngine=https://github.com/alexhopeoconnor/DFTE.git#v1.0.1 [env:esp32] -platform = espressif32 +platform = espressif32@6.13.0 board = esp32dev framework = arduino monitor_speed = 115200 @@ -32,8 +32,8 @@ build_flags = -DWM_LOG_LEVEL=5 -DUNIT_TEST lib_deps = - ESP32Async/ESPAsyncWebServer@^3.9.1 - symlink://../DFTE + ESP32Async/ESPAsyncWebServer@3.9.1 + DeviceFrameworkTemplateEngine=https://github.com/alexhopeoconnor/DFTE.git#v1.0.1 ; Optional: compile tests with DFTE logs bridged into WiFiManager::log (see README) [env:esp8266_dfte_log] diff --git a/scripts/prepare-release.sh b/scripts/prepare-release.sh new file mode 100755 index 0000000..e371f99 --- /dev/null +++ b/scripts/prepare-release.sh @@ -0,0 +1,40 @@ +#!/usr/bin/env bash +set -euo pipefail + +usage() { + echo "Usage: $0 vMAJOR.MINOR.PATCH [--tag]" + exit 2 +} + +tag="${1:-}" +[[ "$tag" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]] || usage +[[ "${2:-}" == "" || "${2:-}" == "--tag" ]] || usage + +root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +version="${tag#v}" +manifest_version="$(sed -n 's/.*"version": "\([^"]*\)".*/\1/p' "$root/library.json" | head -n 1)" + +if [[ "$manifest_version" != "$version" ]]; then + echo "library.json is $manifest_version; expected $version for $tag" >&2 + exit 1 +fi + +if [[ -f "$root/library.properties" ]]; then + properties_version="$(sed -n 's/^version=//p' "$root/library.properties" | head -n 1)" + if [[ "$properties_version" != "$version" ]]; then + echo "library.properties is $properties_version; expected $version for $tag" >&2 + exit 1 + fi +fi + +package_dir="$(mktemp -d)" +trap 'rm -rf "$package_dir"' EXIT +pio pkg pack "$root" --output "$package_dir/package.tar.gz" >/dev/null +echo "Validated PlatformIO package for $tag" + +if [[ "${2:-}" == "--tag" ]]; then + git -C "$root" diff --quiet + git -C "$root" diff --cached --quiet + git -C "$root" tag -a "$tag" -m "Release $tag" + echo "Created $tag. Push the branch and tag; GitHub Actions will publish the release." +fi