From f65a5124407ec8b312840fc9f6372252b7b2aa7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BD=D1=82=D0=BE=D0=BD=20=D0=A2=D1=80=D0=BE=D1=88?= =?UTF-8?q?=D0=B8=D0=BD?= Date: Mon, 28 Sep 2026 22:56:26 +0300 Subject: [PATCH] fix: query the ELF listing with P1=10 first, fall back to P1=20 (v3.6.26) GP 11.4.2.1 makes '10' the superset of '20' (Executable Load Files with their modules), yet the Explore queried '20' plus a separate '10' modules pass and merged the two. A live card answered 6A86 (incorrect P1-P2) to '20' while '10' worked (2026-09-28) - all four P1 values "shall be supported", so this is the guarded-fallback case, not a vendor branch. - Explore now queries '10' (with module parsing) and falls back to '20' (no module lists) only when it yields nothing; a successful fallback drops the primary failure from the result, and the primary error surfaces only when both fail. - The separate P1=10 "modules" pass and ramMergeElfData are gone. 656 frontend / 504 Python green; version 3.6.26; sw simple-v299. --- frontend/index.html | 42 +++++++++++++++-------------------- frontend/sw.js | 2 +- pyproject.toml | 2 +- pysim_simple_server/server.py | 2 +- 4 files changed, 21 insertions(+), 27 deletions(-) diff --git a/frontend/index.html b/frontend/index.html index 0b4be14..294821b 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1713,7 +1713,7 @@ // ===== Version ===== // Single source of truth for the PWA version: shown in the header and used // by the server version check in pysimConnect(). -const SIMPLE_VERSION = '3.6.25'; +const SIMPLE_VERSION = '3.6.26'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== Tab switching ===== @@ -8838,22 +8838,6 @@ function ramFmtPrivileges(hex) { return privs.length ? privs.join(', ') : t('(none)'); } -// Merge ELF-module entries (P1=10, carry module AIDs) into ELF entries (P1=20). -// With TLV format (P2=02), module AIDs come from tag '84' inside E3 templates. -// With raw format (P2=00), module AIDs are not available in the response. -function ramMergeElfData(elfs, modules) { - const byAid = {}; - elfs.forEach(e => { if (e.aid) byAid[e.aid] = e; }); - modules.forEach(m => { - if (m.aid && byAid[m.aid]) { - if (m.moduleAids && m.moduleAids.length) byAid[m.aid].moduleAids = m.moduleAids; - } else if (m.aid) { - elfs.push(m); - } - }); - return elfs; -} - // Render the full explorer result as structured HTML function ramRenderExploreHtml(mem, isd, apps, elfs, fmt) { let html = ''; @@ -8957,9 +8941,9 @@ async function ramExplore(sp) { let cntr = sp.cntr; const errors = []; const mem = { appCount: null, freeNV: null, freeV: null }; - const isd = [], apps = [], elfs = [], modules = []; + const isd = [], apps = [], elfs = []; - async function paginate(p1, collector, parser, label) { + async function paginate(p1, collector, parser, label, quiet) { // Compact listing format (GP 11.4.2.2: P2.b2=0) with the chained GET // RESPONSE - see ramGetStatusApdu(). The card's SCP80 layer runs both // commands, so the PoR carries the listing; ramListingSpi2() picks the @@ -9051,7 +9035,8 @@ async function ramExplore(sp) { } } // The compact error only surfaces when the expanded pass did not rescue it - if (collector.length === before && compactError) errors.push(compactError); + // (`quiet` lets a fallback query decide whether to report it). + if (collector.length === before && compactError && !quiet) errors.push(compactError); } ramShowProgress(t('Memory (GET DATA FF21)') + '...'); @@ -9080,10 +9065,19 @@ async function ramExplore(sp) { // 80=ISD, 40=Applications, 20=Executable Load Files (ELFs), 10=ELF+modules await paginate('80', isd, ramParseAppStatus, t('ISD')); await paginate('40', apps, ramParseAppStatus, t('Apps')); - await paginate('20', elfs, ramParseElfStatus, t('ELFs')); - await paginate('10', modules, (hex) => ramParseElfStatus(hex, true), t('ELF Modules')); - - ramMergeElfData(elfs, modules); + // GP 11.4.2.1: '10' returns the ELFs *with* their modules - the superset of + // '20' (ELFs only). All four P1 values shall be supported, but cards + // deviate: the live card answered 6A86 (incorrect P1-P2) to '20' while + // '10' works (2026-09-28). Query '10' first; fall back to '20' (no + // module lists) only when it yields nothing, and report the primary + // failure only when both fail. + const elfErrorsBefore = errors.length; + await paginate('10', elfs, (hex) => ramParseElfStatus(hex, true), t('ELFs')); + if (!elfs.length) { + const elfFailure = errors.splice(elfErrorsBefore); + await paginate('20', elfs, ramParseElfStatus, t('ELFs'), true); + if (!elfs.length) errors.push(...elfFailure); + } // Only write the counter when a packet was actually accepted: a run whose // start value was stale must never lower the preset counter. if (cntr !== sp.cntr) ramSaveCntr(cntr); diff --git a/frontend/sw.js b/frontend/sw.js index fc42070..9891839 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v298'; +const CACHE = 'simple-v299'; const URLS = [ 'index.html', 'help.html', diff --git a/pyproject.toml b/pyproject.toml index 7bb86ae..c6fc4db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "3.6.25" +version = "3.6.26" description = "HTTP REST server wrapping pysim for the SIMple PWA" requires-python = ">=3.8" # pysim is a git-only dependency installed explicitly by setup.bat/setup.sh. diff --git a/pysim_simple_server/server.py b/pysim_simple_server/server.py index c457301..5464964 100644 --- a/pysim_simple_server/server.py +++ b/pysim_simple_server/server.py @@ -31,7 +31,7 @@ from osmocom.tlv import BER_TLV_IE from osmocom.utils import rpad -VERSION = '3.6.25' +VERSION = '3.6.26' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE