dexter has uploaded this change for review.

View Change

pySim-shell: move export code into filesystem class model

The code that generates the filesystem export lines for the various
different file structures can be moved into the filesystem class model.

This simplifies the code since we do not need any extra logic to
distinguish between the different file structures.

Related: OS#6092
Change-Id: Icc2ee60cfc4379411744ca1033d79a1ee9cff5a6
---
M pySim-shell.py
M pySim/filesystem.py
2 files changed, 118 insertions(+), 58 deletions(-)

git pull ssh://gerrit.osmocom.org:29418/pysim refs/changes/12/37612/1
diff --git a/pySim-shell.py b/pySim-shell.py
index 0e7e63c..4f5959b 100755
--- a/pySim-shell.py
+++ b/pySim-shell.py
@@ -599,66 +599,14 @@
self._cmd.poutput("# directory: %s (%s)" % (df_path, df_path_fid))
try:
fcp_dec = self._cmd.lchan.select(filename, self._cmd)
- self._cmd.poutput("# file: %s (%s)" % (
- self._cmd.lchan.selected_file.name, self._cmd.lchan.selected_file.fid))
-
- structure = self._cmd.lchan.selected_file_structure()
- self._cmd.poutput("# structure: %s" % str(structure))
+ self._cmd.poutput("# file: %s (%s)" %
+ (self._cmd.lchan.selected_file.name, self._cmd.lchan.selected_file.fid))
+ self._cmd.poutput("# structure: %s" % self._cmd.lchan.selected_file_structure())
self._cmd.poutput("# RAW FCP Template: %s" % str(self._cmd.lchan.selected_file_fcp_hex))
self._cmd.poutput("# Decoded FCP Template: %s" % str(self._cmd.lchan.selected_file_fcp))
+ self._cmd.poutput("select " + self._cmd.lchan.selected_file.fully_qualified_path_str())
+ self._cmd.poutput(self._cmd.lchan.selected_file.export(as_json, self._cmd.lchan))

- for f in df_path_list:
- self._cmd.poutput("select " + str(f))
- self._cmd.poutput("select " + self._cmd.lchan.selected_file.name)
-
- if structure == 'transparent':
- if as_json:
- result = self._cmd.lchan.read_binary_dec()
- self._cmd.poutput("update_binary_decoded '%s'" % json.dumps(result[0], cls=JsonEncoder))
- else:
- result = self._cmd.lchan.read_binary()
- self._cmd.poutput("update_binary " + str(result[0]))
- elif structure == 'cyclic' or structure == 'linear_fixed':
- # Use number of records specified in select response
- num_of_rec = self._cmd.lchan.selected_file_num_of_rec()
- if num_of_rec:
- for r in range(1, num_of_rec + 1):
- if as_json:
- result = self._cmd.lchan.read_record_dec(r)
- self._cmd.poutput("update_record_decoded %d '%s'" % (r, json.dumps(result[0], cls=JsonEncoder)))
- else:
- result = self._cmd.lchan.read_record(r)
- self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
-
- # When the select response does not return the number of records, read until we hit the
- # first record that cannot be read.
- else:
- r = 1
- while True:
- try:
- if as_json:
- result = self._cmd.lchan.read_record_dec(r)
- self._cmd.poutput("update_record_decoded %d '%s'" % (r, json.dumps(result[0], cls=JsonEncoder)))
- else:
- result = self._cmd.lchan.read_record(r)
- self._cmd.poutput("update_record %d %s" % (r, str(result[0])))
- except SwMatchError as e:
- # We are past the last valid record - stop
- if e.sw_actual == "9402":
- break
- # Some other problem occurred
- else:
- raise e
- r = r + 1
- elif structure == 'ber_tlv':
- tags = self._cmd.lchan.retrieve_tags()
- for t in tags:
- result = self._cmd.lchan.retrieve_data(t)
- (tag, l, val, remainer) = bertlv_parse_one(h2b(result[0]))
- self._cmd.poutput("set_data 0x%02x %s" % (t, b2h(val)))
- else:
- raise RuntimeError(
- 'Unsupported structure "%s" of file "%s"' % (structure, filename))
except Exception as e:
bad_file_str = df_path + "/" + str(filename) + ", " + str(e)
self._cmd.poutput("# bad file: %s" % bad_file_str)
diff --git a/pySim/filesystem.py b/pySim/filesystem.py
index 77482cc..ad124ff 100644
--- a/pySim/filesystem.py
+++ b/pySim/filesystem.py
@@ -35,7 +35,9 @@
from cmd2 import CommandSet, with_default_category
from smartcard.util import toBytes

-from pySim.utils import sw_match, h2b, b2h, is_hex, auto_int, auto_uint8, auto_uint16, is_hexstr
+from pySim.utils import sw_match, h2b, b2h, is_hex, auto_int, auto_uint8, auto_uint16, is_hexstr, JsonEncoder
+from pySim.utils import bertlv_parse_one
+
from pySim.construct import filter_dict, parse_construct, build_construct
from pySim.jsonpath import js_path_modify
from pySim.commands import SimCardCommands
@@ -774,6 +776,25 @@
raise NotImplementedError(
"%s encoder not yet implemented. Patches welcome." % self)

