Fix/wifimanager portal defaults (#2312)

* [SYS] Don't clobber MQTT/portal defaults with empty form fields

The /wifisave handler unconditionally strcpy'd every form value into the
persisted config, even when WiFiManager surfaced an empty string for a
field the caller omitted. A partial provisioning POST (e.g. only s/p)
silently wiped the compile-time MQTT defaults so the DUT joined WiFi but
never reached the broker.

Mirror the mqtt_pass sentinel guard with strlen() > 0 checks for the
remaining textual fields (server, port, user, topic, name, ota). The
boolean checkboxes and certificate fields already had non-empty guards.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [DOCS] Document /wifisave provisioning portal API

Add a reference page enumerating the HTTP form fields accepted by the
on-device configuration portal so companion apps and provisioning scripts
can integrate without reading firmware source. Documents the empty-field
ignore semantics introduced in the previous commit and the AP-teardown
response race that callers must tolerate.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [SYS] Also length-guard the MQTT password sentinel branch

The sentinel-only check (form-value != MQTT_PASS) was meant to protect
the stored password against accidental re-saves of the form default,
but it also persists an empty string from a partial / programmatic POST
that omits the `pass` field — empty differs from the sentinel, so the
broker password gets wiped. Add a strlen() > 0 precondition so partial
POSTs preserve the existing password.

Verified on the HIL bench (test_sys/test_wifimanager_portal_partial):
without this commit the DUT joined WiFi but never reached the broker;
with it, LWT-online lands within the reconnect budget.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* [DOCS] Reflect length-guard in /wifisave mqtt_pass semantics

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
FlorianandClaude Opus 4.7 authored and GitHub committed 2026-05-23 14:01:48 -05:00
1 parent 319c05af5c
commit 7904d49dbe
3 files changed
+90 -11

No files matched your search

+28 -11
View File
@@ -2356,17 +2356,30 @@ void setupWiFiManager() {
cnt_index = CNT_DEFAULT_INDEX;
# ifndef WIFIMNG_HIDE_MQTT_CONFIG
# if !MQTT_BROKER_MODE
strcpy(cnt_parameters_array[cnt_index].mqtt_server, custom_mqtt_server.getValue());
strcpy(cnt_parameters_array[cnt_index].mqtt_port, custom_mqtt_port.getValue());
strcpy(cnt_parameters_array[cnt_index].mqtt_user, custom_mqtt_user.getValue());
// Check if the MQTT password field contains the default value
if (strcmp(custom_mqtt_pass.getValue(), MQTT_PASS) != 0) {
// If it's not the default password, update the MQTT password
// Only persist non-empty values so a partial form submit (for example a
// programmatic POST of only s/p) cannot wipe compile-time defaults.
// mqtt_pass keeps its existing sentinel-based guard below.
if (strlen(custom_mqtt_server.getValue()) > 0) {
strcpy(cnt_parameters_array[cnt_index].mqtt_server, custom_mqtt_server.getValue());
}
if (strlen(custom_mqtt_port.getValue()) > 0) {
strcpy(cnt_parameters_array[cnt_index].mqtt_port, custom_mqtt_port.getValue());
}
if (strlen(custom_mqtt_user.getValue()) > 0) {
strcpy(cnt_parameters_array[cnt_index].mqtt_user, custom_mqtt_user.getValue());
}
// Persist the MQTT password only when the form supplied a non-empty value
// that also differs from the compile-time MQTT_PASS sentinel. An empty
// value (partial / programmatic POST) must not wipe the stored password.
if (strlen(custom_mqtt_pass.getValue()) > 0 &&
strcmp(custom_mqtt_pass.getValue(), MQTT_PASS) != 0) {
strcpy(cnt_parameters_array[cnt_index].mqtt_pass, custom_mqtt_pass.getValue());
}
strcpy(mqtt_topic, custom_mqtt_topic.getValue());
if (mqtt_topic[strlen(mqtt_topic) - 1] != '/' && strlen(mqtt_topic) < parameters_size) {
strcat(mqtt_topic, "/");
if (strlen(custom_mqtt_topic.getValue()) > 0) {
strcpy(mqtt_topic, custom_mqtt_topic.getValue());
if (mqtt_topic[strlen(mqtt_topic) - 1] != '/' && strlen(mqtt_topic) < parameters_size) {
strcat(mqtt_topic, "/");
}
}
cnt_parameters_array[cnt_index].isConnectionSecure = *custom_mqtt_secure.getValue();
@@ -2386,8 +2399,12 @@ void setupWiFiManager() {
}
# endif
# endif
strcpy(gateway_name, custom_gateway_name.getValue());
strcpy(ota_pass, custom_ota_pass.getValue());
if (strlen(custom_gateway_name.getValue()) > 0) {
strcpy(gateway_name, custom_gateway_name.getValue());
}
if (strlen(custom_ota_pass.getValue()) > 0) {
strcpy(ota_pass, custom_ota_pass.getValue());
}
# endif
# if !MQTT_BROKER_MODE