diff --git a/frontend/index.html b/frontend/index.html index f087bf5..1c76954 100644 --- a/frontend/index.html +++ b/frontend/index.html @@ -1413,7 +1413,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 = '2.7.16'; +const SIMPLE_VERSION = '2.7.17'; document.getElementById('app-version').textContent = 'v' + SIMPLE_VERSION; // ===== Tab switching ===== @@ -8303,15 +8303,26 @@ function pysimProactiveSeqChanged(seq) { _pysimProactiveSeq = seq; return true; } +// TS 102 223 9.4 command types (values equal the proactive command template +// tags); the log falls back to 'Cmd 0xNN' for anything not listed. const CMD_NAMES = { - '03': 'POLL INTERVAL', '05': 'SET UP EVENT LIST', - '13': 'SEND SHORT MESSAGE', '20': 'PLAY TONE', - '21': 'DISPLAY TEXT', '22': 'GET INKEY', '23': 'GET INPUT', - '24': 'SELECT ITEM', '25': 'SET UP MENU', - '26': 'PROVIDE LOCAL INFORMATION', '27': 'TIMER MANAGEMENT', - '15': 'LAUNCH BROWSER', '70': 'ACTIVATE', - '40': 'OPEN CHANNEL', '41': 'CLOSE CHANNEL', - '42': 'RECEIVE DATA', '43': 'SEND DATA', '44': 'GET CHANNEL STATUS', + '01': 'REFRESH', '02': 'MORE TIME', '03': 'POLL INTERVAL', '04': 'POLLING OFF', + '05': 'SET UP EVENT LIST', '10': 'SET UP CALL', '11': 'SEND SS', '12': 'SEND USSD', + '13': 'SEND SHORT MESSAGE', '14': 'SEND DTMF', '15': 'LAUNCH BROWSER', + '16': 'GEOGRAPHICAL LOCATION REQUEST', + '20': 'PLAY TONE', '21': 'DISPLAY TEXT', '22': 'GET INKEY', '23': 'GET INPUT', + '24': 'SELECT ITEM', '25': 'SET UP MENU', '26': 'PROVIDE LOCAL INFORMATION', + '27': 'TIMER MANAGEMENT', '28': 'SET UP IDLE MODE TEXT', + '30': 'PERFORM CARD APDU', '31': 'POWER ON CARD', '32': 'POWER OFF CARD', + '33': 'GET READER STATUS', '34': 'RUN AT COMMAND', '35': 'LANGUAGE NOTIFICATION', + '40': 'OPEN CHANNEL', '41': 'CLOSE CHANNEL', '42': 'RECEIVE DATA', '43': 'SEND DATA', + '44': 'GET CHANNEL STATUS', '45': 'SERVICE SEARCH', '46': 'GET SERVICE INFORMATION', + '47': 'DECLARE SERVICE', '50': 'SET FRAMES', '51': 'GET FRAMES STATUS', + '60': 'RETRIEVE MULTIMEDIA MESSAGE', '61': 'SUBMIT MULTIMEDIA MESSAGE', + '62': 'DISPLAY MULTIMEDIA MESSAGE', '70': 'ACTIVATE', + '71': 'CONTACTLESS STATE CHANGED', '72': 'COMMAND CONTAINER', + '73': 'ENCAPSULATED SESSION CONTROL', '79': 'LSI COMMAND', + '81': 'End of the proactive UICC session', }; const CMD_QUALIFIER_SHORT = { diff --git a/frontend/sw.js b/frontend/sw.js index 4c0a54d..c82e49d 100644 --- a/frontend/sw.js +++ b/frontend/sw.js @@ -1,4 +1,4 @@ -const CACHE = 'simple-v219'; +const CACHE = 'simple-v220'; const URLS = [ 'index.html', 'help.html', diff --git a/frontend/tests/event_forms.test.js b/frontend/tests/event_forms.test.js index 03e5e3a..5c375af 100644 --- a/frontend/tests/event_forms.test.js +++ b/frontend/tests/event_forms.test.js @@ -35,7 +35,10 @@ eval(extractBlock('const REJECTION_CAUSES = [', 'const EVENT_FORMS = {').replace eval(extractBlock('const EVENT_FORMS = {', 'const PLI_QUALIFIERS = [').replace(/^const /gm, 'var ')); test('CMD_NAMES decodes timer management and the BIP commands', () => { + assert.strictEqual(CMD_NAMES['01'], 'REFRESH'); + assert.strictEqual(CMD_NAMES['10'], 'SET UP CALL'); assert.strictEqual(CMD_NAMES['27'], 'TIMER MANAGEMENT'); + assert.strictEqual(CMD_NAMES['28'], 'SET UP IDLE MODE TEXT'); assert.strictEqual(CMD_NAMES['40'], 'OPEN CHANNEL'); assert.strictEqual(CMD_NAMES['41'], 'CLOSE CHANNEL'); assert.strictEqual(CMD_NAMES['42'], 'RECEIVE DATA'); diff --git a/pyproject.toml b/pyproject.toml index 1b77bbd..302e31b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "pysim-simple-server" -version = "2.7.16" +version = "2.7.17" 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 b6d07fb..bf9f8df 100644 --- a/pysim_simple_server/server.py +++ b/pysim_simple_server/server.py @@ -26,7 +26,7 @@ from osmocom.tlv import BER_TLV_IE from osmocom.utils import rpad -VERSION = '2.7.16' +VERSION = '2.7.17' MAX_ENVELOPE_SEGMENTS = 5 # max SMS segments for outgoing C-APDU in ENVELOPE @@ -1086,15 +1086,29 @@ _PROACTIVE_LOG = [] _PROACTIVE_SESSION_START = None _PROACTIVE_ENTRY_ID = 0 +# TS 102 223 §9.4 command types (the values are also the proactive command +# template tags). Unknown values fall back to 'UNKNOWN' in the log. PROACTIVE_TYPE_NAMES = { - 0x03: 'POLL INTERVAL', 0x05: 'SET UP EVENT LIST', - 0x13: 'SEND SHORT MESSAGE', 0x20: 'PLAY TONE', + 0x01: 'REFRESH', 0x02: 'MORE TIME', 0x03: 'POLL INTERVAL', + 0x04: 'POLLING OFF', 0x05: 'SET UP EVENT LIST', 0x10: 'SET UP CALL', + 0x11: 'SEND SS', 0x12: 'SEND USSD', 0x13: 'SEND SHORT MESSAGE', + 0x14: 'SEND DTMF', 0x15: 'LAUNCH BROWSER', + 0x16: 'GEOGRAPHICAL LOCATION REQUEST', 0x20: 'PLAY TONE', 0x21: 'DISPLAY TEXT', 0x22: 'GET INKEY', 0x23: 'GET INPUT', 0x24: 'SELECT ITEM', 0x25: 'SET UP MENU', 0x26: 'PROVIDE LOCAL INFORMATION', 0x27: 'TIMER MANAGEMENT', - 0x15: 'LAUNCH BROWSER', 0x70: 'ACTIVATE', - 0x40: 'OPEN CHANNEL', 0x41: 'CLOSE CHANNEL', - 0x42: 'RECEIVE DATA', 0x43: 'SEND DATA', 0x44: 'GET CHANNEL STATUS', + 0x28: 'SET UP IDLE MODE TEXT', 0x30: 'PERFORM CARD APDU', + 0x31: 'POWER ON CARD', 0x32: 'POWER OFF CARD', 0x33: 'GET READER STATUS', + 0x34: 'RUN AT COMMAND', 0x35: 'LANGUAGE NOTIFICATION', + 0x40: 'OPEN CHANNEL', 0x41: 'CLOSE CHANNEL', 0x42: 'RECEIVE DATA', + 0x43: 'SEND DATA', 0x44: 'GET CHANNEL STATUS', 0x45: 'SERVICE SEARCH', + 0x46: 'GET SERVICE INFORMATION', 0x47: 'DECLARE SERVICE', + 0x50: 'SET FRAMES', 0x51: 'GET FRAMES STATUS', + 0x60: 'RETRIEVE MULTIMEDIA MESSAGE', 0x61: 'SUBMIT MULTIMEDIA MESSAGE', + 0x62: 'DISPLAY MULTIMEDIA MESSAGE', 0x70: 'ACTIVATE', + 0x71: 'CONTACTLESS STATE CHANGED', 0x72: 'COMMAND CONTAINER', + 0x73: 'ENCAPSULATED SESSION CONTROL', 0x79: 'LSI COMMAND', + 0x81: 'End of the proactive UICC session', } PLI_QUALIFIER_NAMES = { diff --git a/tests/test_proactive_names.py b/tests/test_proactive_names.py new file mode 100644 index 0000000..caf99c1 --- /dev/null +++ b/tests/test_proactive_names.py @@ -0,0 +1,38 @@ +# coding=utf-8 +"""Tests for the proactive command type names (TS 102 223 §9.4). + +The live card issues REFRESH (0x01) right after a Location status event; a +missing entry makes the proactive log show the generic 'Cmd 0x01' fallback. +""" + +import unittest + +from pysim_simple_server.server import PROACTIVE_TYPE_NAMES + + +class ProactiveTypeNamesTests(unittest.TestCase): + def test_refresh_is_named(self): + self.assertEqual(PROACTIVE_TYPE_NAMES[0x01], 'REFRESH') + + def test_common_types_match_the_spec(self): + for value, name in [ + (0x02, 'MORE TIME'), (0x03, 'POLL INTERVAL'), (0x04, 'POLLING OFF'), + (0x05, 'SET UP EVENT LIST'), (0x10, 'SET UP CALL'), + (0x11, 'SEND SS'), (0x12, 'SEND USSD'), + (0x13, 'SEND SHORT MESSAGE'), (0x14, 'SEND DTMF'), + (0x15, 'LAUNCH BROWSER'), (0x16, 'GEOGRAPHICAL LOCATION REQUEST'), + (0x21, 'DISPLAY TEXT'), (0x24, 'SELECT ITEM'), (0x25, 'SET UP MENU'), + (0x26, 'PROVIDE LOCAL INFORMATION'), (0x27, 'TIMER MANAGEMENT'), + (0x28, 'SET UP IDLE MODE TEXT'), (0x40, 'OPEN CHANNEL'), + (0x44, 'GET CHANNEL STATUS'), (0x70, 'ACTIVATE'), + ]: + self.assertEqual(PROACTIVE_TYPE_NAMES[value], name, hex(value)) + + def test_names_are_nonempty_strings(self): + for value, name in PROACTIVE_TYPE_NAMES.items(): + self.assertIsInstance(value, int) + self.assertTrue(isinstance(name, str) and name, hex(value)) + + +if __name__ == '__main__': + unittest.main()