+ @staticmethod
+ def export(as_json: bool, lchan):
+ """
+ Export the file contents of a TransparentEF. This method returns a shell command string (See also ShellCommand
+ definition in this class) that can be used to write the file contents back.
+ """
+
+ if lchan.selected_file_structure() != 'transparent':
+ raise ValueError("selected file has structure type '%s', expecting a file with structure 'transparent'" %
+ lchan.selected_file_structure())
+ export_str = ""
+ if as_json:
+ result = lchan.read_binary_dec()
+ export_str += ("update_binary_decoded '%s'\n" % json.dumps(result[0], cls=JsonEncoder))
+ else:
+ result = lchan.read_binary()
+ export_str += ("update_binary %s\n" % str(result[0]))
+ return export_str.strip()
+

class LinFixedEF(CardEF):
"""Linear Fixed EF (Entry File) in the smart card filesystem.
@@ -1044,6 +1065,54 @@
raise NotImplementedError(
"%s encoder not yet implemented. Patches welcome." % self)

+ @staticmethod
+ def export(as_json: bool, lchan):
+ """
+ Export the file contents of a LinFixedEF (or a CyclicEF). This method returns a shell command string (See also
+ ShellCommand definition in this class) that can be used to write the file contents back.
+ """
+
+ # A CyclicEF is a subclass of LinFixedEF.
+ if lchan.selected_file_structure() != 'linear_fixed' and lchan.selected_file_structure() != 'cyclic':
+ raise ValueError("selected file has structure type '%s', expecting a file with structure 'linear_fixed' or 'cyclic'" %
+ lchan.selected_file_structure())
+
+ export_str = ""
+
+ # Use number of records specified in select response
+ num_of_rec = lchan.selected_file_num_of_rec()
+ if num_of_rec:
+ for r in range(1, num_of_rec + 1):
+ if as_json:
+ result = lchan.read_record_dec(r)
+ export_str += ("update_record_decoded %d '%s'\n" % (r, json.dumps(result[0], cls=JsonEncoder)))
+ else:
+ result = lchan.read_record(r)
+ export_str += ("update_record %d %s\n" % (r, str(result[0])))
+
+ # In case the select response does not return the number of records, read until we hit the first record that
+ # cannot be read.
+ else:
+ r = 1
+ while True:
+ try:
+ if as_json:
+ result = lchan.read_record_dec(r)
+ export_str += ("update_record_decoded %d '%s'\n" % (r, json.dumps(result[0], cls=JsonEncoder)))
+ else:
+ result = lchan.read_record(r)
+ export_str += ("update_record %d %s\n" % (r, str(result[0])))
+ except SwMatchError as e:
+ # We are past the last valid record - stop
+ if e.sw_actual == "9402":
+ break
+ # Some other problem occurred
+ else:
+ raise e
+ r = r + 1
+
+ return export_str.strip()
+

class CyclicEF(LinFixedEF):
"""Cyclic EF (Entry File) in the smart card filesystem"""
@@ -1264,6 +1333,33 @@
self.size = size
self.shell_commands = [self.ShellCommands()]

+ @staticmethod
+ def export(as_json: bool, lchan):
+ """
+ Export the file contents of a BerTlvEF. This method returns a shell command string (See also ShellCommand
+ definition in this class) that can be used to write the file contents back.
+ """
+
+ if lchan.selected_file_structure() != 'ber_tlv':
+ raise ValueError("selected file has structure type '%s', expecting a file with structure 'ber_tlv'" %
+ lchan.selected_file_structure())
+
+ # TODO: Add JSON output as soon as we have a set_data_decoded command and a retrieve_data_dec method.
+ if as_json:
+ raise NotImplementedError("BerTlvEF encoder not yet implemented. Patches welcome.")
+
+ export_str = ""
+ tags = lchan.retrieve_tags()
+ if tags == []:
+ export_str += "# empty file, no tags"
+ else:
+ for t in tags:
+ result = lchan.retrieve_data(t)
+ (tag, l, val, remainer) = bertlv_parse_one(h2b(result[0]))
+ export_str += ("set_data 0x%02x %s\n" % (t, b2h(val)))
+ return export_str.strip()
+
+
def interpret_sw(sw_data: dict, sw: str):
"""Interpret a given status word.


To view, visit change 37612. To unsubscribe, or for help writing mail filters, visit settings.

Gerrit-Project: pysim
Gerrit-Branch: master
Gerrit-Change-Id: Icc2ee60cfc4379411744ca1033d79a1ee9cff5a6
Gerrit-Change-Number: 37612
Gerrit-PatchSet: 1
Gerrit-Owner: dexter <pmaier@sysmocom.de>
Gerrit-MessageType: newchange