mirror of
https://github.com/MarekZegare4/MeshCore-Solo.git
synced 2026-11-02 10:51:57 +00:00
fix(companion): scopes, room bot backlog, DM flood fallback, atomic trail saves
- Removing a scope moves the channels that used it to the default, not to unscoped. - The room bot ignores posts older than 5 minutes: the backlog a room replays after login no longer gets answers. - Adverts take the default scope from the scope list, as messages do; the legacy fields always mirror it, and CMD_SET_DEFAULT_FLOOD_SCOPE with a full list answers TABLE_FULL. - The last DM resend drops the direct path and floods. - /trail and /waypoints are written through a temp file. - An admin login timeout keeps the saved password. - History records read from the SD card are sanitised. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -336,7 +336,8 @@ public:
|
||||
if ((_state != LOGGING_IN && _state != WAITING) || (int32_t)(millis() - _deadline_ms) < 0) return;
|
||||
if (_state == LOGGING_IN) {
|
||||
the_mesh.cancelUiPendingLogin(_target.id.pub_key); // a late answer mustn't land elsewhere
|
||||
the_mesh.forgetRoomPassword(_target.id.pub_key);
|
||||
// No answer says nothing about the password (out of range, busy mesh):
|
||||
// keep a saved one, as RoomSessions does. Only a rejection forgets it.
|
||||
_state = IDLE;
|
||||
_outcome = LOGIN_TIMEOUT;
|
||||
return;
|
||||
|
||||
@@ -467,7 +467,8 @@ public:
|
||||
|
||||
// Periodic resend driver for outgoing DMs whose ACK deadline lapsed with no
|
||||
// ACK: resend with the next attempt# (reusing the original timestamp so the
|
||||
// recipient dedups) while resends remain, else mark it failed (✗).
|
||||
// recipient dedups) while resends remain, else mark it failed (✗). The last
|
||||
// resend floods.
|
||||
void tickDmResends() {
|
||||
uint32_t now = millis();
|
||||
for (int i = 0; i < _dm_hist_count; i++) {
|
||||
@@ -477,6 +478,13 @@ public:
|
||||
if (e.resends_left == 0) { failDm(e); continue; }
|
||||
ContactInfo c;
|
||||
if (!contactByPrefix(e.prefix, c)) { failDm(e); continue; }
|
||||
// Last attempt over a direct path that went unanswered every time: drop
|
||||
// the path and flood it, as the phone app does -- the route may be gone,
|
||||
// and the flood finds a new one (the ACK's return path re-learns it).
|
||||
if (e.resends_left == 1 && c.out_path_len != OUT_PATH_UNKNOWN &&
|
||||
the_mesh.resetContactPath(c.id.pub_key)) {
|
||||
c.out_path_len = OUT_PATH_UNKNOWN;
|
||||
}
|
||||
uint32_t expected_ack = 0, est_timeout = 0;
|
||||
uint8_t next_attempt = e.attempt + 1;
|
||||
if (the_mesh.sendMessage(c, e.msg_ts, next_attempt, e.text,
|
||||
|
||||
@@ -63,10 +63,7 @@ public:
|
||||
// trail can't wipe a previously saved one.
|
||||
void onShutdown() {
|
||||
if (!_prefs || !_prefs->trail_autosave_lowbatt || _trail.count() == 0) return;
|
||||
DataStore* ds = the_mesh.getDataStore();
|
||||
if (!ds) return;
|
||||
File f = ds->openWrite("/trail");
|
||||
if (f) { _trail.writeTo(f); f.close(); }
|
||||
save();
|
||||
}
|
||||
|
||||
TrailStore& store() { return _trail; }
|
||||
@@ -74,6 +71,7 @@ public:
|
||||
// ── Actions ────────────────────────────────────────────────────────────────
|
||||
enum FileResult : uint8_t { FILE_OK, FILE_NO_FS, FILE_MISSING, FILE_FAILED };
|
||||
static constexpr const char* TRAIL_FILE = "/trail";
|
||||
static constexpr const char* TRAIL_TMP = "/trail.tmp";
|
||||
|
||||
bool isActive() const { return _trail.isActive(); }
|
||||
void setActive(bool on) { _trail.setActive(on); }
|
||||
@@ -82,10 +80,14 @@ public:
|
||||
FileResult save() {
|
||||
DataStore* ds = the_mesh.getDataStore();
|
||||
if (!ds) return FILE_NO_FS;
|
||||
File f = ds->openWrite(TRAIL_FILE);
|
||||
// Through a temp file: a write cut short (flash full, power lost mid-save)
|
||||
// keeps the previous saved trail instead of leaving a torn /trail.
|
||||
File f = ds->openWrite(TRAIL_TMP);
|
||||
if (!f) return FILE_FAILED;
|
||||
bool ok = _trail.writeTo(f);
|
||||
f.close();
|
||||
if (ok) ok = ds->commitFile(TRAIL_TMP, TRAIL_FILE);
|
||||
else ds->removeFile(TRAIL_TMP);
|
||||
return ok ? FILE_OK : FILE_FAILED;
|
||||
}
|
||||
FileResult load() {
|
||||
|
||||
@@ -26,10 +26,12 @@ public:
|
||||
void save() {
|
||||
DataStore* ds = the_mesh.getDataStore();
|
||||
if (!ds) return;
|
||||
File f = ds->openWrite("/waypoints");
|
||||
File f = ds->openWrite("/waypoints.tmp"); // temp + rename: a cut-short write keeps the old file
|
||||
if (!f) return;
|
||||
_store.writeTo(f);
|
||||
bool ok = _store.writeTo(f);
|
||||
f.close();
|
||||
if (ok) ds->commitFile("/waypoints.tmp", "/waypoints");
|
||||
else ds->removeFile("/waypoints.tmp");
|
||||
}
|
||||
|
||||
// `label` cut to what a waypoint holds (WAYPOINT_LABEL_LEN - 1 bytes) at a
|
||||
|
||||
Reference in New Issue
Block a user