| 86 | } |
| 87 | |
| 88 | bool load(WebServerSettings& settings) { |
| 89 | std::map<std::string, std::string> map; |
| 90 | if (!file::loadPropertiesFile(SETTINGS_FILE, map)) { |
| 91 | return false; |
| 92 | } |
| 93 | |
| 94 | // Parse all settings from the map |
| 95 | auto wifi_enabled = map.find(KEY_WIFI_ENABLED); |
| 96 | auto wifi_mode = map.find(KEY_WIFI_MODE); |
| 97 | auto ap_ssid = map.find(KEY_AP_SSID); |
| 98 | auto ap_password = map.find(KEY_AP_PASSWORD); |
| 99 | auto ap_open_network = map.find(KEY_AP_OPEN_NETWORK); |
| 100 | auto ap_channel = map.find(KEY_AP_CHANNEL); |
| 101 | auto webserver_enabled = map.find(KEY_WEBSERVER_ENABLED); |
| 102 | auto webserver_port = map.find(KEY_WEBSERVER_PORT); |
| 103 | auto webserver_auth_enabled = map.find(KEY_WEBSERVER_AUTH_ENABLED); |
| 104 | auto webserver_username = map.find(KEY_WEBSERVER_USERNAME); |
| 105 | auto webserver_password = map.find(KEY_WEBSERVER_PASSWORD); |
| 106 | |
| 107 | // WiFi settings |
| 108 | settings.wifiEnabled = (wifi_enabled != map.end()) |
| 109 | ? (wifi_enabled->second == "1" || wifi_enabled->second == "true") |
| 110 | : false; // Default disabled |
| 111 | |
| 112 | settings.wifiMode = (wifi_mode != map.end() && wifi_mode->second == "1") |
| 113 | ? WiFiMode::AccessPoint |
| 114 | : WiFiMode::Station; |
| 115 | |
| 116 | auto parseInt = [](const std::string& value, int min, int max, int fallback) -> int { |
| 117 | int v = 0; |
| 118 | auto [ptr, ec] = std::from_chars(value.data(), value.data() + value.size(), v); |
| 119 | if (ec != std::errc{} || v < min || v > max) { |
| 120 | return fallback; |
| 121 | } |
| 122 | return v; |
| 123 | }; |
| 124 | |
| 125 | // AP mode settings |
| 126 | settings.apSsid = (ap_ssid != map.end() && !ap_ssid->second.empty()) |
| 127 | ? ap_ssid->second |
| 128 | : generateDefaultApSsid(); |
| 129 | settings.apPassword = (ap_password != map.end()) ? ap_password->second : ""; |
| 130 | settings.apOpenNetwork = (ap_open_network != map.end()) |
| 131 | ? (ap_open_network->second == "1" || ap_open_network->second == "true") |
| 132 | : false; |
| 133 | settings.apChannel = (ap_channel != map.end()) |
| 134 | ? static_cast<uint8_t>(parseInt(ap_channel->second, 1, 13, 1)) |
| 135 | : 1; |
| 136 | |
| 137 | // Security: If AP password is empty, generate a strong random password. |
| 138 | // Skip this if user explicitly wants an open network. |
| 139 | // Note: We only auto-generate for EMPTY passwords, not user-set ones. |
| 140 | if (!settings.apOpenNetwork && isEmptyCredential(settings.apPassword)) { |
| 141 | LOGGER.info("AP password is empty - generating secure random password"); |
| 142 | |
| 143 | // Generate 12-character random password (alphanumeric, ~71 bits of entropy) |
| 144 | // WPA2 requires 8-63 characters, so 12 is well within range |
| 145 | settings.apPassword = generateRandomCredential(12); |
no test coverage detected