From 40d9a1d5c3e85486aea964c1446402a3be6ec05a Mon Sep 17 00:00:00 2001 From: cirakg Date: Tue, 20 Oct 2026 01:51:05 +0200 Subject: feat(import): parse otpauth URIs and unencrypted Aegis JSON in resumable steps Streaming JSON tokenizer with depth and length limits and native skipping of long strings; Aegis reader for database versions 1-3 with groups and encrypted-export detection; otpauth parser with ';' separators and Steam; import job with Replace mode, duplicate and replacement checks, a sealed multi-part buffer and a report. Google Authenticator exports deferred. Co-Authored-By: Claude Opus 5.5 --- source/import/ImportReport.mc | 71 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 71 insertions(+) create mode 100644 source/import/ImportReport.mc (limited to 'source/import/ImportReport.mc') diff --git a/source/import/ImportReport.mc b/source/import/ImportReport.mc new file mode 100644 index 0000000..b2a986a --- /dev/null +++ b/source/import/ImportReport.mc @@ -0,0 +1,71 @@ +// Author: cirakg & claude ai +// Summary: The outcome of one import: counts, per-item errors and skips, notes, and the texts for the status field and results screen. + +import Toybox.Lang; + +class ImportReport { + + // At most this many detail lines are kept (the results screen scrolls). + static const MAX_DETAILS = 40; + + var imported as Number = 0; + var pendingLocked as Number = 0; + var duplicates as Number = 0; + var errors as Number = 0; + var skipped as Number = 0; + var removed as Number = 0; + // Set when the whole import failed (not JSON, encrypted, storage full + // before anything was stored...). + var failure as String? = null; + // Set while a multi-part import waits for its next part. + var waitingChars as Number = -1; + var details as Array = [] as Array; + + function initialize() { + } + + function addDetail(line as String) as Void { + if (details.size() < MAX_DETAILS) { + details.add(line); + } else if (details.size() == MAX_DETAILS) { + details.add("…"); + } + } + + // One line for the phone's Import status field, for example + // "Imported 12 · 2 duplicates skipped · 1 error · 1 skipped". + function summary() as String { + var f = failure; + if (f != null) { + return "Not imported: " + f; + } + if (waitingChars >= 0) { + return "Waiting for more: received " + waitingChars + + " characters. Paste the next part into the import fields and turn 'Import now' on again."; + } + var text = "Imported " + imported; + if (pendingLocked > 0) { + text += " (" + pendingLocked + " locked, waiting for your PIN)"; + } + if (removed > 0) { + text += " · " + removed + " old imported removed"; + } + if (duplicates > 0) { + text += " · " + duplicates + (duplicates == 1 ? " duplicate skipped" : " duplicates skipped"); + } + if (errors > 0) { + text += " · " + errors + (errors == 1 ? " error" : " errors"); + } + if (skipped > 0) { + text += " · " + skipped + " skipped"; + } + return text; + } + + // The summary followed by the details, for the results screen. + function lines() as Array { + var out = [summary()] as Array; + out.addAll(details); + return out; + } +} -- cgit v1.2.3