mirror of
https://github.com/alexhopeoconnor/firmware.git
synced 2026-10-17 09:01:30 +10:00
Merge upstream develop into packet authentication policy
This commit is contained in:
+27
-15
@@ -177,6 +177,9 @@ bool NextHopRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p->to == NODENUM_BROADCAST_NO_LORA)
|
||||
return false;
|
||||
|
||||
// Allow rebroadcast if hop_limit > 0 OR if we're exhausting hops (which sets hop_limit = 0 but still needs one relay)
|
||||
if (!isToUs(p) && !isFromUs(p) && (p->hop_limit > 0 || exhaustHops)) {
|
||||
if (p->id != 0) {
|
||||
@@ -210,11 +213,10 @@ bool NextHopRouter::perhapsRebroadcast(const meshtastic_MeshPacket *p)
|
||||
}
|
||||
#endif
|
||||
|
||||
if (p->next_hop == NO_NEXT_HOP_PREFERENCE) {
|
||||
FloodingRouter::send(tosend);
|
||||
} else {
|
||||
NextHopRouter::send(tosend);
|
||||
}
|
||||
ErrorCode res =
|
||||
(p->next_hop == NO_NEXT_HOP_PREFERENCE) ? FloodingRouter::send(tosend) : NextHopRouter::send(tosend);
|
||||
if (res == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(tosend);
|
||||
|
||||
return true;
|
||||
}
|
||||
@@ -419,8 +421,10 @@ int32_t NextHopRouter::doRetransmissions()
|
||||
trafficManagementModule->clearNextHop(p.packet->to);
|
||||
}
|
||||
#endif
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet))
|
||||
FloodingRouter::send(copy);
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet)) {
|
||||
if (FloodingRouter::send(copy) == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(copy);
|
||||
}
|
||||
} else {
|
||||
#if NEXTHOP_EARLY_FLOOD_ON_UNVERIFIED
|
||||
// M4 (gated): if the route isn't proven healthy, don't spend a second directed
|
||||
@@ -434,22 +438,30 @@ int32_t NextHopRouter::doRetransmissions()
|
||||
meshtastic_NodeInfoLite *sentTo = nodeDB->getMeshNode(p.packet->to);
|
||||
if (sentTo)
|
||||
sentTo->next_hop = NO_NEXT_HOP_PREFERENCE;
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet))
|
||||
FloodingRouter::send(copy);
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet)) {
|
||||
if (FloodingRouter::send(copy) == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(copy);
|
||||
}
|
||||
} else {
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet))
|
||||
NextHopRouter::send(copy);
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet)) {
|
||||
if (NextHopRouter::send(copy) == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(copy);
|
||||
}
|
||||
}
|
||||
#else
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet))
|
||||
NextHopRouter::send(copy);
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet)) {
|
||||
if (NextHopRouter::send(copy) == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(copy);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
} else {
|
||||
// Note: we call the superclass version because we don't want to have our version of send() add a new
|
||||
// retransmission record
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet))
|
||||
FloodingRouter::send(copy);
|
||||
if (auto *copy = packetPool.allocCopy(*p.packet)) {
|
||||
if (FloodingRouter::send(copy) == ERRNO_SHOULD_RELEASE)
|
||||
packetPool.release(copy);
|
||||
}
|
||||
}
|
||||
|
||||
// Queue again
|
||||
|
||||
@@ -206,7 +206,11 @@ class NextHopRouter : public FloodingRouter
|
||||
*/
|
||||
std::optional<uint8_t> getNextHop(NodeNum to, uint8_t relay_node);
|
||||
|
||||
#ifdef PIO_UNIT_TESTING
|
||||
public: // expose perhapsRebroadcast to the test shim
|
||||
#else
|
||||
private:
|
||||
#endif
|
||||
/** Check if we should be rebroadcasting this packet if so, do so.
|
||||
* @return true if we did rebroadcast */
|
||||
bool perhapsRebroadcast(const meshtastic_MeshPacket *p) override;
|
||||
|
||||
+8
-1
@@ -3312,13 +3312,20 @@ void NodeDB::addFromContact(meshtastic_SharedContact contact)
|
||||
|
||||
/** Update user info and channel for this node based on received user data
|
||||
*/
|
||||
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex)
|
||||
bool NodeDB::updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex, bool xeddsaSigned)
|
||||
{
|
||||
meshtastic_NodeInfoLite *info = getOrCreateMeshNode(nodeId);
|
||||
if (!info) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Once a node has proven it signs, only a signed update may change its identity. The public-key guard
|
||||
// below is no help - an attacker can replay the victim's real (public) key. Our own record is exempt.
|
||||
if (nodeId != getNodeNum() && nodeInfoLiteHasXeddsaSigned(info) && !xeddsaSigned) {
|
||||
LOG_WARN("Refusing unsigned identity update for node 0x%08x that previously signed", nodeId);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI)
|
||||
if (p.public_key.size == 32 && nodeId != nodeDB->getNodeNum()) {
|
||||
printBytes("Incoming Pubkey: ", p.public_key.bytes, 32);
|
||||
|
||||
+3
-3
@@ -255,9 +255,9 @@ class NodeDB
|
||||
*/
|
||||
void updateTelemetry(uint32_t nodeId, const meshtastic_Telemetry &t, RxSource src = RX_SRC_RADIO);
|
||||
|
||||
/** Update user info and channel for this node based on received user data
|
||||
*/
|
||||
bool updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex = 0);
|
||||
/** Update user info and channel for this node based on received user data.
|
||||
* A known signer's identity is only learned when xeddsaSigned; defaults false so callers fail closed. */
|
||||
bool updateUser(uint32_t nodeId, meshtastic_User &p, uint8_t channelIndex = 0, bool xeddsaSigned = false);
|
||||
|
||||
/*
|
||||
* Sets a node either favorite or unfavorite. Returns true if the node ends
|
||||
|
||||
+65
-19
@@ -359,15 +359,6 @@ ErrorCode Router::sendLocal(meshtastic_MeshPacket *p, RxSource src)
|
||||
return send(p);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Send a packet on a suitable interface.
|
||||
*/
|
||||
ErrorCode Router::rawSend(meshtastic_MeshPacket *p)
|
||||
{
|
||||
assert(iface); // This should have been detected already in sendLocal (or we just received a packet from outside)
|
||||
return iface->send(p);
|
||||
}
|
||||
|
||||
/**
|
||||
* Send a packet on a suitable interface. This routine will
|
||||
* later free() the packet to pool. This routine is not allowed to stall.
|
||||
@@ -531,6 +522,62 @@ void Router::sniffReceived(const meshtastic_MeshPacket *p, const meshtastic_Rout
|
||||
}
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI) && !(MESHTASTIC_EXCLUDE_XEDDSA)
|
||||
/** Size a decoded Data as the sender's signedDataFits() gate would have, with padding stripped:
|
||||
* unknown fields inside Data.payload survive in payload.size and would otherwise let a forger
|
||||
* inflate an unsigned broadcast past the signable budget. Returns false only if sizing failed.
|
||||
* Sizing only what this build's schema decodes, so a signable type that later grows needs its
|
||||
* legitimate maximum re-checked against the budget or honest unsigned broadcasts get dropped. */
|
||||
static bool canonicalSignableSize(meshtastic_Data *d, size_t *size)
|
||||
{
|
||||
const pb_msgdesc_t *fields = nullptr;
|
||||
switch (d->portnum) {
|
||||
case meshtastic_PortNum_POSITION_APP:
|
||||
fields = &meshtastic_Position_msg;
|
||||
break;
|
||||
case meshtastic_PortNum_TELEMETRY_APP:
|
||||
fields = &meshtastic_Telemetry_msg;
|
||||
break;
|
||||
case meshtastic_PortNum_WAYPOINT_APP:
|
||||
fields = &meshtastic_Waypoint_msg;
|
||||
break;
|
||||
case meshtastic_PortNum_NODEINFO_APP:
|
||||
fields = &meshtastic_User_msg;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (fields) {
|
||||
// Scratch kept off the stack: these decoded structs are large for the smaller MCU targets.
|
||||
// Safe as file-static state because both callers of checkXeddsaReceivePolicy hold cryptLock.
|
||||
static union {
|
||||
// cppcheck-suppress unusedStructMember ; written by pb_decode through &inner
|
||||
meshtastic_Position position;
|
||||
// cppcheck-suppress unusedStructMember ; written by pb_decode through &inner
|
||||
meshtastic_Telemetry telemetry;
|
||||
// cppcheck-suppress unusedStructMember ; written by pb_decode through &inner
|
||||
meshtastic_Waypoint waypoint;
|
||||
// cppcheck-suppress unusedStructMember ; written by pb_decode through &inner
|
||||
meshtastic_User user;
|
||||
} inner;
|
||||
|
||||
memset(&inner, 0, sizeof(inner));
|
||||
size_t canonicalPayload;
|
||||
if (pb_decode_from_bytes(d->payload.bytes, d->payload.size, fields, &inner) &&
|
||||
pb_get_encoded_size(&canonicalPayload, fields, &inner) && canonicalPayload <= d->payload.size) {
|
||||
// Only the length matters when sizing a bytes field, so swap it in place instead of
|
||||
// copying the whole Data; restored below because modules still need the real payload.
|
||||
const pb_size_t prevSize = d->payload.size;
|
||||
d->payload.size = (pb_size_t)canonicalPayload;
|
||||
const bool sized = pb_get_encoded_size(size, &meshtastic_Data_msg, d);
|
||||
d->payload.size = prevSize;
|
||||
return sized;
|
||||
}
|
||||
}
|
||||
|
||||
return pb_get_encoded_size(size, &meshtastic_Data_msg, d);
|
||||
}
|
||||
|
||||
enum class NodeInfoBootstrapResult { NOT_APPLICABLE, VERIFIED, INVALID };
|
||||
|
||||
static NodeInfoBootstrapResult verifyFirstContactNodeInfo(meshtastic_MeshPacket *p)
|
||||
@@ -557,7 +604,7 @@ static NodeInfoBootstrapResult verifyFirstContactNodeInfo(meshtastic_MeshPacket
|
||||
return NodeInfoBootstrapResult::VERIFIED;
|
||||
}
|
||||
|
||||
bool checkXeddsaReceivePolicy(meshtastic_MeshPacket *p, size_t encodedDataSize)
|
||||
bool checkXeddsaReceivePolicy(meshtastic_MeshPacket *p)
|
||||
{
|
||||
const auto policy = config.security.packet_signature_policy;
|
||||
const bool strict = policy == meshtastic_Config_SecurityConfig_PacketSignaturePolicy_PACKET_SIGNATURE_POLICY_STRICT;
|
||||
@@ -617,15 +664,14 @@ bool checkXeddsaReceivePolicy(meshtastic_MeshPacket *p, size_t encodedDataSize)
|
||||
return true;
|
||||
|
||||
// In Balanced, preserve legacy unsigned-unicast compatibility and only reject a signable
|
||||
// unsigned broadcast from a known signer. encodedDataSize is
|
||||
// the size of the encoded Data exactly as the sender built it (or 0 to size p->decoded
|
||||
// canonically); with no signature field present it is the unsigned base, and adding
|
||||
// XEDDSA_SIGNATURE_FIELD_BYTES mirrors the sender-side signedDataFits() gate per packet,
|
||||
// whatever fields the Data carried. Oversized broadcasts remain compatible.
|
||||
// unsigned broadcast from a known signer. Canonical sizing removes unknown protobuf
|
||||
// fields before mirroring the sender-side signedDataFits() gate. Oversized broadcasts
|
||||
// remain compatible.
|
||||
if (nodeDB->hasSeenXeddsaSigner(p->from) && isBroadcast(p->to)) {
|
||||
if (encodedDataSize == 0 && !pb_get_encoded_size(&encodedDataSize, &meshtastic_Data_msg, &p->decoded))
|
||||
size_t canonicalSize;
|
||||
if (!canonicalSignableSize(&p->decoded, &canonicalSize))
|
||||
return true; // can't size it; never drop on a sizing failure
|
||||
if (encodedDataSize + XEDDSA_SIGNATURE_FIELD_BYTES + MESHTASTIC_HEADER_LENGTH <= MAX_LORA_PAYLOAD_LEN) {
|
||||
if (canonicalSize + XEDDSA_SIGNATURE_FIELD_BYTES + MESHTASTIC_HEADER_LENGTH <= MAX_LORA_PAYLOAD_LEN) {
|
||||
LOG_WARN("Dropping unsigned broadcast from 0x%08x that previously signed", p->from);
|
||||
return false;
|
||||
}
|
||||
@@ -813,8 +859,8 @@ DecodeState perhapsDecode(meshtastic_MeshPacket *p)
|
||||
p->channel = chIndex; // change to store the index instead of the hash
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI) && !(MESHTASTIC_EXCLUDE_XEDDSA)
|
||||
// Use the encoded Data size before merging local-only bitfield state.
|
||||
if (!checkXeddsaReceivePolicy(p, rawSize))
|
||||
// Run before merging local-only bitfield state into the decoded Data.
|
||||
if (!checkXeddsaReceivePolicy(p))
|
||||
return DecodeState::DECODE_POLICY_REJECT;
|
||||
#endif
|
||||
|
||||
|
||||
+3
-4
@@ -94,7 +94,6 @@ class Router : protected concurrency::OSThread, protected PacketHistory
|
||||
* NOTE: This method will free the provided packet (even if we return an error code)
|
||||
*/
|
||||
virtual ErrorCode send(meshtastic_MeshPacket *p);
|
||||
virtual ErrorCode rawSend(meshtastic_MeshPacket *p);
|
||||
|
||||
/* Statistics for the amount of duplicate received packets and the amount of times we cancel a relay because someone did it
|
||||
before us */
|
||||
@@ -187,9 +186,9 @@ void resetRoutingAuthEvaluationCount();
|
||||
meshtastic_Routing_Error perhapsEncode(meshtastic_MeshPacket *p);
|
||||
|
||||
#if !(MESHTASTIC_EXCLUDE_PKI) && !(MESHTASTIC_EXCLUDE_XEDDSA)
|
||||
/** Enforce the configured XEdDSA receive policy; zero encodedDataSize derives it canonically.
|
||||
* The caller must hold cryptLock. Returns false when the packet must be dropped. */
|
||||
bool checkXeddsaReceivePolicy(meshtastic_MeshPacket *p, size_t encodedDataSize = 0);
|
||||
/** Enforce the configured XEdDSA receive policy. The caller must hold cryptLock.
|
||||
* Returns false when the packet must be dropped. */
|
||||
bool checkXeddsaReceivePolicy(meshtastic_MeshPacket *p);
|
||||
#endif
|
||||
|
||||
extern Router *router;
|
||||
|
||||
@@ -74,15 +74,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
||||
ResourceNode *nodeAPIv1FromRadioOptions = new ResourceNode("/api/v1/fromradio", "OPTIONS", &handleAPIv1FromRadio);
|
||||
ResourceNode *nodeAPIv1FromRadio = new ResourceNode("/api/v1/fromradio", "GET", &handleAPIv1FromRadio);
|
||||
|
||||
// ResourceNode *nodeHotspotApple = new ResourceNode("/hotspot-detect.html", "GET", &handleHotspot);
|
||||
// ResourceNode *nodeHotspotAndroid = new ResourceNode("/generate_204", "GET", &handleHotspot);
|
||||
|
||||
ResourceNode *nodeAdmin = new ResourceNode("/admin", "GET", &handleAdmin);
|
||||
// ResourceNode *nodeAdminSettings = new ResourceNode("/admin/settings", "GET", &handleAdminSettings);
|
||||
// ResourceNode *nodeAdminSettingsApply = new ResourceNode("/admin/settings/apply", "POST", &handleAdminSettingsApply);
|
||||
// ResourceNode *nodeAdminFs = new ResourceNode("/admin/fs", "GET", &handleFs);
|
||||
// ResourceNode *nodeUpdateFs = new ResourceNode("/admin/fs/update", "POST", &handleUpdateFs);
|
||||
// ResourceNode *nodeDeleteFs = new ResourceNode("/admin/fs/delete", "GET", &handleDeleteFsContent);
|
||||
|
||||
ResourceNode *nodeRestart = new ResourceNode("/restart", "POST", &handleRestart);
|
||||
ResourceNode *nodeFormUpload = new ResourceNode("/upload", "POST", &handleFormUpload);
|
||||
@@ -100,8 +92,6 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
||||
secureServer->registerNode(nodeAPIv1ToRadio);
|
||||
secureServer->registerNode(nodeAPIv1FromRadioOptions);
|
||||
secureServer->registerNode(nodeAPIv1FromRadio);
|
||||
// secureServer->registerNode(nodeHotspotApple);
|
||||
// secureServer->registerNode(nodeHotspotAndroid);
|
||||
secureServer->registerNode(nodeRestart);
|
||||
secureServer->registerNode(nodeFormUpload);
|
||||
secureServer->registerNode(nodeJsonScanNetworks);
|
||||
@@ -109,12 +99,7 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
||||
secureServer->registerNode(nodeJsonDelete);
|
||||
secureServer->registerNode(nodeJsonReport);
|
||||
secureServer->registerNode(nodeJsonNodes);
|
||||
// secureServer->registerNode(nodeUpdateFs);
|
||||
// secureServer->registerNode(nodeDeleteFs);
|
||||
secureServer->registerNode(nodeAdmin);
|
||||
// secureServer->registerNode(nodeAdminFs);
|
||||
// secureServer->registerNode(nodeAdminSettings);
|
||||
// secureServer->registerNode(nodeAdminSettingsApply);
|
||||
secureServer->registerNode(nodeRoot); // This has to be last
|
||||
|
||||
// Insecure nodes
|
||||
@@ -122,20 +107,13 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer)
|
||||
insecureServer->registerNode(nodeAPIv1ToRadio);
|
||||
insecureServer->registerNode(nodeAPIv1FromRadioOptions);
|
||||
insecureServer->registerNode(nodeAPIv1FromRadio);
|
||||
// insecureServer->registerNode(nodeHotspotApple);
|
||||
// insecureServer->registerNode(nodeHotspotAndroid);
|
||||
insecureServer->registerNode(nodeRestart);
|
||||
insecureServer->registerNode(nodeFormUpload);
|
||||
insecureServer->registerNode(nodeJsonScanNetworks);
|
||||
insecureServer->registerNode(nodeJsonFsBrowseStatic);
|
||||
insecureServer->registerNode(nodeJsonDelete);
|
||||
insecureServer->registerNode(nodeJsonReport);
|
||||
// insecureServer->registerNode(nodeUpdateFs);
|
||||
// insecureServer->registerNode(nodeDeleteFs);
|
||||
insecureServer->registerNode(nodeAdmin);
|
||||
// insecureServer->registerNode(nodeAdminFs);
|
||||
// insecureServer->registerNode(nodeAdminSettings);
|
||||
// insecureServer->registerNode(nodeAdminSettingsApply);
|
||||
insecureServer->registerNode(nodeRoot); // This has to be last
|
||||
}
|
||||
|
||||
@@ -230,36 +208,6 @@ void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res)
|
||||
LOG_DEBUG("webAPI handleAPIv1ToRadio");
|
||||
}
|
||||
|
||||
void htmlDeleteDir(const char *dirname)
|
||||
{
|
||||
|
||||
File root = FSCom.open(dirname);
|
||||
if (!root) {
|
||||
return;
|
||||
}
|
||||
if (!root.isDirectory()) {
|
||||
return;
|
||||
}
|
||||
|
||||
File file = root.openNextFile();
|
||||
while (file) {
|
||||
if (file.isDirectory() && !String(file.name()).endsWith(".")) {
|
||||
htmlDeleteDir(file.name());
|
||||
file.flush();
|
||||
file.close();
|
||||
} else {
|
||||
String fileName = String(file.name());
|
||||
file.flush();
|
||||
file.close();
|
||||
LOG_DEBUG(" %s", fileName.c_str());
|
||||
FSCom.remove(fileName);
|
||||
}
|
||||
file = root.openNextFile();
|
||||
}
|
||||
root.flush();
|
||||
root.close();
|
||||
}
|
||||
|
||||
// Escape a string into a JSON double-quoted literal. Matches the previous
|
||||
// SimpleJSON StringifyString behavior (0x00-0x1F and 0x7F -> \u00xx lowercase,
|
||||
// escapes " \ / \b \f \n \r \t, UTF-8 passes through unchanged).
|
||||
@@ -858,45 +806,6 @@ void handleNodes(HTTPRequest *req, HTTPResponse *res)
|
||||
res->print(out.c_str());
|
||||
}
|
||||
|
||||
/*
|
||||
This supports the Apple Captive Network Assistant (CNA) Portal
|
||||
*/
|
||||
void handleHotspot(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
LOG_INFO("Hotspot Request");
|
||||
|
||||
/*
|
||||
If we don't do a redirect, be sure to return a "Success" message
|
||||
otherwise iOS will have trouble detecting that the connection to the SoftAP worked.
|
||||
*/
|
||||
|
||||
// Status code is 200 OK by default.
|
||||
// We want to deliver a simple HTML page, so we send a corresponding content type:
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
res->setHeader("Access-Control-Allow-Origin", "*");
|
||||
res->setHeader("Access-Control-Allow-Methods", "GET");
|
||||
|
||||
// res->println("<!DOCTYPE html>");
|
||||
res->println("<meta http-equiv=\"refresh\" content=\"0;url=/\" />");
|
||||
}
|
||||
|
||||
void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
res->setHeader("Access-Control-Allow-Origin", "*");
|
||||
res->setHeader("Access-Control-Allow-Methods", "GET");
|
||||
|
||||
res->println("<h1>Meshtastic</h1>");
|
||||
res->println("Delete Content in /static/*");
|
||||
|
||||
LOG_INFO("Delete files from /static/* : ");
|
||||
|
||||
concurrency::LockGuard g(spiLock);
|
||||
htmlDeleteDir("/static");
|
||||
|
||||
res->println("<p><hr><p><a href=/admin>Back to admin</a>");
|
||||
}
|
||||
|
||||
void handleAdmin(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
@@ -909,52 +818,6 @@ void handleAdmin(HTTPRequest *req, HTTPResponse *res)
|
||||
res->println("<a href=/json/report>Device Report</a><br>");
|
||||
}
|
||||
|
||||
void handleAdminSettings(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
res->setHeader("Access-Control-Allow-Origin", "*");
|
||||
res->setHeader("Access-Control-Allow-Methods", "GET");
|
||||
|
||||
res->println("<h1>Meshtastic</h1>");
|
||||
res->println("This isn't done.");
|
||||
res->println("<form action=/admin/settings/apply method=post>");
|
||||
res->println("<table border=1>");
|
||||
res->println("<tr><td>Set?</td><td>Setting</td><td>current value</td><td>new value</td></tr>");
|
||||
res->println("<tr><td><input type=checkbox></td><td>WiFi SSID</td><td>false</td><td><input type=radio></td></tr>");
|
||||
res->println("<tr><td><input type=checkbox></td><td>WiFi Password</td><td>false</td><td><input type=radio></td></tr>");
|
||||
res->println(
|
||||
"<tr><td><input type=checkbox></td><td>Smart Position Update</td><td>false</td><td><input type=radio></td></tr>");
|
||||
res->println("</table>");
|
||||
res->println("<table>");
|
||||
res->println("<input type=submit value=Apply New Settings>");
|
||||
res->println("<form>");
|
||||
res->println("<p><hr><p><a href=/admin>Back to admin</a>");
|
||||
}
|
||||
|
||||
void handleAdminSettingsApply(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
res->setHeader("Access-Control-Allow-Origin", "*");
|
||||
res->setHeader("Access-Control-Allow-Methods", "POST");
|
||||
res->println("<h1>Meshtastic</h1>");
|
||||
res->println(
|
||||
"<html><head><meta http-equiv=\"refresh\" content=\"1;url=/admin/settings\" /><title>Settings Applied. </title>");
|
||||
|
||||
res->println("Settings Applied. Please wait.");
|
||||
}
|
||||
|
||||
void handleFs(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
res->setHeader("Access-Control-Allow-Origin", "*");
|
||||
res->setHeader("Access-Control-Allow-Methods", "GET");
|
||||
|
||||
res->println("<h1>Meshtastic</h1>");
|
||||
res->println("<a href=/admin/fs/delete>Delete Web Content</a><p><form action=/admin/fs/update "
|
||||
"method=post><input type=submit value=UPDATE_WEB_CONTENT></form>Be patient!");
|
||||
res->println("<p><hr><p><a href=/admin>Back to admin</a>");
|
||||
}
|
||||
|
||||
void handleRestart(HTTPRequest *req, HTTPResponse *res)
|
||||
{
|
||||
res->setHeader("Content-Type", "text/html");
|
||||
|
||||
@@ -4,7 +4,6 @@ void registerHandlers(HTTPServer *insecureServer, HTTPSServer *secureServer);
|
||||
// Declare some handler functions for the various URLs on the server
|
||||
void handleAPIv1FromRadio(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleAPIv1ToRadio(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleHotspot(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleStatic(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleRestart(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleFormUpload(HTTPRequest *req, HTTPResponse *res);
|
||||
@@ -13,12 +12,7 @@ void handleFsBrowseStatic(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleFsDeleteStatic(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleReport(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleNodes(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleUpdateFs(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleDeleteFsContent(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleFs(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleAdmin(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleAdminSettings(HTTPRequest *req, HTTPResponse *res);
|
||||
void handleAdminSettingsApply(HTTPRequest *req, HTTPResponse *res);
|
||||
|
||||
// Interface to the PhoneAPI to access the protobufs with messages
|
||||
class HttpAPI : public PhoneAPI
|
||||
|
||||
@@ -1,14 +1,3 @@
|
||||
#include "mesh/http/ContentHelper.h"
|
||||
// #include <Arduino.h>
|
||||
// #include "main.h"
|
||||
|
||||
void replaceAll(std::string &str, const std::string &from, const std::string &to)
|
||||
{
|
||||
if (from.empty())
|
||||
return;
|
||||
size_t start_pos = 0;
|
||||
while ((start_pos = str.find(from, start_pos)) != std::string::npos) {
|
||||
str.replace(start_pos, from.length(), to);
|
||||
start_pos += to.length(); // In case 'to' contains 'from', like replacing 'x' with 'yx'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,5 +3,3 @@
|
||||
#include <string>
|
||||
|
||||
#define BoolToString(x) ((x) ? "true" : "false")
|
||||
|
||||
void replaceAll(std::string &str, const std::string &from, const std::string &to);
|
||||
|
||||
Reference in New Issue
Block a user