';
html += '
';
@@ -9107,8 +9127,6 @@ async function ramExplore(sp) {
const mem = { appCount: null, freeNV: null, freeV: null };
const isd = [], apps = [], elfs = [];
- ramOpModalBegin(t('Explore Card (all GP data)'));
-
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
@@ -9122,7 +9140,6 @@ async function ramExplore(sp) {
while (guard++ < 32) {
const apdu = ramGetStatusApdu(p1, p2);
ramShowProgress(label + ' P1=' + p1 + ' P2=' + p2 + '...');
- ramOpModalStatus(label + ' P1=' + p1 + ' P2=' + p2);
const res = await ramSendOta(apdu, Object.assign({}, sp, { cntr, spi2 }));
// Reuse the format detected for the first packet of this run
if (res.ram_format) sp.ram_format = res.ram_format;
@@ -9173,7 +9190,7 @@ async function ramExplore(sp) {
// Expanded pass: the fallback when the compact listing produced
// nothing (a card may reject P2.b2=0 - it is deprecated), and the
// optional details source (privileges, ELF/SD associations) behind the
- // "Query expanded registry details (E3)" checkbox.
+ // "Fetch expanded registry details" checkbox.
const wantDetails = !!(document.getElementById('ram-expanded-details') || {}).checked;
if (collector.length === before || wantDetails) {
ramShowProgress(label + ' P1=' + p1 + ' P2=02 (expanded)...');
@@ -9207,7 +9224,6 @@ async function ramExplore(sp) {
}
ramShowProgress(t('Card resources (GET DATA FF21)') + '...');
- ramOpModalStatus(t('Card resources (GET DATA FF21)'));
try {
const memRes = await ramSendOta('80CAFF2100', Object.assign({}, sp, { spi2: '01' }));
if (memRes.ram_format) sp.ram_format = memRes.ram_format;
@@ -9257,13 +9273,12 @@ async function ramExplore(sp) {
stepsEl.classList.remove('hidden');
stepsEl.textContent = errors.join('\n');
} else {
- resultEl.textContent = t('OK') + ' — ' + isd.length + ' ' + t('ISD') + ', ' + apps.length + ' ' + t('Apps') + ', ' + elfs.length + ' ' + t('ELFs') + (mem.freeNV ? ', ' + mem.freeNV + ' ' + t('free NV') : '') + (sp.ram_format ? ' — ' + t('RAM command format:') + ' ' + sp.ram_format : '');
+ resultEl.textContent = t('OK');
resultEl.classList.remove('hidden', 'text-red-600'); resultEl.classList.add('text-green-600');
}
_ramExplorerData = { mem: mem, isd: isd, apps: apps, elfs: elfs, fmt: sp.ram_format || '' };
ramRenderExplorer();
- ramOpModalFinish(resultEl.textContent, errors.length === 0, errors.join('\n'));
}
// GP DELETE (Card Spec v2.3.1 11.2, Tables 11-20/11-23): P1=00 (last/only
@@ -18067,7 +18082,9 @@ const LANG_RU = {
'KIc and KID are required': 'Укажите KIc и KID',
'KIc key and KID key are required': 'Укажите ключ KIc и ключ KID',
'RAM command format:': 'Формат команд RAM:',
- 'Query expanded registry details (E3)': 'Запрашивать расширенные данные реестра (E3)',
+ 'Fetch expanded registry details (load-file / SD associations)': 'Запрашивать расширенные данные реестра (связи load-файла и SD)',
+ 'Runs one extra expanded query per listing (P2=02, tag list 4F 9F70 C5 C4 CC CE EA). The compact listing carries AID/lifecycle/privileges/version only; the expanded pass adds the Load File AID, Security Domain AID and module lists. It costs extra secured packets and some cards reject it - the Explore already uses it automatically when the compact listing returns nothing.': 'Запускает дополнительный расширенный запрос для каждого списка (P2=02, список тегов 4F 9F70 C5 C4 CC CE EA). Компактный список содержит только AID/состояние/привилегии/версию; расширенный проход добавляет AID load-файла, AID Security Domain и списки модулей. Он расходует дополнительные защищённые пакеты, и не все карты его поддерживают — Explore и так применяет его автоматически, когда компактный список ничего не вернул.',
+ 'compact = the TS 102 226 5.2.1 command string (one C-APDU per secured packet, e.g. GET STATUS + GET RESPONSE); expanded = the AA/22 Command Scripting template (5.2.1.0). Detected per operation with a read-only GET STATUS probe; a card that rejects the deprecated compact P2.b2=0 form answers the expanded one.': 'compact = командная строка по TS 102 226 5.2.1 (один C-APDU на защищённый пакет, например GET STATUS + GET RESPONSE); expanded = шаблон Command Scripting AA/22 (5.2.1.0). Определяется для каждой операции пробным чтением GET STATUS; карта, отклоняющая устаревшую компактную форму P2.b2=0, отвечает в расширенной.',
'Card resources (GET DATA FF21)': 'Ресурсы карты (GET DATA FF21)',
'Extended Card Resources (GP tag FF21, ETSI TS 102 226 8.2.1.7.2): installed applications, free non-volatile and free volatile memory': 'Расширенные ресурсы карты (тег GP FF21, ETSI TS 102 226 8.2.1.7.2): число установленных приложений, свободная энергонезависимая и свободная энергозависимая память',
'No secured packet to send. Generate a secure packet first.': 'Нет защищённого пакета для отправки. Сначала соберите пакет.',
diff --git a/frontend/sw.js b/frontend/sw.js
index 62b1ceb..c0f774f 100644
--- a/frontend/sw.js
+++ b/frontend/sw.js
@@ -1,4 +1,4 @@
-const CACHE = 'simple-v317';
+const CACHE = 'simple-v318';
const URLS = [
'index.html',
'help.html',
diff --git a/frontend/tests/ram.test.js b/frontend/tests/ram.test.js
index cd809d4..f9b2155 100644
--- a/frontend/tests/ram.test.js
+++ b/frontend/tests/ram.test.js
@@ -22,7 +22,7 @@ function extractFunc(src, name) {
}
// Extract chain builder functions and dependencies
-const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml', 'ramStepLine', 'ramStepComponents', 'ramInstallFailHint', 'ramProbeParse', 'ramGetStatusApdu', 'ramDeleteApdu',
+const FNS = ['berLenStr', 'buildApdu', 'escHtml', 'esc', 'chainInit', 'chainRamBuildRowHex', 'ramFmtLifecycle', 'ramFmtPrivileges', 'ramRenderExploreHtml', 'ramStepLine', 'ramStepComponents', 'ramInstallFailHint', 'ramProbeParse', 'ramExpandedDetailsInit', 'ramExpandedDetailsChanged', 'ramGetStatusApdu', 'ramDeleteApdu',
'stkParamsBuild', 'ramRemoteSwOk', 'spPorAccepted', 'ramIncrementCntr', 'ramDeleteFromExplorer', 'ramListingSpi2', 'ramRemoveFromExplorer', 'ramHasInstance', 'ramExpandedQueryApdu',
'_parseRawElfEntry', '_parseRawAppEntry', 'ramParseElfStatus', 'ramParseAppStatus', 'parseTLV', '_parseE3Entry',
'ramCardIdxAfterRemove', 'ramClearResults', 'ramHideProgress', 'ramOpChanged', 'ramRender', 'ramApplyCard', 'ramExecute', 'decodePrivileges', 'ramActionBtn', 'ramCapToolkitMode', 'ramOpProgressText',
@@ -788,3 +788,49 @@ test('ramProbeParse reads AID=version lines', () => {
assert.ok(ramProbeParse('').error);
assert.ok(ramProbeParse(' \n# only comments\n').error);
});
+
+test('ramRenderExploreHtml counts apps and ELFs in the section headings', () => {
+ global.t = s => s;
+ const out = ramRenderExploreHtml(null,
+ [{ aid: 'A1', lifecycle: '07' }],
+ [{ aid: 'A2', lifecycle: '07' }],
+ [{ aid: 'E1', lifecycle: '01', moduleAids: [] }]);
+ delete global.t;
+ assert.ok(out.includes('Applications / Applet Instances (1)'), out);
+ assert.ok(out.includes('Executable Load Files (ELFs) / Packages (1)'), out);
+ // the ISD is a single occurrence (GP 11.4.2.1): no count in its heading
+ assert.ok(out.includes('ISD (Issuer Security Domain)'), out);
+ assert.ok(!out.includes('ISD (Issuer Security Domain) ('), out);
+});
+
+test('the RAM command format line explains compact vs expanded', () => {
+ global.t = s => s;
+ const out = ramRenderExploreHtml({ appCount: 1, freeNV: 1, freeV: 1 }, [], [], [], 'compact');
+ delete global.t;
+ assert.ok(out.includes('title="compact = the TS 102 226 5.2.1 command string'), out);
+});
+
+test('ramExplore runs inline, without the blocking modal', () => {
+ const src = extractFunc(html, 'ramExplore');
+ assert.ok(!/ramOpModal(Begin|Finish|Status)/.test(src),
+ 'Explore must not use the RAM modal (installs only)');
+});
+
+test('the expanded-details checkbox is a sticky preference', () => {
+ const els = fakeRamDocument(['ram-expanded-details']);
+ const store = {};
+ globalThis.localStorage = {
+ getItem: k => (k in store ? store[k] : null),
+ setItem: (k, v) => { store[k] = String(v); },
+ };
+ els['ram-expanded-details'].checked = false;
+ ramExpandedDetailsInit();
+ assert.strictEqual(els['ram-expanded-details'].checked, false); // default: off
+ store['simple_ram_expanded'] = '1';
+ ramExpandedDetailsInit();
+ assert.strictEqual(els['ram-expanded-details'].checked, true); // stored choice applied
+ els['ram-expanded-details'].checked = false;
+ ramExpandedDetailsChanged();
+ assert.strictEqual(store['simple_ram_expanded'], '0'); // ticking stores it
+ delete globalThis.localStorage;
+});
diff --git a/pyproject.toml b/pyproject.toml
index d082065..272b249 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
[project]
name = "pysim-simple-server"
-version = "3.6.44"
+version = "3.6.45"
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 bf60d2d..15ebcbb 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.44'
+VERSION = '3.6.45'
MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE