Public Access
The plan and decisions Q105 to Q116 (docs/milestones/S1.md). lib/net: strict IPv4 parsing, prefix and mask, and the checks a Fixed setting must pass, each refusal with its reason. A Saved Network is Automatic or Fixed (address/prefix and an optional gateway), kept with it in flash. Settings: two DNS servers (9.9.9.9, 1.1.1.1), "Always use my DNS", two NTP servers (pool.ntp.org, time.cloudflare.com). Host-tested: 383 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
90 lines
2.9 KiB
C++
90 lines
2.9 KiB
C++
#include "saved_networks.h"
|
|
|
|
namespace roro {
|
|
|
|
namespace {
|
|
std::string key(int i, const char* field) { return "net" + std::to_string(i) + "_" + field; }
|
|
} // namespace
|
|
|
|
void SavedNetworks::load() {
|
|
networks_.clear();
|
|
int32_t n = 0;
|
|
store_.getInt("net_count", n);
|
|
for (int i = 0; i < n && i < kMax; i++) {
|
|
SavedNetwork net;
|
|
if (!store_.getString(key(i, "ssid").c_str(), net.ssid)) break;
|
|
store_.getString(key(i, "pass").c_str(), net.password);
|
|
int32_t hidden = 0;
|
|
store_.getInt(key(i, "hid").c_str(), hidden);
|
|
net.hidden = hidden != 0;
|
|
std::string ip; // "address/prefix [gateway]", or empty for Automatic
|
|
net.fixed = store_.getString(key(i, "ip").c_str(), ip) && !ip.empty() && net::parseFixed(ip, net.ip).empty();
|
|
networks_.push_back(net);
|
|
}
|
|
}
|
|
|
|
const SavedNetwork* SavedNetworks::find(const std::string& ssid) const {
|
|
for (auto& n : networks_)
|
|
if (n.ssid == ssid) return &n;
|
|
return nullptr;
|
|
}
|
|
|
|
std::string SavedNetworks::add(const std::string& ssid, const std::string& password, bool hidden) {
|
|
if (ssid.empty() || ssid.size() > 32) return "Network name must be 1 to 32 bytes";
|
|
if (!password.empty() && (password.size() < 8 || password.size() > 63))
|
|
return "Password must be 8 to 63 characters (or empty for an open network)";
|
|
for (auto& n : networks_) {
|
|
if (n.ssid == ssid) {
|
|
n.password = password;
|
|
n.hidden = hidden;
|
|
save();
|
|
return "";
|
|
}
|
|
}
|
|
if (count() >= kMax) return "Already 8 saved networks: forget one first";
|
|
SavedNetwork added;
|
|
added.ssid = ssid;
|
|
added.password = password;
|
|
added.hidden = hidden;
|
|
networks_.push_back(added);
|
|
save();
|
|
return "";
|
|
}
|
|
|
|
std::string SavedNetworks::setIp(const std::string& ssid, const net::FixedIp* fixed) {
|
|
for (auto& n : networks_) {
|
|
if (n.ssid != ssid) continue;
|
|
if (fixed) {
|
|
std::string why = net::checkFixed(*fixed);
|
|
if (!why.empty()) return why;
|
|
n.ip = *fixed;
|
|
}
|
|
n.fixed = fixed != nullptr;
|
|
save();
|
|
return "";
|
|
}
|
|
return "Not a saved network";
|
|
}
|
|
|
|
void SavedNetworks::forget(const std::string& ssid) {
|
|
for (auto it = networks_.begin(); it != networks_.end(); ++it) {
|
|
if (it->ssid == ssid) {
|
|
networks_.erase(it);
|
|
save();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
void SavedNetworks::save() {
|
|
for (int i = 0; i < count(); i++) {
|
|
store_.putString(key(i, "ssid").c_str(), networks_[i].ssid);
|
|
store_.putString(key(i, "pass").c_str(), networks_[i].password);
|
|
store_.putInt(key(i, "hid").c_str(), networks_[i].hidden ? 1 : 0);
|
|
store_.putString(key(i, "ip").c_str(), networks_[i].fixed ? net::formatFixed(networks_[i].ip) : "");
|
|
}
|
|
store_.putInt("net_count", count());
|
|
}
|
|
|
|
} // namespace roro
|