From 9776dd1082ec8ff1d49ffb83c41c277102522fed Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Mon, 19 Dec 2022 22:13:58 -0500 Subject: [PATCH 01/31] add new iwconfig parser --- jc/parsers/iwconfig.py | 170 ++++++++++++++++++++++ tests/fixtures/ubuntu-20.10/iwconfig.json | 29 ++++ tests/fixtures/ubuntu-20.10/iwconfig.out | 9 ++ tests/test_iwconfig.py | 33 +++++ 4 files changed, 241 insertions(+) create mode 100644 jc/parsers/iwconfig.py create mode 100644 tests/fixtures/ubuntu-20.10/iwconfig.json create mode 100644 tests/fixtures/ubuntu-20.10/iwconfig.out create mode 100644 tests/test_iwconfig.py diff --git a/jc/parsers/iwconfig.py b/jc/parsers/iwconfig.py new file mode 100644 index 00000000..c474c03a --- /dev/null +++ b/jc/parsers/iwconfig.py @@ -0,0 +1,170 @@ +"""jc - JSON Convert `iwconfig` command output parser + +No `iwconfig` options are supported. + +Usage (cli): + + $ iwconfig | jc --iwconfig + +or + + $ jc iwconfig + +Usage (module): + + import jc + result = jc.parse('iwconfig', iwconfig_command_output) + +Schema: + + [ + ] + + +Examples: + + $ iwconfig | jc --ifconfig -p + +""" +import re +from typing import List, Dict +from jc.jc_types import JSONDictType +import jc.utils + +class info(): + """Provides parser metadata (version, author, etc.)""" + version = '1.0' + description = '`iwconfig` command parser' + author = 'Thomas Vincent' + author_email = 'vrince@gmail.com' + compatible = ['linux'] + magic_commands = ['iwconfig'] + + +__version__ = info.version + + +def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: + """ + Final processing to conform to the schema. + + Parameters: + + proc_data: (List of Dictionaries) raw structured data to process + + Returns: + + List of Dictionaries. Structured to conform to the schema. + """ + int_list = { + 'flags', 'mtu', 'ipv6_mask', 'rx_packets', 'rx_bytes', 'rx_errors', 'rx_dropped', + 'rx_overruns', 'rx_frame', 'tx_packets', 'tx_bytes', 'tx_errors', 'tx_dropped', + 'tx_overruns', 'tx_carrier', 'tx_collisions', 'metric', 'nd6_options', 'lane' + } + float_list = {'rx_power_mw', 'rx_power_dbm', 'tx_bias_ma'} + return proc_data + +def parse( + data: str, + raw: bool = False, + quiet: bool = False +) -> List[JSONDictType]: + """ + Main text parsing function + + Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + + Returns: + + List of Dictionaries. Raw or processed structured data. + """ + jc.utils.compatibility(__name__, info.compatible, quiet) + jc.utils.input_type_check(data) + + raw_output: List[Dict] = [] + + # for backwards compatibility, preset all fields to None + wireless_extension_obj: Dict = { + "name": None, + "protocol": None, + "essid": None, + "mode": None, + "frequency": None, + "frequency_unit": None, + "access_point": None, + "bit_rate": None, + "tx_power": None, + "tx_power_unit": None, + "retry_short_limit": None, + "rts_threshold": None, + "fragment_threshold": None, + "poser_management": None, + "link_quality": None, + "signal_level": None, + "rx_invalid_nwid": None, + "rx_invalid_crypt": None, + "rx_invalid_frag": None, + "tx_excessive_retries": None, + "invalid_misc": None, + "missed_beacon": None + } + + interface_item: Dict = wireless_extension_obj.copy() + + re_interface = re.compile(r'^(?P[a-zA-Z0-9:._-]+)\s+(?P([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P[a-zA-Z0-9:._\s]+)\"') + re_mode = re.compile(r'Mode:(?P\w+)') + re_frequency = re.compile(r'Frequency:(?P[0-9.]+)\s(?P\w+)') + re_access_point = re.compile(r'Access Point:\s*(?P[0-9A-F:]+)') + re_bit_rate = re.compile(r'Bit Rate=(?P[0-9.]+)\s(?P[\w\/]+)') + re_tx_power= re.compile(r'Tx-Power=(?P[-0-9]+)\s(?P[\w]+)') + re_retry_short_limit = re.compile(r'Retry short limit:(?P[0-9\/]+)') + re_rts_threshold = re.compile(r'RTS thr:(?P(off|on))') + re_fragment_threshold = re.compile(r'Fragment thr:(?P(off|on))') + re_power_management = re.compile(r'Power Management:(?P(off|on))') + re_link_quality = re.compile(r'Link Quality=(?P[0-9\/]+)') + re_signal_level = re.compile(r'Signal level=(?P[-0-9]+)\s(?P[\w]+)') + re_rx_invalid_nwid = re.compile(r'Rx invalid nwid:(?P[-0-9]+)') + re_rx_invalid_crypt = re.compile(r'Rx invalid crypt:(?P[-0-9]+)') + re_rx_invalid_frag = re.compile(r'Rx invalid frag:(?P[-0-9]+)') + re_tx_excessive_retries = re.compile(r'Tx excessive retries:(?P[-0-9]+)') + re_invalid_misc = re.compile(r'Invalid misc:(?P[0-9]+)') + re_missed_beacon = re.compile(r'Missed beacon:(?P[0-9]+)') + + re_all = [ + re_mode, re_frequency, re_access_point, re_bit_rate, re_tx_power, + re_retry_short_limit, re_rts_threshold, re_fragment_threshold, re_power_management, + re_link_quality, re_signal_level, re_rx_invalid_nwid, re_rx_invalid_crypt, + re_rx_invalid_frag, re_tx_excessive_retries, re_invalid_misc, re_missed_beacon + ] + + if jc.utils.has_data(data): + for line in filter(None, data.splitlines()): + + # Find new interface lines + interface_match = re.search(re_interface, line) + if interface_match: + if interface_item['name'] is not None: + raw_output.append(interface_item) + interface_item = wireless_extension_obj.copy() + + interface_item.update(interface_match.groupdict()) + continue + + # we do not have any interface yet continue to search for it --> next line + if interface_item['name'] is None: + continue + + # Filling interface with whatever we can find + for re_entry in re_all: + match = re.search(re_entry, line) + if match: + interface_item.update(match.groupdict()) + + if interface_item['name'] is not None: + raw_output.append(interface_item) + + return raw_output if raw else _process(raw_output) diff --git a/tests/fixtures/ubuntu-20.10/iwconfig.json b/tests/fixtures/ubuntu-20.10/iwconfig.json new file mode 100644 index 00000000..e175a143 --- /dev/null +++ b/tests/fixtures/ubuntu-20.10/iwconfig.json @@ -0,0 +1,29 @@ +[ + { + "name": "wlp5s0", + "protocol": "IEEE 802.11", + "essid": "BLABLABLA", + "mode": "Managed", + "frequency": "5.18", + "frequency_unit": "GHz", + "access_point": "E6:63:DA:16:50:BF", + "bit_rate": "6", + "tx_power": "30", + "tx_power_unit": "dBm", + "retry_short_limit": "7", + "rts_threshold": "off", + "fragment_threshold": "off", + "poser_management": null, + "link_quality": null, + "signal_level": "-52", + "rx_invalid_nwid": "0", + "rx_invalid_crypt": "0", + "rx_invalid_frag": "0", + "tx_excessive_retries": "0", + "invalid_misc": "1766", + "missed_beacon": "0", + "bit_rate_unit": "Mb/s", + "power_management": "58/70", + "signal_level_unit": "dBm" + } +] \ No newline at end of file diff --git a/tests/fixtures/ubuntu-20.10/iwconfig.out b/tests/fixtures/ubuntu-20.10/iwconfig.out new file mode 100644 index 00000000..eaec4add --- /dev/null +++ b/tests/fixtures/ubuntu-20.10/iwconfig.out @@ -0,0 +1,9 @@ +wlp5s0 IEEE 802.11 ESSID:"BLABLABLA" + Mode:Managed Frequency:5.18 GHz Access Point: E6:63:DA:16:50:BF + Bit Rate=6 Mb/s Tx-Power=30 dBm + Retry short limit:7 RTS thr:off Fragment thr:off + Power Management:on + Link Quality=58/70 Signal level=-52 dBm + Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0 + Tx excessive retries:0 Invalid misc:1766 Missed beacon:0 + diff --git a/tests/test_iwconfig.py b/tests/test_iwconfig.py new file mode 100644 index 00000000..2bdbae62 --- /dev/null +++ b/tests/test_iwconfig.py @@ -0,0 +1,33 @@ +import os +import json +import unittest +import jc.parsers.iwconfig + +THIS_DIR = os.path.dirname(os.path.abspath(__file__)) + + +class iwconfigTests(unittest.TestCase): + + # input + with open(os.path.join(THIS_DIR, 'fixtures/ubuntu-20.10/iwconfig.out'), 'r', encoding='utf-8') as f: + ubuntu_20_10_iwconfig= f.read() + + # output + with open(os.path.join(THIS_DIR, 'fixtures/ubuntu-20.10/iwconfig.json'), 'r', encoding='utf-8') as f: + ubuntu_20_10_iwconfig_json= json.loads(f.read()) + + def test_iwconfig_nodata(self): + """ + Test 'iwconfig' with no data + """ + self.assertEqual(jc.parsers.iwconfig.parse('', quiet=True), []) + + def test_iwconfig_ubuntu_20_04(self): + """ + Test 'iwconfig' on Ubuntu 20.10 + """ + self.assertEqual(jc.parsers.iwconfig.parse(self.ubuntu_20_10_iwconfig, quiet=True), self.ubuntu_20_10_iwconfig_json) + + +if __name__ == '__main__': + unittest.main() From e207cccdc5b193403f86a862a49749b278f03379 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Mon, 19 Dec 2022 22:23:10 -0500 Subject: [PATCH 02/31] fix copy/paste mess in regex --- jc/parsers/iwconfig.py | 6 ++++-- tests/fixtures/ubuntu-20.10/iwconfig.json | 11 +++++------ 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/jc/parsers/iwconfig.py b/jc/parsers/iwconfig.py index c474c03a..9b0c13c9 100644 --- a/jc/parsers/iwconfig.py +++ b/jc/parsers/iwconfig.py @@ -97,14 +97,16 @@ def parse( "frequency_unit": None, "access_point": None, "bit_rate": None, + "bit_rate_unit": None, "tx_power": None, "tx_power_unit": None, "retry_short_limit": None, "rts_threshold": None, "fragment_threshold": None, - "poser_management": None, + "power_management": None, "link_quality": None, "signal_level": None, + "signal_level_unit": None, "rx_invalid_nwid": None, "rx_invalid_crypt": None, "rx_invalid_frag": None, @@ -125,7 +127,7 @@ def parse( re_rts_threshold = re.compile(r'RTS thr:(?P(off|on))') re_fragment_threshold = re.compile(r'Fragment thr:(?P(off|on))') re_power_management = re.compile(r'Power Management:(?P(off|on))') - re_link_quality = re.compile(r'Link Quality=(?P[0-9\/]+)') + re_link_quality = re.compile(r'Link Quality=(?P[0-9\/]+)') re_signal_level = re.compile(r'Signal level=(?P[-0-9]+)\s(?P[\w]+)') re_rx_invalid_nwid = re.compile(r'Rx invalid nwid:(?P[-0-9]+)') re_rx_invalid_crypt = re.compile(r'Rx invalid crypt:(?P[-0-9]+)') diff --git a/tests/fixtures/ubuntu-20.10/iwconfig.json b/tests/fixtures/ubuntu-20.10/iwconfig.json index e175a143..6f824ed2 100644 --- a/tests/fixtures/ubuntu-20.10/iwconfig.json +++ b/tests/fixtures/ubuntu-20.10/iwconfig.json @@ -8,22 +8,21 @@ "frequency_unit": "GHz", "access_point": "E6:63:DA:16:50:BF", "bit_rate": "6", + "bit_rate_unit": "Mb/s", "tx_power": "30", "tx_power_unit": "dBm", "retry_short_limit": "7", "rts_threshold": "off", "fragment_threshold": "off", - "poser_management": null, - "link_quality": null, + "power_management": "on", + "link_quality": "58/70", "signal_level": "-52", + "signal_level_unit": "dBm", "rx_invalid_nwid": "0", "rx_invalid_crypt": "0", "rx_invalid_frag": "0", "tx_excessive_retries": "0", "invalid_misc": "1766", - "missed_beacon": "0", - "bit_rate_unit": "Mb/s", - "power_management": "58/70", - "signal_level_unit": "dBm" + "missed_beacon": "0" } ] \ No newline at end of file From e996b3a9f6a3fba2d5cd8e947fa5712b30b9ed55 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Mon, 19 Dec 2022 22:36:48 -0500 Subject: [PATCH 03/31] add iwconfig raw vs process + tests --- jc/parsers/iwconfig.py | 16 +++++++---- tests/fixtures/ubuntu-20.10/iwconfig-raw.json | 28 +++++++++++++++++++ tests/fixtures/ubuntu-20.10/iwconfig.json | 28 +++++++++---------- tests/test_iwconfig.py | 11 +++++++- 4 files changed, 62 insertions(+), 21 deletions(-) create mode 100644 tests/fixtures/ubuntu-20.10/iwconfig-raw.json diff --git a/jc/parsers/iwconfig.py b/jc/parsers/iwconfig.py index 9b0c13c9..09417781 100644 --- a/jc/parsers/iwconfig.py +++ b/jc/parsers/iwconfig.py @@ -56,12 +56,16 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: List of Dictionaries. Structured to conform to the schema. """ - int_list = { - 'flags', 'mtu', 'ipv6_mask', 'rx_packets', 'rx_bytes', 'rx_errors', 'rx_dropped', - 'rx_overruns', 'rx_frame', 'tx_packets', 'tx_bytes', 'tx_errors', 'tx_dropped', - 'tx_overruns', 'tx_carrier', 'tx_collisions', 'metric', 'nd6_options', 'lane' - } - float_list = {'rx_power_mw', 'rx_power_dbm', 'tx_bias_ma'} + int_list = ['signal_level', 'rx_invalid_nwid', 'rx_invalid_crypt', 'rx_invalid_frag', + 'tx_excessive_retries', 'invalid_misc', 'missed_beacon', 'tx_power', 'retry_short_limit'] + float_list = ['frequency', 'bit_rate'] + bool_list = ['rts_threshold', 'fragment_threshold', 'power_management'] + + + proc_data = [ { key: int(value) if key in int_list else value for key, value in proc_data_item.items() } for proc_data_item in proc_data ] + proc_data = [ { key: float(value) if key in float_list else value for key, value in proc_data_item.items() } for proc_data_item in proc_data ] + proc_data = [ { key: value == 'on' if key in bool_list else value for key, value in proc_data_item .items() } for proc_data_item in proc_data ] + return proc_data def parse( diff --git a/tests/fixtures/ubuntu-20.10/iwconfig-raw.json b/tests/fixtures/ubuntu-20.10/iwconfig-raw.json new file mode 100644 index 00000000..6f824ed2 --- /dev/null +++ b/tests/fixtures/ubuntu-20.10/iwconfig-raw.json @@ -0,0 +1,28 @@ +[ + { + "name": "wlp5s0", + "protocol": "IEEE 802.11", + "essid": "BLABLABLA", + "mode": "Managed", + "frequency": "5.18", + "frequency_unit": "GHz", + "access_point": "E6:63:DA:16:50:BF", + "bit_rate": "6", + "bit_rate_unit": "Mb/s", + "tx_power": "30", + "tx_power_unit": "dBm", + "retry_short_limit": "7", + "rts_threshold": "off", + "fragment_threshold": "off", + "power_management": "on", + "link_quality": "58/70", + "signal_level": "-52", + "signal_level_unit": "dBm", + "rx_invalid_nwid": "0", + "rx_invalid_crypt": "0", + "rx_invalid_frag": "0", + "tx_excessive_retries": "0", + "invalid_misc": "1766", + "missed_beacon": "0" + } +] \ No newline at end of file diff --git a/tests/fixtures/ubuntu-20.10/iwconfig.json b/tests/fixtures/ubuntu-20.10/iwconfig.json index 6f824ed2..511884e3 100644 --- a/tests/fixtures/ubuntu-20.10/iwconfig.json +++ b/tests/fixtures/ubuntu-20.10/iwconfig.json @@ -4,25 +4,25 @@ "protocol": "IEEE 802.11", "essid": "BLABLABLA", "mode": "Managed", - "frequency": "5.18", + "frequency": 5.18, "frequency_unit": "GHz", "access_point": "E6:63:DA:16:50:BF", - "bit_rate": "6", + "bit_rate": 6.0, "bit_rate_unit": "Mb/s", - "tx_power": "30", + "tx_power": 30, "tx_power_unit": "dBm", - "retry_short_limit": "7", - "rts_threshold": "off", - "fragment_threshold": "off", - "power_management": "on", + "retry_short_limit": 7, + "rts_threshold": false, + "fragment_threshold": false, + "power_management": true, "link_quality": "58/70", - "signal_level": "-52", + "signal_level": -52, "signal_level_unit": "dBm", - "rx_invalid_nwid": "0", - "rx_invalid_crypt": "0", - "rx_invalid_frag": "0", - "tx_excessive_retries": "0", - "invalid_misc": "1766", - "missed_beacon": "0" + "rx_invalid_nwid": 0, + "rx_invalid_crypt": 0, + "rx_invalid_frag": 0, + "tx_excessive_retries": 0, + "invalid_misc": 1766, + "missed_beacon": 0 } ] \ No newline at end of file diff --git a/tests/test_iwconfig.py b/tests/test_iwconfig.py index 2bdbae62..050b9b52 100644 --- a/tests/test_iwconfig.py +++ b/tests/test_iwconfig.py @@ -16,18 +16,27 @@ class iwconfigTests(unittest.TestCase): with open(os.path.join(THIS_DIR, 'fixtures/ubuntu-20.10/iwconfig.json'), 'r', encoding='utf-8') as f: ubuntu_20_10_iwconfig_json= json.loads(f.read()) + with open(os.path.join(THIS_DIR, 'fixtures/ubuntu-20.10/iwconfig-raw.json'), 'r', encoding='utf-8') as f: + ubuntu_20_10_iwconfig_raw_json= json.loads(f.read()) + + def test_iwconfig_nodata(self): """ Test 'iwconfig' with no data """ self.assertEqual(jc.parsers.iwconfig.parse('', quiet=True), []) + def test_iwconfig_ubuntu_20_04(self): + """ + Test 'iwconfig' raw on Ubuntu 20.10 + """ + self.assertEqual(jc.parsers.iwconfig.parse(self.ubuntu_20_10_iwconfig, quiet=True, raw=True), self.ubuntu_20_10_iwconfig_raw_json) + def test_iwconfig_ubuntu_20_04(self): """ Test 'iwconfig' on Ubuntu 20.10 """ self.assertEqual(jc.parsers.iwconfig.parse(self.ubuntu_20_10_iwconfig, quiet=True), self.ubuntu_20_10_iwconfig_json) - if __name__ == '__main__': unittest.main() From bcd7bac95058e328c455cbd1b50f197c42a0f4d5 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Wed, 21 Dec 2022 17:12:20 -0500 Subject: [PATCH 04/31] iwconfig: add to parser list + move test fixture to generic folder --- jc/lib.py | 1 + jc/parsers/iwconfig.py | 68 +++++++++---------- .../iwconfig-raw.json | 0 .../{ubuntu-20.10 => generic}/iwconfig.json | 0 .../{ubuntu-20.10 => generic}/iwconfig.out | 0 tests/test_iwconfig.py | 24 +++---- 6 files changed, 46 insertions(+), 47 deletions(-) rename tests/fixtures/{ubuntu-20.10 => generic}/iwconfig-raw.json (100%) rename tests/fixtures/{ubuntu-20.10 => generic}/iwconfig.json (100%) rename tests/fixtures/{ubuntu-20.10 => generic}/iwconfig.out (100%) diff --git a/jc/lib.py b/jc/lib.py index ab0a544c..ca02f5e1 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -65,6 +65,7 @@ parsers: List[str] = [ 'iptables', 'iso-datetime', 'iw-scan', + 'iwconfig', 'jar-manifest', 'jobs', 'jwt', diff --git a/jc/parsers/iwconfig.py b/jc/parsers/iwconfig.py index 09417781..8d7d2f01 100644 --- a/jc/parsers/iwconfig.py +++ b/jc/parsers/iwconfig.py @@ -18,6 +18,32 @@ Usage (module): Schema: [ + { + "name": string, + "protocol": string, + "essid": string, + "mode": string, + "frequency": float, + "frequency_unit": string, + "access_point": string, + "bit_rate": float, + "bit_rate_unit": string, + "tx_power": integer, + "tx_power_unit": string, + "retry_short_limit": integer, + "rts_threshold": boolean, + "fragment_threshold": boolean, + "power_management": boolean, + "link_quality": string, + "signal_level": integer, + "signal_level_unit": string, + "rx_invalid_nwid": integer, + "rx_invalid_crypt": integer, + "rx_invalid_frag": integer, + "tx_excessive_retries": integer, + "invalid_misc": integer, + "missed_beacon": integer + } ] @@ -90,36 +116,7 @@ def parse( jc.utils.input_type_check(data) raw_output: List[Dict] = [] - - # for backwards compatibility, preset all fields to None - wireless_extension_obj: Dict = { - "name": None, - "protocol": None, - "essid": None, - "mode": None, - "frequency": None, - "frequency_unit": None, - "access_point": None, - "bit_rate": None, - "bit_rate_unit": None, - "tx_power": None, - "tx_power_unit": None, - "retry_short_limit": None, - "rts_threshold": None, - "fragment_threshold": None, - "power_management": None, - "link_quality": None, - "signal_level": None, - "signal_level_unit": None, - "rx_invalid_nwid": None, - "rx_invalid_crypt": None, - "rx_invalid_frag": None, - "tx_excessive_retries": None, - "invalid_misc": None, - "missed_beacon": None - } - - interface_item: Dict = wireless_extension_obj.copy() + wireless_extension_obj: Dict = {} re_interface = re.compile(r'^(?P[a-zA-Z0-9:._-]+)\s+(?P([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P[a-zA-Z0-9:._\s]+)\"') re_mode = re.compile(r'Mode:(?P\w+)') @@ -147,21 +144,22 @@ def parse( re_rx_invalid_frag, re_tx_excessive_retries, re_invalid_misc, re_missed_beacon ] + interface_item = None if jc.utils.has_data(data): for line in filter(None, data.splitlines()): # Find new interface lines interface_match = re.search(re_interface, line) if interface_match: - if interface_item['name'] is not None: + if interface_item is not None: raw_output.append(interface_item) - interface_item = wireless_extension_obj.copy() + interface_item = dict() interface_item.update(interface_match.groupdict()) continue - # we do not have any interface yet continue to search for it --> next line - if interface_item['name'] is None: + # we do not have any interface yet continue to search for it --> next line + if interface_item is None: continue # Filling interface with whatever we can find @@ -170,7 +168,7 @@ def parse( if match: interface_item.update(match.groupdict()) - if interface_item['name'] is not None: + if interface_item is not None: raw_output.append(interface_item) return raw_output if raw else _process(raw_output) diff --git a/tests/fixtures/ubuntu-20.10/iwconfig-raw.json b/tests/fixtures/generic/iwconfig-raw.json similarity index 100% rename from tests/fixtures/ubuntu-20.10/iwconfig-raw.json rename to tests/fixtures/generic/iwconfig-raw.json diff --git a/tests/fixtures/ubuntu-20.10/iwconfig.json b/tests/fixtures/generic/iwconfig.json similarity index 100% rename from tests/fixtures/ubuntu-20.10/iwconfig.json rename to tests/fixtures/generic/iwconfig.json diff --git a/tests/fixtures/ubuntu-20.10/iwconfig.out b/tests/fixtures/generic/iwconfig.out similarity index 100% rename from tests/fixtures/ubuntu-20.10/iwconfig.out rename to tests/fixtures/generic/iwconfig.out diff --git a/tests/test_iwconfig.py b/tests/test_iwconfig.py index 050b9b52..062d595c 100644 --- a/tests/test_iwconfig.py +++ b/tests/test_iwconfig.py @@ -9,15 +9,15 @@ THIS_DIR = os.path.dirname(os.path.abspath(__file__)) class iwconfigTests(unittest.TestCase): # input - with open(os.path.join(THIS_DIR, 'fixtures/ubuntu-20.10/iwconfig.out'), 'r', encoding='utf-8') as f: - ubuntu_20_10_iwconfig= f.read() + with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig.out'), 'r', encoding='utf-8') as f: + iwconfig_output = f.read() # output - with open(os.path.join(THIS_DIR, 'fixtures/ubuntu-20.10/iwconfig.json'), 'r', encoding='utf-8') as f: - ubuntu_20_10_iwconfig_json= json.loads(f.read()) + with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig.json'), 'r', encoding='utf-8') as f: + iwconfig_json = json.loads(f.read()) - with open(os.path.join(THIS_DIR, 'fixtures/ubuntu-20.10/iwconfig-raw.json'), 'r', encoding='utf-8') as f: - ubuntu_20_10_iwconfig_raw_json= json.loads(f.read()) + with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig-raw.json'), 'r', encoding='utf-8') as f: + iwconfig_raw_json = json.loads(f.read()) def test_iwconfig_nodata(self): @@ -26,17 +26,17 @@ class iwconfigTests(unittest.TestCase): """ self.assertEqual(jc.parsers.iwconfig.parse('', quiet=True), []) - def test_iwconfig_ubuntu_20_04(self): + def test_iwconfig_raw(self): """ - Test 'iwconfig' raw on Ubuntu 20.10 + Test 'iwconfig' raw """ - self.assertEqual(jc.parsers.iwconfig.parse(self.ubuntu_20_10_iwconfig, quiet=True, raw=True), self.ubuntu_20_10_iwconfig_raw_json) + self.assertEqual(jc.parsers.iwconfig.parse(self.iwconfig_output, quiet=True, raw=True), self.iwconfig_raw_json) - def test_iwconfig_ubuntu_20_04(self): + def test_iwconfig(self): """ - Test 'iwconfig' on Ubuntu 20.10 + Test 'iwconfig' """ - self.assertEqual(jc.parsers.iwconfig.parse(self.ubuntu_20_10_iwconfig, quiet=True), self.ubuntu_20_10_iwconfig_json) + self.assertEqual(jc.parsers.iwconfig.parse(self.iwconfig_output, quiet=True), self.iwconfig_json) if __name__ == '__main__': unittest.main() From 8d9a2744afbdd62722ab057a568428d53a4c92a6 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Wed, 21 Dec 2022 17:15:25 -0500 Subject: [PATCH 05/31] iwconfig: add example in the docstring --- jc/parsers/iwconfig.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/jc/parsers/iwconfig.py b/jc/parsers/iwconfig.py index 8d7d2f01..138f7d8f 100644 --- a/jc/parsers/iwconfig.py +++ b/jc/parsers/iwconfig.py @@ -49,7 +49,35 @@ Schema: Examples: - $ iwconfig | jc --ifconfig -p + $ iwconfig 2> /dev/null | jc --iwconfig -p + [ + { + "name": "wlp5s0", + "protocol": "IEEE 802.11", + "essid": "BLABLABLA", + "mode": "Managed", + "frequency": 5.18, + "frequency_unit": "GHz", + "access_point": "E6:64:DA:16:51:BF", + "bit_rate": 6.0, + "bit_rate_unit": "Mb/s", + "tx_power": 30, + "tx_power_unit": "dBm", + "retry_short_limit": 7, + "rts_threshold": false, + "fragment_threshold": false, + "power_management": true, + "link_quality": "61/70", + "signal_level": -49, + "signal_level_unit": "dBm", + "rx_invalid_nwid": 0, + "rx_invalid_crypt": 0, + "rx_invalid_frag": 0, + "tx_excessive_retries": 0, + "invalid_misc": 2095, + "missed_beacon": 0 + } + ] """ import re From a60bf00d54d7a462d0a4e9460ea2002bc31114d5 Mon Sep 17 00:00:00 2001 From: Thomas Vincent Date: Wed, 21 Dec 2022 17:25:57 -0500 Subject: [PATCH 06/31] iwconfig: add test with many interface --- tests/fixtures/generic/iwconfig-many.json | 54 +++++++++++++++++++++++ tests/fixtures/generic/iwconfig-many.out | 17 +++++++ tests/test_iwconfig.py | 12 +++++ 3 files changed, 83 insertions(+) create mode 100644 tests/fixtures/generic/iwconfig-many.json create mode 100644 tests/fixtures/generic/iwconfig-many.out diff --git a/tests/fixtures/generic/iwconfig-many.json b/tests/fixtures/generic/iwconfig-many.json new file mode 100644 index 00000000..aae19364 --- /dev/null +++ b/tests/fixtures/generic/iwconfig-many.json @@ -0,0 +1,54 @@ +[ + { + "name": "wlp5s0", + "protocol": "IEEE 802.11", + "essid": "BLABLABLA", + "mode": "Managed", + "frequency": 5.18, + "frequency_unit": "GHz", + "access_point": "E6:63:DA:16:50:BF", + "bit_rate": 6.0, + "bit_rate_unit": "Mb/s", + "tx_power": 30, + "tx_power_unit": "dBm", + "retry_short_limit": 7, + "rts_threshold": false, + "fragment_threshold": false, + "power_management": true, + "link_quality": "58/70", + "signal_level": -52, + "signal_level_unit": "dBm", + "rx_invalid_nwid": 0, + "rx_invalid_crypt": 0, + "rx_invalid_frag": 0, + "tx_excessive_retries": 0, + "invalid_misc": 1766, + "missed_beacon": 0 + }, + { + "name": "wlp5s02", + "protocol": "IEEE 802.11", + "essid": "BLABLABLA2", + "mode": "Managed", + "frequency": 5.18, + "frequency_unit": "GHz", + "access_point": "E6:63:DA:16:50:BF", + "bit_rate": 6.0, + "bit_rate_unit": "Mb/s", + "tx_power": 30, + "tx_power_unit": "dBm", + "retry_short_limit": 7, + "rts_threshold": false, + "fragment_threshold": false, + "power_management": true, + "link_quality": "58/70", + "signal_level": -53, + "signal_level_unit": "dBm", + "rx_invalid_nwid": 41, + "rx_invalid_crypt": 42, + "rx_invalid_frag": 43, + "tx_excessive_retries": 44, + "invalid_misc": 1766, + "missed_beacon": 0 + } +] \ No newline at end of file diff --git a/tests/fixtures/generic/iwconfig-many.out b/tests/fixtures/generic/iwconfig-many.out new file mode 100644 index 00000000..b4bf15cc --- /dev/null +++ b/tests/fixtures/generic/iwconfig-many.out @@ -0,0 +1,17 @@ +wlp5s0 IEEE 802.11 ESSID:"BLABLABLA" + Mode:Managed Frequency:5.18 GHz Access Point: E6:63:DA:16:50:BF + Bit Rate=6 Mb/s Tx-Power=30 dBm + Retry short limit:7 RTS thr:off Fragment thr:off + Power Management:on + Link Quality=58/70 Signal level=-52 dBm + Rx invalid nwid:0 Rx invalid crypt:0 Rx invalid frag:0 + Tx excessive retries:0 Invalid misc:1766 Missed beacon:0 + +wlp5s02 IEEE 802.11 ESSID:"BLABLABLA2" + Mode:Managed Frequency:5.18 GHz Access Point: E6:63:DA:16:50:BF + Bit Rate=6 Mb/s Tx-Power=30 dBm + Retry short limit:7 RTS thr:off Fragment thr:off + Power Management:on + Link Quality=58/70 Signal level=-53 dBm + Rx invalid nwid:41 Rx invalid crypt:42 Rx invalid frag:43 + Tx excessive retries:44 Invalid misc:1766 Missed beacon:0 diff --git a/tests/test_iwconfig.py b/tests/test_iwconfig.py index 062d595c..b3b9fee0 100644 --- a/tests/test_iwconfig.py +++ b/tests/test_iwconfig.py @@ -12,6 +12,9 @@ class iwconfigTests(unittest.TestCase): with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig.out'), 'r', encoding='utf-8') as f: iwconfig_output = f.read() + with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig-many.out'), 'r', encoding='utf-8') as f: + iwconfig_many_output = f.read() + # output with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig.json'), 'r', encoding='utf-8') as f: iwconfig_json = json.loads(f.read()) @@ -19,6 +22,9 @@ class iwconfigTests(unittest.TestCase): with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig-raw.json'), 'r', encoding='utf-8') as f: iwconfig_raw_json = json.loads(f.read()) + with open(os.path.join(THIS_DIR, 'fixtures/generic/iwconfig-many.json'), 'r', encoding='utf-8') as f: + iwconfig_many_json = json.loads(f.read()) + def test_iwconfig_nodata(self): """ @@ -38,5 +44,11 @@ class iwconfigTests(unittest.TestCase): """ self.assertEqual(jc.parsers.iwconfig.parse(self.iwconfig_output, quiet=True), self.iwconfig_json) + def test_iwconfig_many(self): + """ + Test 'iwconfig' many interface + """ + self.assertEqual(jc.parsers.iwconfig.parse(self.iwconfig_many_output, quiet=True), self.iwconfig_many_json) + if __name__ == '__main__': unittest.main() From 33c40874fa9d9bbe0c3455277bd6ceda978dd7b7 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 09:12:09 -0800 Subject: [PATCH 07/31] version bump and add iwconfig parser --- CHANGELOG | 3 + README.md | 1 + completions/jc_bash_completion.sh | 4 +- completions/jc_zsh_completion.sh | 6 +- docs/parsers/iwconfig.md | 112 ++++++++++++++++++++++++++++++ jc/lib.py | 2 +- jc/parsers/iwconfig.py | 63 ++++++++--------- man/jc.1 | 7 +- setup.py | 2 +- 9 files changed, 162 insertions(+), 38 deletions(-) create mode 100644 docs/parsers/iwconfig.md diff --git a/CHANGELOG b/CHANGELOG index 6c71ee95..6c54d8e7 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,8 @@ jc changelog +20221223 v1.23.4 +- Add `iwconfig` command parser + 20221216 v1.22.3 - Add Common Log Format and Combined Log Format file parser (standard and streaming) - Add PostgreSQL password file parser diff --git a/README.md b/README.md index a1a7e0e5..af4a162e 100644 --- a/README.md +++ b/README.md @@ -206,6 +206,7 @@ option. | ` --ip-address` | IPv4 and IPv6 Address string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ip_address) | | ` --iptables` | `iptables` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iptables) | | ` --iw-scan` | `iw dev [device] scan` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iw_scan) | +| ` --iwconfig` | `iwconfig` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/iwconfig) | | ` --jar-manifest` | Java MANIFEST.MF file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jar_manifest) | | ` --jobs` | `jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jobs) | | ` --jwt` | JWT string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jwt) | diff --git a/completions/jc_bash_completion.sh b/completions/jc_bash_completion.sh index 6bda7edb..31a7f400 100644 --- a/completions/jc_bash_completion.sh +++ b/completions/jc_bash_completion.sh @@ -3,8 +3,8 @@ _jc() local cur prev words cword jc_commands jc_parsers jc_options \ jc_about_options jc_about_mod_options jc_help_options jc_special_options - jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo) - jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo) + jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo) + jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo) jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y) jc_about_options=(--about -a) jc_about_mod_options=(--pretty -p --yaml-out -y --monochrome -m --force-color -C) diff --git a/completions/jc_zsh_completion.sh b/completions/jc_zsh_completion.sh index dc602a29..95424afd 100644 --- a/completions/jc_zsh_completion.sh +++ b/completions/jc_zsh_completion.sh @@ -9,7 +9,7 @@ _jc() { jc_help_options jc_help_options_describe \ jc_special_options jc_special_options_describe - jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo) + jc_commands=(acpi airport arp blkid cbt chage cksum crontab date df dig dmidecode dpkg du env file findmnt finger free git gpg hciconfig id ifconfig iostat iptables iw iwconfig jobs last lastb ls lsblk lsmod lsof lspci lsusb md5 md5sum mdadm mount mpstat netstat nmcli ntpq os-prober pidstat ping ping6 pip pip3 postconf printenv ps route rpm rsync sfdisk sha1sum sha224sum sha256sum sha384sum sha512sum shasum ss sshd stat sum sysctl systemctl systeminfo timedatectl top tracepath tracepath6 traceroute traceroute6 udevadm ufw uname update-alternatives upower uptime vdir vmstat w wc who xrandr zipinfo) jc_commands_describe=( 'acpi:run "acpi" command with magic syntax.' 'airport:run "airport" command with magic syntax.' @@ -38,6 +38,7 @@ _jc() { 'iostat:run "iostat" command with magic syntax.' 'iptables:run "iptables" command with magic syntax.' 'iw:run "iw" command with magic syntax.' + 'iwconfig:run "iwconfig" command with magic syntax.' 'jobs:run "jobs" command with magic syntax.' 'last:run "last" command with magic syntax.' 'lastb:run "lastb" command with magic syntax.' @@ -101,7 +102,7 @@ _jc() { 'xrandr:run "xrandr" command with magic syntax.' 'zipinfo:run "zipinfo" command with magic syntax.' ) - jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo) + jc_parsers=(--acpi --airport --airport-s --arp --asciitable --asciitable-m --blkid --cbt --cef --cef-s --chage --cksum --clf --clf-s --crontab --crontab-u --csv --csv-s --date --datetime-iso --df --dig --dir --dmidecode --dpkg-l --du --email-address --env --file --findmnt --finger --free --fstab --git-log --git-log-s --git-ls-remote --gpg --group --gshadow --hash --hashsum --hciconfig --history --hosts --id --ifconfig --ini --iostat --iostat-s --ip-address --iptables --iw-scan --iwconfig --jar-manifest --jobs --jwt --kv --last --ls --ls-s --lsblk --lsmod --lsof --lspci --lsusb --m3u --mdadm --mount --mpstat --mpstat-s --netstat --nmcli --ntpq --openvpn --os-prober --passwd --pci-ids --pgpass --pidstat --pidstat-s --ping --ping-s --pip-list --pip-show --plist --postconf --proc --proc-buddyinfo --proc-consoles --proc-cpuinfo --proc-crypto --proc-devices --proc-diskstats --proc-filesystems --proc-interrupts --proc-iomem --proc-ioports --proc-loadavg --proc-locks --proc-meminfo --proc-modules --proc-mtrr --proc-pagetypeinfo --proc-partitions --proc-slabinfo --proc-softirqs --proc-stat --proc-swaps --proc-uptime --proc-version --proc-vmallocinfo --proc-vmstat --proc-zoneinfo --proc-driver-rtc --proc-net-arp --proc-net-dev --proc-net-dev-mcast --proc-net-if-inet6 --proc-net-igmp --proc-net-igmp6 --proc-net-ipv6-route --proc-net-netlink --proc-net-netstat --proc-net-packet --proc-net-protocols --proc-net-route --proc-net-unix --proc-pid-fdinfo --proc-pid-io --proc-pid-maps --proc-pid-mountinfo --proc-pid-numa-maps --proc-pid-smaps --proc-pid-stat --proc-pid-statm --proc-pid-status --ps --route --rpm-qi --rsync --rsync-s --semver --sfdisk --shadow --ss --sshd-conf --stat --stat-s --sysctl --syslog --syslog-s --syslog-bsd --syslog-bsd-s --systemctl --systemctl-lj --systemctl-ls --systemctl-luf --systeminfo --time --timedatectl --timestamp --top --top-s --tracepath --traceroute --udevadm --ufw --ufw-appinfo --uname --update-alt-gs --update-alt-q --upower --uptime --url --vmstat --vmstat-s --w --wc --who --x509-cert --xml --xrandr --yaml --zipinfo) jc_parsers_describe=( '--acpi:`acpi` command parser' '--airport:`airport -I` command parser' @@ -155,6 +156,7 @@ _jc() { '--ip-address:IPv4 and IPv6 Address string parser' '--iptables:`iptables` command parser' '--iw-scan:`iw dev [device] scan` command parser' + '--iwconfig:`iwconfig` command parser' '--jar-manifest:Java MANIFEST.MF file parser' '--jobs:`jobs` command parser' '--jwt:JWT string parser' diff --git a/docs/parsers/iwconfig.md b/docs/parsers/iwconfig.md new file mode 100644 index 00000000..1f0ef642 --- /dev/null +++ b/docs/parsers/iwconfig.md @@ -0,0 +1,112 @@ +[Home](https://kellyjonbrazil.github.io/jc/) + + +# jc.parsers.iwconfig + +jc - JSON Convert `iwconfig` command output parser + +No `iwconfig` options are supported. + +Usage (cli): + + $ iwconfig | jc --iwconfig + +or + + $ jc iwconfig + +Usage (module): + + import jc + result = jc.parse('iwconfig', iwconfig_command_output) + +Schema: + + [ + { + "name": string, + "protocol": string, + "essid": string, + "mode": string, + "frequency": float, + "frequency_unit": string, + "access_point": string, + "bit_rate": float, + "bit_rate_unit": string, + "tx_power": integer, + "tx_power_unit": string, + "retry_short_limit": integer, + "rts_threshold": boolean, + "fragment_threshold": boolean, + "power_management": boolean, + "link_quality": string, + "signal_level": integer, + "signal_level_unit": string, + "rx_invalid_nwid": integer, + "rx_invalid_crypt": integer, + "rx_invalid_frag": integer, + "tx_excessive_retries": integer, + "invalid_misc": integer, + "missed_beacon": integer + } + ] + + +Examples: + + $ iwconfig | jc --iwconfig -p + [ + { + "name": "wlp5s0", + "protocol": "IEEE 802.11", + "essid": "BLABLABLA", + "mode": "Managed", + "frequency": 5.18, + "frequency_unit": "GHz", + "access_point": "E6:64:DA:16:51:BF", + "bit_rate": 6.0, + "bit_rate_unit": "Mb/s", + "tx_power": 30, + "tx_power_unit": "dBm", + "retry_short_limit": 7, + "rts_threshold": false, + "fragment_threshold": false, + "power_management": true, + "link_quality": "61/70", + "signal_level": -49, + "signal_level_unit": "dBm", + "rx_invalid_nwid": 0, + "rx_invalid_crypt": 0, + "rx_invalid_frag": 0, + "tx_excessive_retries": 0, + "invalid_misc": 2095, + "missed_beacon": 0 + } + ] + + + +### parse + +```python +def parse(data: str, + raw: bool = False, + quiet: bool = False) -> List[JSONDictType] +``` + +Main text parsing function + +Parameters: + + data: (string) text data to parse + raw: (boolean) unprocessed output if True + quiet: (boolean) suppress warning messages if True + +Returns: + + List of Dictionaries. Raw or processed structured data. + +### Parser Information +Compatibility: linux + +Version 1.0 by Thomas Vincent (vrince@gmail.com) diff --git a/jc/lib.py b/jc/lib.py index ca02f5e1..e0eca5f3 100644 --- a/jc/lib.py +++ b/jc/lib.py @@ -9,7 +9,7 @@ from .jc_types import ParserInfoType, JSONDictType from jc import appdirs -__version__ = '1.22.3' +__version__ = '1.22.4' parsers: List[str] = [ 'acpi', diff --git a/jc/parsers/iwconfig.py b/jc/parsers/iwconfig.py index 138f7d8f..c380dd94 100644 --- a/jc/parsers/iwconfig.py +++ b/jc/parsers/iwconfig.py @@ -19,37 +19,37 @@ Schema: [ { - "name": string, - "protocol": string, - "essid": string, - "mode": string, - "frequency": float, - "frequency_unit": string, - "access_point": string, - "bit_rate": float, - "bit_rate_unit": string, - "tx_power": integer, - "tx_power_unit": string, - "retry_short_limit": integer, - "rts_threshold": boolean, - "fragment_threshold": boolean, - "power_management": boolean, - "link_quality": string, - "signal_level": integer, - "signal_level_unit": string, - "rx_invalid_nwid": integer, - "rx_invalid_crypt": integer, - "rx_invalid_frag": integer, - "tx_excessive_retries": integer, - "invalid_misc": integer, - "missed_beacon": integer + "name": string, + "protocol": string, + "essid": string, + "mode": string, + "frequency": float, + "frequency_unit": string, + "access_point": string, + "bit_rate": float, + "bit_rate_unit": string, + "tx_power": integer, + "tx_power_unit": string, + "retry_short_limit": integer, + "rts_threshold": boolean, + "fragment_threshold": boolean, + "power_management": boolean, + "link_quality": string, + "signal_level": integer, + "signal_level_unit": string, + "rx_invalid_nwid": integer, + "rx_invalid_crypt": integer, + "rx_invalid_frag": integer, + "tx_excessive_retries": integer, + "invalid_misc": integer, + "missed_beacon": integer } ] Examples: - $ iwconfig 2> /dev/null | jc --iwconfig -p + $ iwconfig | jc --iwconfig -p [ { "name": "wlp5s0", @@ -110,18 +110,20 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: List of Dictionaries. Structured to conform to the schema. """ - int_list = ['signal_level', 'rx_invalid_nwid', 'rx_invalid_crypt', 'rx_invalid_frag', - 'tx_excessive_retries', 'invalid_misc', 'missed_beacon', 'tx_power', 'retry_short_limit'] + int_list = [ + 'signal_level', 'rx_invalid_nwid', 'rx_invalid_crypt', 'rx_invalid_frag', + 'tx_excessive_retries', 'invalid_misc', 'missed_beacon', 'tx_power', 'retry_short_limit' + ] float_list = ['frequency', 'bit_rate'] bool_list = ['rts_threshold', 'fragment_threshold', 'power_management'] - proc_data = [ { key: int(value) if key in int_list else value for key, value in proc_data_item.items() } for proc_data_item in proc_data ] proc_data = [ { key: float(value) if key in float_list else value for key, value in proc_data_item.items() } for proc_data_item in proc_data ] proc_data = [ { key: value == 'on' if key in bool_list else value for key, value in proc_data_item .items() } for proc_data_item in proc_data ] return proc_data + def parse( data: str, raw: bool = False, @@ -144,7 +146,6 @@ def parse( jc.utils.input_type_check(data) raw_output: List[Dict] = [] - wireless_extension_obj: Dict = {} re_interface = re.compile(r'^(?P[a-zA-Z0-9:._-]+)\s+(?P([a-zA-Z0-9]+\s)*[a-zA-Z0-9.]+)\s+ESSID:\"(?P[a-zA-Z0-9:._\s]+)\"') re_mode = re.compile(r'Mode:(?P\w+)') @@ -185,10 +186,10 @@ def parse( interface_item = dict() interface_item.update(interface_match.groupdict()) continue - + # we do not have any interface yet continue to search for it --> next line if interface_item is None: - continue + continue # Filling interface with whatever we can find for re_entry in re_all: diff --git a/man/jc.1 b/man/jc.1 index 9c65cd60..09f5bdb3 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2022-12-16 1.22.3 "JSON Convert" +.TH jc 1 2022-12-23 1.22.4 "JSON Convert" .SH NAME \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings .SH SYNOPSIS @@ -295,6 +295,11 @@ Deprecated - please use datetime-iso \fB--iw-scan\fP `iw dev [device] scan` command parser +.TP +.B +\fB--iwconfig\fP +`iwconfig` command parser + .TP .B \fB--jar-manifest\fP diff --git a/setup.py b/setup.py index dfdf1c96..2d9a8b4f 100755 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ with open('README.md', 'r') as f: setuptools.setup( name='jc', - version='1.22.3', + version='1.22.4', author='Kelly Brazil', author_email='kellyjonbrazil@gmail.com', description='Converts the output of popular command-line tools and file-types to JSON.', From 04a2af86813cbdcb045ce07e1792965b94ed1936 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 09:14:19 -0800 Subject: [PATCH 08/31] add python 3.6 to tests --- .github/workflows/pythonapp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index 024eb065..e0acff36 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -14,7 +14,7 @@ jobs: strategy: matrix: os: [macos-latest, ubuntu-latest, windows-latest] - python-version: ["3.7", "3.8", "3.9", "3.10", "3.11"] + python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] steps: - uses: actions/checkout@v3 From 55480c059a760ca49150e1677bcc27863cdd4caa Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 09:17:42 -0800 Subject: [PATCH 09/31] python 3.6 compatibility: remove re.Match type annotation --- jc/parsers/findmnt.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/findmnt.py b/jc/parsers/findmnt.py index 3b32344a..699e7b38 100644 --- a/jc/parsers/findmnt.py +++ b/jc/parsers/findmnt.py @@ -140,7 +140,7 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: return proc_data -def _replace(matchobj: re.Match) -> str: +def _replace(matchobj): if matchobj: matchlen = len(matchobj.group(1)) return ' ' * matchlen + '/' From 5534a1c755833b1deb300a344e8a0d28f2e3f7fa Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 09:20:32 -0800 Subject: [PATCH 10/31] change to ubuntu-20.04 for python 3.6 tests --- .github/workflows/pythonapp.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pythonapp.yml b/.github/workflows/pythonapp.yml index e0acff36..7d6ae2c3 100644 --- a/.github/workflows/pythonapp.yml +++ b/.github/workflows/pythonapp.yml @@ -13,7 +13,7 @@ jobs: runs-on: ${{ matrix.os }} strategy: matrix: - os: [macos-latest, ubuntu-latest, windows-latest] + os: [macos-latest, ubuntu-20.04, windows-latest] python-version: ["3.6", "3.7", "3.8", "3.9", "3.10", "3.11"] steps: From a476e4639b4bec21208e9ef20b424bf00ddccf62 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 09:21:27 -0800 Subject: [PATCH 11/31] force test --- jc/parsers/findmnt.py | 1 + 1 file changed, 1 insertion(+) diff --git a/jc/parsers/findmnt.py b/jc/parsers/findmnt.py index 699e7b38..241cc11e 100644 --- a/jc/parsers/findmnt.py +++ b/jc/parsers/findmnt.py @@ -140,6 +140,7 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: return proc_data + def _replace(matchobj): if matchobj: matchlen = len(matchobj.group(1)) From edcca4fb961c9a49c7ae50392130a742af2a0bbe Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 09:23:51 -0800 Subject: [PATCH 12/31] remove re type annotations for python 3.6 compatibility --- jc/parsers/findmnt.py | 1 - jc/parsers/ifconfig.py | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/jc/parsers/findmnt.py b/jc/parsers/findmnt.py index 241cc11e..699e7b38 100644 --- a/jc/parsers/findmnt.py +++ b/jc/parsers/findmnt.py @@ -140,7 +140,6 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: return proc_data - def _replace(matchobj): if matchobj: matchlen = len(matchobj.group(1)) diff --git a/jc/parsers/ifconfig.py b/jc/parsers/ifconfig.py index 1daa3f42..8f0f719f 100644 --- a/jc/parsers/ifconfig.py +++ b/jc/parsers/ifconfig.py @@ -326,7 +326,7 @@ def _process(proc_data: List[JSONDictType]) -> List[JSONDictType]: return proc_data -def _bundle_match(pattern_list: List[re.Pattern], string: str) -> Optional[re.Match]: +def _bundle_match(pattern_list, string): """Returns a match object if a string matches one of a list of patterns. If no match is found, returns None""" for pattern in pattern_list: From 44d1d52426714e2c4014cae755ea1a55645727c7 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 09:40:26 -0800 Subject: [PATCH 13/31] fix test for python 3.6 --- tests/test_jc_utils.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/test_jc_utils.py b/tests/test_jc_utils.py index e32263b3..bfd051e7 100644 --- a/tests/test_jc_utils.py +++ b/tests/test_jc_utils.py @@ -79,6 +79,14 @@ class MyTests(unittest.TestCase): None: {'string': None, 'format': None, 'naive': None, 'utc': None} } + # fixup for change in behavior after python 3.6: + # Changed in version 3.7: When the %z directive is provided to the strptime() method, + # the UTC offsets can have a colon as a separator between hours, minutes and seconds. + # For example, '+01:00:00' will be parsed as an offset of one hour. In addition, + # providing 'Z' is identical to '+00:00'. + if sys.version_info < (3, 7, 0): + del datetime_map['2000/01/01-01:00:00.000000+00:00'] + for input_string, expected_output in datetime_map.items(): ts = jc.utils.timestamp(input_string) ts_dict = { From ba371ec730e5e262d3fc3f2206492e1deb78fd87 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 10:08:19 -0800 Subject: [PATCH 14/31] fix proc signature for pid_stat so hack comm names with parenthesis are handled correctly. --- jc/parsers/proc.py | 2 +- tests/fixtures/linux-proc/pid_stat_hack | 1 + tests/fixtures/linux-proc/pid_stat_hack.json | 1 + tests/test_proc.py | 3 +++ tests/test_proc_pid_stat.py | 12 +++++++++++- 5 files changed, 17 insertions(+), 2 deletions(-) create mode 100644 tests/fixtures/linux-proc/pid_stat_hack create mode 100644 tests/fixtures/linux-proc/pid_stat_hack.json diff --git a/jc/parsers/proc.py b/jc/parsers/proc.py index 147a201c..4d81bb1b 100644 --- a/jc/parsers/proc.py +++ b/jc/parsers/proc.py @@ -201,7 +201,7 @@ def parse( pid_mountinfo_p = re.compile(r'^\d+ \d+ \d+:\d+ /.+\n') pid_numa_maps_p = re.compile(r'^[a-f0-9]{12} default [^\n]+\n') pid_smaps_p = re.compile(r'^[0-9a-f]{12}-[0-9a-f]{12} [rwxsp\-]{4} [0-9a-f]{8} [0-9a-f]{2}:[0-9a-f]{2} \d+ [^\n]+\nSize:\s+\d+ \S\S\n') - pid_stat_p = re.compile(r'^\d+ \(.{1,15}\) \S \d+ \d+ \d+ \d+ -?\d+ (?:\d+ ){43}\d+$', re.DOTALL) + pid_stat_p = re.compile(r'^\d+ \(.+\) \S \d+ \d+ \d+ \d+ -?\d+ (?:\d+ ){43}\d+$', re.DOTALL) pid_statm_p = re.compile(r'^\d+ \d+ \d+\s\d+\s\d+\s\d+\s\d+$') pid_status_p = re.compile(r'^Name:\t.+\nUmask:\t\d+\nState:\t.+\nTgid:\t\d+\n') diff --git a/tests/fixtures/linux-proc/pid_stat_hack b/tests/fixtures/linux-proc/pid_stat_hack new file mode 100644 index 00000000..2f93dc20 --- /dev/null +++ b/tests/fixtures/linux-proc/pid_stat_hack @@ -0,0 +1 @@ +2001 (bad) S 1 2 3 4 5) S 1888 2001 1888 34816 2001 4202496 428 0 0 0 0 0 0 0 20 0 1 0 75513 115900416 297 18446744073709551615 4194304 5100612 140737020052256 140737020050904 140096699233308 0 65536 4 65538 18446744072034584486 0 0 17 0 0 0 0 0 0 7200240 7236240 35389440 140737020057179 140737020057223 140737020057223 140737020059606 0 diff --git a/tests/fixtures/linux-proc/pid_stat_hack.json b/tests/fixtures/linux-proc/pid_stat_hack.json new file mode 100644 index 00000000..9303b703 --- /dev/null +++ b/tests/fixtures/linux-proc/pid_stat_hack.json @@ -0,0 +1 @@ +{"pid":2001,"comm":"bad) S 1 2 3 4 5","state":"S","ppid":1888,"pgrp":2001,"session":1888,"tty_nr":34816,"tpg_id":2001,"flags":4202496,"minflt":428,"cminflt":0,"majflt":0,"cmajflt":0,"utime":0,"stime":0,"cutime":0,"cstime":0,"priority":20,"nice":0,"num_threads":1,"itrealvalue":0,"starttime":75513,"vsize":115900416,"rss":297,"rsslim":18446744073709551615,"startcode":4194304,"endcode":5100612,"startstack":140737020052256,"kstkeep":140737020050904,"kstkeip":140096699233308,"signal":0,"blocked":65536,"sigignore":4,"sigcatch":65538,"wchan":18446744072034584486,"nswap":0,"cnswap":0,"exit_signal":17,"processor":0,"rt_priority":0,"policy":0,"delayacct_blkio_ticks":0,"guest_time":0,"cguest_time":0,"start_data":7200240,"end_data":7236240,"start_brk":35389440,"arg_start":140737020057179,"arg_end":140737020057223,"env_start":140737020057223,"env_end":140737020059606,"exit_code":0,"state_pretty":"Sleeping in an interruptible wait"} diff --git a/tests/test_proc.py b/tests/test_proc.py index db55e0fe..6825c628 100644 --- a/tests/test_proc.py +++ b/tests/test_proc.py @@ -206,6 +206,9 @@ class MyTests(unittest.TestCase): 'pid_stat_w_space_and_nl_in_comm': ( 'fixtures/linux-proc/pid_stat_w_space_and_nl_in_comm', 'fixtures/linux-proc/pid_stat_w_space_and_nl_in_comm.json'), + 'proc_pid_stat_hack': ( + 'fixtures/linux-proc/pid_stat_hack', + 'fixtures/linux-proc/pid_stat_hack.json'), 'proc_pid_statm': ( 'fixtures/linux-proc/pid_statm', 'fixtures/linux-proc/pid_statm.json'), diff --git a/tests/test_proc_pid_stat.py b/tests/test_proc_pid_stat.py index 4201abf8..fcb67ca9 100644 --- a/tests/test_proc_pid_stat.py +++ b/tests/test_proc_pid_stat.py @@ -19,7 +19,10 @@ class MyTests(unittest.TestCase): 'fixtures/linux-proc/pid_stat.json'), 'pid_stat_w_space_and_nl_in_comm': ( 'fixtures/linux-proc/pid_stat_w_space_and_nl_in_comm', - 'fixtures/linux-proc/pid_stat_w_space_and_nl_in_comm.json') + 'fixtures/linux-proc/pid_stat_w_space_and_nl_in_comm.json'), + 'pid_stat_hack': ( + 'fixtures/linux-proc/pid_stat_hack', + 'fixtures/linux-proc/pid_stat_hack.json') } for file, filepaths in fixtures.items(): @@ -49,6 +52,13 @@ class MyTests(unittest.TestCase): self.assertEqual(jc.parsers.proc_pid_stat.parse(self.f_in['pid_stat_w_space_and_nl_in_comm'], quiet=True), self.f_json['pid_stat_w_space_and_nl_in_comm']) + def test_proc_pid_stat_hack(self): + """ + Test '/proc//stat' with evil command hack + """ + self.assertEqual(jc.parsers.proc_pid_stat.parse(self.f_in['pid_stat_hack'], quiet=True), + self.f_json['pid_stat_hack']) + if __name__ == '__main__': unittest.main() From 6674f549f2e3bbef07e7d82612c3983273d9060a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 23 Dec 2022 10:14:19 -0800 Subject: [PATCH 15/31] version bumps and doc update --- CHANGELOG | 3 +++ docs/parsers/findmnt.md | 2 +- docs/parsers/ifconfig.md | 2 +- docs/parsers/proc.md | 2 +- jc/parsers/findmnt.py | 2 +- jc/parsers/ifconfig.py | 2 +- jc/parsers/proc.py | 2 +- 7 files changed, 9 insertions(+), 6 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 6c54d8e7..c8c8bcf8 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -2,6 +2,9 @@ jc changelog 20221223 v1.23.4 - Add `iwconfig` command parser +- Fix `proc` parser magic signature detection for `/proc/pid/stat` hacks +- Fix python 3.6-related issues +- Add python 3.6 to automated tests 20221216 v1.22.3 - Add Common Log Format and Combined Log Format file parser (standard and streaming) diff --git a/docs/parsers/findmnt.md b/docs/parsers/findmnt.md index 40f65812..a2206de1 100644 --- a/docs/parsers/findmnt.md +++ b/docs/parsers/findmnt.md @@ -114,4 +114,4 @@ Returns: ### Parser Information Compatibility: linux -Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/docs/parsers/ifconfig.md b/docs/parsers/ifconfig.md index 0d345c9f..7e806e82 100644 --- a/docs/parsers/ifconfig.md +++ b/docs/parsers/ifconfig.md @@ -240,4 +240,4 @@ Returns: ### Parser Information Compatibility: linux, aix, freebsd, darwin -Version 2.1 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 2.2 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/docs/parsers/proc.md b/docs/parsers/proc.md index 19e14f34..f54e2436 100644 --- a/docs/parsers/proc.md +++ b/docs/parsers/proc.md @@ -139,4 +139,4 @@ Returns: ### Parser Information Compatibility: linux -Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/jc/parsers/findmnt.py b/jc/parsers/findmnt.py index 699e7b38..8ae88a4e 100644 --- a/jc/parsers/findmnt.py +++ b/jc/parsers/findmnt.py @@ -93,7 +93,7 @@ import jc.utils class info(): """Provides parser metadata (version, author, etc.)""" - version = '1.0' + version = '1.1' description = '`findmnt` command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/ifconfig.py b/jc/parsers/ifconfig.py index 8f0f719f..83ed94e0 100644 --- a/jc/parsers/ifconfig.py +++ b/jc/parsers/ifconfig.py @@ -219,7 +219,7 @@ import jc.utils class info(): """Provides parser metadata (version, author, etc.)""" - version = '2.1' + version = '2.2' description = '`ifconfig` command parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/jc/parsers/proc.py b/jc/parsers/proc.py index 4d81bb1b..5ac5013a 100644 --- a/jc/parsers/proc.py +++ b/jc/parsers/proc.py @@ -120,7 +120,7 @@ from jc.exceptions import ParseError class info(): """Provides parser metadata (version, author, etc.)""" - version = '1.0' + version = '1.1' description = '`/proc/` file parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' From 68717e849327496c38ed4f422a11976dc1f76f7f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 27 Dec 2022 13:59:10 -0800 Subject: [PATCH 16/31] add parser metadata tags and category view to help --- CHANGELOG | 4 ++- jc/cli.py | 60 ++++++++++++++++++++++++++----- jc/cli_data.py | 5 +-- jc/jc_types.py | 1 + jc/parsers/acpi.py | 1 + jc/parsers/airport.py | 1 + jc/parsers/airport_s.py | 1 + jc/parsers/arp.py | 1 + jc/parsers/asciitable.py | 1 + jc/parsers/asciitable_m.py | 1 + jc/parsers/blkid.py | 1 + jc/parsers/cbt.py | 1 + jc/parsers/cef.py | 2 ++ jc/parsers/cef_s.py | 1 + jc/parsers/chage.py | 1 + jc/parsers/cksum.py | 1 + jc/parsers/clf.py | 1 + jc/parsers/clf_s.py | 1 + jc/parsers/crontab.py | 1 + jc/parsers/crontab_u.py | 1 + jc/parsers/csv.py | 1 + jc/parsers/csv_s.py | 1 + jc/parsers/date.py | 1 + jc/parsers/datetime_iso.py | 1 + jc/parsers/df.py | 1 + jc/parsers/dig.py | 1 + jc/parsers/dir.py | 1 + jc/parsers/dmidecode.py | 1 + jc/parsers/dpkg_l.py | 1 + jc/parsers/du.py | 1 + jc/parsers/email_address.py | 1 + jc/parsers/env.py | 1 + jc/parsers/file.py | 1 + jc/parsers/findmnt.py | 1 + jc/parsers/finger.py | 1 + jc/parsers/foo.py | 3 ++ jc/parsers/foo_s.py | 3 ++ jc/parsers/free.py | 1 + jc/parsers/fstab.py | 1 + jc/parsers/git_log.py | 1 + jc/parsers/git_log_s.py | 1 + jc/parsers/git_ls_remote.py | 1 + jc/parsers/gpg.py | 1 + jc/parsers/group.py | 1 + jc/parsers/gshadow.py | 1 + jc/parsers/hash.py | 1 + jc/parsers/hashsum.py | 1 + jc/parsers/hciconfig.py | 1 + jc/parsers/history.py | 1 + jc/parsers/hosts.py | 1 + jc/parsers/id.py | 1 + jc/parsers/ifconfig.py | 1 + jc/parsers/ini.py | 1 + jc/parsers/iostat.py | 1 + jc/parsers/iostat_s.py | 1 + jc/parsers/ip_address.py | 1 + jc/parsers/iptables.py | 1 + jc/parsers/iso_datetime.py | 1 + jc/parsers/iw_scan.py | 1 + jc/parsers/iwconfig.py | 1 + jc/parsers/jar_manifest.py | 1 + jc/parsers/jobs.py | 1 + jc/parsers/jwt.py | 1 + jc/parsers/kv.py | 5 +-- jc/parsers/last.py | 1 + jc/parsers/ls.py | 1 + jc/parsers/ls_s.py | 1 + jc/parsers/lsblk.py | 1 + jc/parsers/lsmod.py | 1 + jc/parsers/lsof.py | 1 + jc/parsers/lspci.py | 1 + jc/parsers/lsusb.py | 1 + jc/parsers/m3u.py | 1 + jc/parsers/mdadm.py | 1 + jc/parsers/mount.py | 1 + jc/parsers/mpstat.py | 1 + jc/parsers/mpstat_s.py | 1 + jc/parsers/netstat.py | 1 + jc/parsers/nmcli.py | 1 + jc/parsers/ntpq.py | 1 + jc/parsers/openvpn.py | 1 + jc/parsers/os_prober.py | 1 + jc/parsers/passwd.py | 1 + jc/parsers/pci_ids.py | 1 + jc/parsers/pgpass.py | 1 + jc/parsers/pidstat.py | 1 + jc/parsers/pidstat_s.py | 1 + jc/parsers/ping.py | 1 + jc/parsers/ping_s.py | 1 + jc/parsers/pip_list.py | 1 + jc/parsers/pip_show.py | 1 + jc/parsers/plist.py | 1 + jc/parsers/postconf.py | 1 + jc/parsers/proc.py | 1 + jc/parsers/proc_buddyinfo.py | 1 + jc/parsers/proc_consoles.py | 1 + jc/parsers/proc_cpuinfo.py | 1 + jc/parsers/proc_crypto.py | 1 + jc/parsers/proc_devices.py | 1 + jc/parsers/proc_diskstats.py | 1 + jc/parsers/proc_driver_rtc.py | 1 + jc/parsers/proc_filesystems.py | 1 + jc/parsers/proc_interrupts.py | 1 + jc/parsers/proc_iomem.py | 1 + jc/parsers/proc_ioports.py | 1 + jc/parsers/proc_loadavg.py | 1 + jc/parsers/proc_locks.py | 1 + jc/parsers/proc_meminfo.py | 1 + jc/parsers/proc_modules.py | 1 + jc/parsers/proc_mtrr.py | 1 + jc/parsers/proc_net_arp.py | 1 + jc/parsers/proc_net_dev.py | 1 + jc/parsers/proc_net_dev_mcast.py | 1 + jc/parsers/proc_net_if_inet6.py | 1 + jc/parsers/proc_net_igmp.py | 1 + jc/parsers/proc_net_igmp6.py | 1 + jc/parsers/proc_net_ipv6_route.py | 1 + jc/parsers/proc_net_netlink.py | 1 + jc/parsers/proc_net_netstat.py | 1 + jc/parsers/proc_net_packet.py | 1 + jc/parsers/proc_net_protocols.py | 1 + jc/parsers/proc_net_route.py | 1 + jc/parsers/proc_net_unix.py | 1 + jc/parsers/proc_pagetypeinfo.py | 1 + jc/parsers/proc_partitions.py | 1 + jc/parsers/proc_pid_fdinfo.py | 1 + jc/parsers/proc_pid_io.py | 1 + jc/parsers/proc_pid_maps.py | 1 + jc/parsers/proc_pid_mountinfo.py | 1 + jc/parsers/proc_pid_numa_maps.py | 1 + jc/parsers/proc_pid_smaps.py | 1 + jc/parsers/proc_pid_stat.py | 1 + jc/parsers/proc_pid_statm.py | 1 + jc/parsers/proc_pid_status.py | 1 + jc/parsers/proc_slabinfo.py | 1 + jc/parsers/proc_softirqs.py | 1 + jc/parsers/proc_stat.py | 1 + jc/parsers/proc_swaps.py | 1 + jc/parsers/proc_uptime.py | 1 + jc/parsers/proc_version.py | 1 + jc/parsers/proc_vmallocinfo.py | 1 + jc/parsers/proc_vmstat.py | 1 + jc/parsers/proc_zoneinfo.py | 1 + jc/parsers/ps.py | 1 + jc/parsers/route.py | 1 + jc/parsers/rpm_qi.py | 1 + jc/parsers/rsync.py | 1 + jc/parsers/rsync_s.py | 1 + jc/parsers/semver.py | 1 + jc/parsers/sfdisk.py | 1 + jc/parsers/shadow.py | 1 + jc/parsers/ss.py | 1 + jc/parsers/sshd_conf.py | 1 + jc/parsers/stat.py | 1 + jc/parsers/stat_s.py | 1 + jc/parsers/sysctl.py | 1 + jc/parsers/syslog.py | 2 ++ jc/parsers/syslog_bsd.py | 2 ++ jc/parsers/syslog_bsd_s.py | 1 + jc/parsers/syslog_s.py | 1 + jc/parsers/systemctl.py | 1 + jc/parsers/systemctl_lj.py | 1 + jc/parsers/systemctl_ls.py | 1 + jc/parsers/systemctl_luf.py | 1 + jc/parsers/systeminfo.py | 1 + jc/parsers/time.py | 1 + jc/parsers/timedatectl.py | 1 + jc/parsers/timestamp.py | 1 + jc/parsers/top.py | 1 + jc/parsers/top_s.py | 1 + jc/parsers/tracepath.py | 1 + jc/parsers/traceroute.py | 1 + jc/parsers/udevadm.py | 1 + jc/parsers/ufw.py | 1 + jc/parsers/ufw_appinfo.py | 1 + jc/parsers/uname.py | 1 + jc/parsers/update_alt_gs.py | 1 + jc/parsers/update_alt_q.py | 1 + jc/parsers/upower.py | 1 + jc/parsers/uptime.py | 1 + jc/parsers/url.py | 1 + jc/parsers/vmstat.py | 1 + jc/parsers/vmstat_s.py | 1 + jc/parsers/w.py | 1 + jc/parsers/wc.py | 1 + jc/parsers/who.py | 1 + jc/parsers/x509_cert.py | 1 + jc/parsers/xml.py | 1 + jc/parsers/xrandr.py | 4 +-- jc/parsers/yaml.py | 1 + jc/parsers/zipinfo.py | 1 + 191 files changed, 254 insertions(+), 17 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index c8c8bcf8..12971cdd 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,10 @@ jc changelog -20221223 v1.23.4 +20221227 v1.23.4 - Add `iwconfig` command parser - Fix `proc` parser magic signature detection for `/proc/pid/stat` hacks +- Add category tags to parser metadata +- Add "list parsers by category" view to help - Fix python 3.6-related issues - Add python 3.6 to automated tests diff --git a/jc/cli.py b/jc/cli.py index 8525f6dd..c00aec29 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -9,7 +9,7 @@ from datetime import datetime, timezone import textwrap import shlex import subprocess -from typing import List, Union, Optional, TextIO +from typing import List, Dict, Union, Optional, TextIO from types import ModuleType from .lib import ( __version__, parser_info, all_parser_info, parsers, _get_parser, _parser_is_streaming, @@ -64,12 +64,12 @@ if PYGMENTS_INSTALLED: class JcCli(): __slots__ = ( 'data_in', 'data_out', 'options', 'args', 'parser_module', 'parser_name', 'indent', 'pad', - 'custom_colors', 'show_hidden', 'ascii_only', 'json_separators', 'json_indent', - 'run_timestamp', 'about', 'debug', 'verbose_debug', 'force_color', 'mono', 'help_me', - 'pretty', 'quiet', 'ignore_exceptions', 'raw', 'meta_out', 'unbuffer', 'version_info', - 'yaml_output', 'bash_comp', 'zsh_comp', 'magic_found_parser', 'magic_options', - 'magic_run_command', 'magic_run_command_str', 'magic_stdout', 'magic_stderr', - 'magic_returncode' + 'custom_colors', 'show_hidden', 'show_categories', 'ascii_only', 'json_separators', + 'json_indent', 'run_timestamp', 'about', 'debug', 'verbose_debug', 'force_color', 'mono', + 'help_me', 'pretty', 'quiet', 'ignore_exceptions', 'raw', 'meta_out', 'unbuffer', + 'version_info', 'yaml_output', 'bash_comp', 'zsh_comp', 'magic_found_parser', + 'magic_options', 'magic_run_command', 'magic_run_command_str', 'magic_stdout', + 'magic_stderr', 'magic_returncode' ) def __init__(self) -> None: @@ -83,6 +83,7 @@ class JcCli(): self.pad: int = 0 self.custom_colors: CustomColorType = {} self.show_hidden: bool = False + self.show_categories: bool = False self.ascii_only: bool = False self.json_separators: Optional[tuple[str, str]] = (',', ':') self.json_indent: Optional[int] = None @@ -198,6 +199,41 @@ class JcCli(): return ptext + def parser_categories_text(self) -> str: + """Return lists of parsers by category""" + category_text: str = '' + padding_char: str = ' ' + all_parsers = all_parser_info(show_hidden=True, show_deprecated=False) + generic = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'generic' in x['tags']] + standard = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'standard' in x['tags']] + command = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if 'command' in x['tags']] + file_str_bin = [ + {'arg': x['argument'], 'desc': x['description']} for x in all_parsers + if 'file' in x['tags'] or + 'string' in x['tags'] or + 'binary' in x['tags'] + ] + streaming = [{'arg': x['argument'], 'desc': x['description']} for x in all_parsers if x.get('streaming')] + categories: Dict = { + 'Generic Parsers:': generic, + 'Standard Spec Parsers:': standard, + 'File/String/Binary Parsers:': file_str_bin, + 'Streaming Parsers:': streaming, + 'Command Parsers:': command + } + + for cat, cat_objs in categories.items(): + category_text += f'{cat} ({len(cat_objs)})\n' + for p in cat_objs: + parser_arg: str = p.get('arg', 'UNKNOWN') + parser_desc: str = p.get('desc', 'No description available.') + padding: int = self.pad - len(parser_arg) + padding_text: str = padding_char * padding + category_text += f'{parser_arg}{padding_text}{parser_desc}\n' + category_text += '\n' + + return category_text[:-1] + def options_text(self) -> str: """Return the argument and description information from each option""" otext: str = '' @@ -236,8 +272,6 @@ class JcCli(): def helptext(self) -> str: """Return the help text with the list of parsers""" - self.indent = 4 - self.pad = 20 parsers_string: str = self.parsers_text() options_string: str = self.options_text() helptext_string: str = f'{helptext_preamble_string}{parsers_string}\nOptions:\n{options_string}\n{helptext_end_string}' @@ -248,6 +282,13 @@ class JcCli(): Pages the parser documentation if a parser is found in the arguments, otherwise the general help text is printed. """ + self.indent = 4 + self.pad = 20 + + if self.show_categories: + utils._safe_print(self.parser_categories_text()) + return + for arg in self.args: parser_name: str = self.parser_shortname(arg) @@ -655,6 +696,7 @@ class JcCli(): self.force_color = 'C' in self.options self.help_me = 'h' in self.options self.show_hidden = self.options.count('h') > 1 # verbose help + self.show_categories = self.options.count('h') > 2 self.pretty = 'p' in self.options self.quiet = 'q' in self.options self.ignore_exceptions = self.options.count('q') > 1 diff --git a/jc/cli_data.py b/jc/cli_data.py index 9dd61a65..6311d97f 100644 --- a/jc/cli_data.py +++ b/jc/cli_data.py @@ -91,6 +91,7 @@ Examples: Parser Documentation: $ jc --help --dig - Show Hidden Parsers: - $ jc -hh + More Help: + $ jc -hh # show hidden parsers + $ jc -hhh # list parsers by category ''' \ No newline at end of file diff --git a/jc/jc_types.py b/jc/jc_types.py index 6ac5894d..d34af271 100644 --- a/jc/jc_types.py +++ b/jc/jc_types.py @@ -20,6 +20,7 @@ if sys.version_info >= (3, 8): "author_email": str, "compatible": List[str], "magic_commands": List[str], + "tags": List[str], "documentation": str, "streaming": bool, "plugin": bool, diff --git a/jc/parsers/acpi.py b/jc/parsers/acpi.py index c1f731c0..ed69fd1a 100644 --- a/jc/parsers/acpi.py +++ b/jc/parsers/acpi.py @@ -233,6 +233,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['acpi'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/airport.py b/jc/parsers/airport.py index b7e7353e..696100f8 100644 --- a/jc/parsers/airport.py +++ b/jc/parsers/airport.py @@ -86,6 +86,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['darwin'] magic_commands = ['airport -I'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/airport_s.py b/jc/parsers/airport_s.py index cb7d1fe1..802c726e 100644 --- a/jc/parsers/airport_s.py +++ b/jc/parsers/airport_s.py @@ -115,6 +115,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['darwin'] magic_commands = ['airport -s'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/arp.py b/jc/parsers/arp.py index e1e24f0c..130fa7a1 100644 --- a/jc/parsers/arp.py +++ b/jc/parsers/arp.py @@ -125,6 +125,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'aix', 'freebsd', 'darwin'] magic_commands = ['arp'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/asciitable.py b/jc/parsers/asciitable.py index 724eb849..b7da4b94 100644 --- a/jc/parsers/asciitable.py +++ b/jc/parsers/asciitable.py @@ -130,6 +130,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['generic', 'string'] __version__ = info.version diff --git a/jc/parsers/asciitable_m.py b/jc/parsers/asciitable_m.py index 8c77ce8b..24b55487 100644 --- a/jc/parsers/asciitable_m.py +++ b/jc/parsers/asciitable_m.py @@ -115,6 +115,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['generic', 'string'] __version__ = info.version diff --git a/jc/parsers/blkid.py b/jc/parsers/blkid.py index 19969740..6d28664c 100644 --- a/jc/parsers/blkid.py +++ b/jc/parsers/blkid.py @@ -127,6 +127,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['blkid'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/cbt.py b/jc/parsers/cbt.py index 96adf3bc..184993fa 100644 --- a/jc/parsers/cbt.py +++ b/jc/parsers/cbt.py @@ -106,6 +106,7 @@ class info(): author_email = 'andreas.weiden@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] magic_commands = ['cbt'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/cef.py b/jc/parsers/cef.py index a834c0ce..dae7bc54 100644 --- a/jc/parsers/cef.py +++ b/jc/parsers/cef.py @@ -129,6 +129,8 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the pycef library at https://github.com/DavidJBianco/pycef/releases/tag/v1.11-2' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] + __version__ = info.version diff --git a/jc/parsers/cef_s.py b/jc/parsers/cef_s.py index 6a945e25..00215aca 100644 --- a/jc/parsers/cef_s.py +++ b/jc/parsers/cef_s.py @@ -103,6 +103,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the pycef library at https://github.com/DavidJBianco/pycef/releases/tag/v1.11-2' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] streaming = True diff --git a/jc/parsers/chage.py b/jc/parsers/chage.py index a2ef099f..ad24cffa 100644 --- a/jc/parsers/chage.py +++ b/jc/parsers/chage.py @@ -63,6 +63,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['chage --list', 'chage -l'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/cksum.py b/jc/parsers/cksum.py index 87428f92..4e798474 100644 --- a/jc/parsers/cksum.py +++ b/jc/parsers/cksum.py @@ -60,6 +60,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['cksum', 'sum'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/clf.py b/jc/parsers/clf.py index 214e13a3..374553ce 100644 --- a/jc/parsers/clf.py +++ b/jc/parsers/clf.py @@ -179,6 +179,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] __version__ = info.version diff --git a/jc/parsers/clf_s.py b/jc/parsers/clf_s.py index 610c7ca1..fc43ed1e 100644 --- a/jc/parsers/clf_s.py +++ b/jc/parsers/clf_s.py @@ -95,6 +95,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] streaming = True diff --git a/jc/parsers/crontab.py b/jc/parsers/crontab.py index 9f19026f..07f7da53 100644 --- a/jc/parsers/crontab.py +++ b/jc/parsers/crontab.py @@ -180,6 +180,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] magic_commands = ['crontab'] + tags = ['file', 'command'] __version__ = info.version diff --git a/jc/parsers/crontab_u.py b/jc/parsers/crontab_u.py index aa4f77be..7fb5a938 100644 --- a/jc/parsers/crontab_u.py +++ b/jc/parsers/crontab_u.py @@ -176,6 +176,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] + tags = ['file', 'command'] __version__ = info.version diff --git a/jc/parsers/csv.py b/jc/parsers/csv.py index 8d1e5833..e97e9669 100644 --- a/jc/parsers/csv.py +++ b/jc/parsers/csv.py @@ -86,6 +86,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the python standard csv library' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] __version__ = info.version diff --git a/jc/parsers/csv_s.py b/jc/parsers/csv_s.py index 98a8ce97..9eaf1a1b 100644 --- a/jc/parsers/csv_s.py +++ b/jc/parsers/csv_s.py @@ -69,6 +69,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the python standard csv library' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] streaming = True diff --git a/jc/parsers/date.py b/jc/parsers/date.py index 237ead6d..594f3b80 100644 --- a/jc/parsers/date.py +++ b/jc/parsers/date.py @@ -84,6 +84,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['date'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/datetime_iso.py b/jc/parsers/datetime_iso.py index 2da60b62..e7ef9404 100644 --- a/jc/parsers/datetime_iso.py +++ b/jc/parsers/datetime_iso.py @@ -75,6 +75,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the pyiso8601 library from https://github.com/micktwomey/pyiso8601/releases/tag/1.0.2' compatible = ['linux', 'aix', 'freebsd', 'darwin', 'win32', 'cygwin'] + tags = ['standard', 'string'] __version__ = info.version diff --git a/jc/parsers/df.py b/jc/parsers/df.py index e077a1e2..50fdfa8e 100644 --- a/jc/parsers/df.py +++ b/jc/parsers/df.py @@ -105,6 +105,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['df'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/dig.py b/jc/parsers/dig.py index be7bcd2e..b184454e 100644 --- a/jc/parsers/dig.py +++ b/jc/parsers/dig.py @@ -328,6 +328,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'aix', 'freebsd', 'darwin', 'win32', 'cygwin'] magic_commands = ['dig'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/dir.py b/jc/parsers/dir.py index 68ea7070..0115fce6 100644 --- a/jc/parsers/dir.py +++ b/jc/parsers/dir.py @@ -126,6 +126,7 @@ class info(): author = 'Rasheed Elsaleh' author_email = 'rasheed@rebelliondefense.com' compatible = ['win32'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/dmidecode.py b/jc/parsers/dmidecode.py index 3b7e180e..98a342e3 100644 --- a/jc/parsers/dmidecode.py +++ b/jc/parsers/dmidecode.py @@ -131,6 +131,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['dmidecode'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/dpkg_l.py b/jc/parsers/dpkg_l.py index c82ee28a..6375c608 100644 --- a/jc/parsers/dpkg_l.py +++ b/jc/parsers/dpkg_l.py @@ -138,6 +138,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['dpkg -l'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/du.py b/jc/parsers/du.py index 96c26a3e..f55edda2 100644 --- a/jc/parsers/du.py +++ b/jc/parsers/du.py @@ -98,6 +98,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] magic_commands = ['du'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/email_address.py b/jc/parsers/email_address.py index a2132fb1..f20c6726 100644 --- a/jc/parsers/email_address.py +++ b/jc/parsers/email_address.py @@ -47,6 +47,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'string'] __version__ = info.version diff --git a/jc/parsers/env.py b/jc/parsers/env.py index 667a4ffc..1d4bb68a 100644 --- a/jc/parsers/env.py +++ b/jc/parsers/env.py @@ -78,6 +78,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] magic_commands = ['env', 'printenv'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/file.py b/jc/parsers/file.py index 10789858..264ccb3a 100644 --- a/jc/parsers/file.py +++ b/jc/parsers/file.py @@ -69,6 +69,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'aix', 'freebsd', 'darwin'] magic_commands = ['file'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/findmnt.py b/jc/parsers/findmnt.py index 8ae88a4e..4d18c4f1 100644 --- a/jc/parsers/findmnt.py +++ b/jc/parsers/findmnt.py @@ -99,6 +99,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['findmnt'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/finger.py b/jc/parsers/finger.py index 52f37381..f46df76e 100644 --- a/jc/parsers/finger.py +++ b/jc/parsers/finger.py @@ -98,6 +98,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'freebsd'] magic_commands = ['finger'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/foo.py b/jc/parsers/foo.py index e27b4e0a..8a4d1c97 100644 --- a/jc/parsers/foo.py +++ b/jc/parsers/foo.py @@ -48,6 +48,9 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + + # tags options: generic, standard, file, string, binary, command + tags = ['command'] magic_commands = ['foo'] diff --git a/jc/parsers/foo_s.py b/jc/parsers/foo_s.py index 88aae251..93416e41 100644 --- a/jc/parsers/foo_s.py +++ b/jc/parsers/foo_s.py @@ -58,6 +58,9 @@ class info(): # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + + # tags options: generic, standard, file, string, binary, command + tags = ['command'] streaming = True diff --git a/jc/parsers/free.py b/jc/parsers/free.py index 8116f2e9..b8d0b187 100644 --- a/jc/parsers/free.py +++ b/jc/parsers/free.py @@ -79,6 +79,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['free'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/fstab.py b/jc/parsers/fstab.py index f51afed8..c5618696 100644 --- a/jc/parsers/fstab.py +++ b/jc/parsers/fstab.py @@ -90,6 +90,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/git_log.py b/jc/parsers/git_log.py index d89d56cd..9b58fc11 100644 --- a/jc/parsers/git_log.py +++ b/jc/parsers/git_log.py @@ -159,6 +159,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] magic_commands = ['git log'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/git_log_s.py b/jc/parsers/git_log_s.py index b4364410..048e199f 100644 --- a/jc/parsers/git_log_s.py +++ b/jc/parsers/git_log_s.py @@ -93,6 +93,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['command'] streaming = True diff --git a/jc/parsers/git_ls_remote.py b/jc/parsers/git_ls_remote.py index 352a9442..e05c8993 100644 --- a/jc/parsers/git_ls_remote.py +++ b/jc/parsers/git_ls_remote.py @@ -72,6 +72,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] magic_commands = ['git ls-remote'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/gpg.py b/jc/parsers/gpg.py index dff1495a..21eb32fa 100644 --- a/jc/parsers/gpg.py +++ b/jc/parsers/gpg.py @@ -126,6 +126,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['gpg --with-colons'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/group.py b/jc/parsers/group.py index e24ec987..6bf65f52 100644 --- a/jc/parsers/group.py +++ b/jc/parsers/group.py @@ -114,6 +114,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/gshadow.py b/jc/parsers/gshadow.py index 34422498..b2b87dea 100644 --- a/jc/parsers/gshadow.py +++ b/jc/parsers/gshadow.py @@ -82,6 +82,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/hash.py b/jc/parsers/hash.py index 9de1b592..c88b87a9 100644 --- a/jc/parsers/hash.py +++ b/jc/parsers/hash.py @@ -43,6 +43,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/hashsum.py b/jc/parsers/hashsum.py index 512016a0..be47b742 100644 --- a/jc/parsers/hashsum.py +++ b/jc/parsers/hashsum.py @@ -76,6 +76,7 @@ class info(): compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['md5sum', 'md5', 'shasum', 'sha1sum', 'sha224sum', 'sha256sum', 'sha384sum', 'sha512sum'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/hciconfig.py b/jc/parsers/hciconfig.py index e21f2b68..7b769172 100644 --- a/jc/parsers/hciconfig.py +++ b/jc/parsers/hciconfig.py @@ -323,6 +323,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['hciconfig'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/history.py b/jc/parsers/history.py index 095f09c1..d07d09e0 100644 --- a/jc/parsers/history.py +++ b/jc/parsers/history.py @@ -69,6 +69,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Optimizations by https://github.com/philippeitis' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/hosts.py b/jc/parsers/hosts.py index 6164f61c..bb5dd009 100644 --- a/jc/parsers/hosts.py +++ b/jc/parsers/hosts.py @@ -79,6 +79,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/id.py b/jc/parsers/id.py index d3f310a2..fffff37d 100644 --- a/jc/parsers/id.py +++ b/jc/parsers/id.py @@ -112,6 +112,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] magic_commands = ['id'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ifconfig.py b/jc/parsers/ifconfig.py index 83ed94e0..f8bc9503 100644 --- a/jc/parsers/ifconfig.py +++ b/jc/parsers/ifconfig.py @@ -225,6 +225,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'aix', 'freebsd', 'darwin'] magic_commands = ['ifconfig'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ini.py b/jc/parsers/ini.py index 6a68ba07..37081219 100644 --- a/jc/parsers/ini.py +++ b/jc/parsers/ini.py @@ -76,6 +76,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using configparser from the standard library' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] __version__ = info.version diff --git a/jc/parsers/iostat.py b/jc/parsers/iostat.py index 4bd01f3b..0a4b4107 100644 --- a/jc/parsers/iostat.py +++ b/jc/parsers/iostat.py @@ -166,6 +166,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['iostat'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/iostat_s.py b/jc/parsers/iostat_s.py index b86e56ed..c2f5921f 100644 --- a/jc/parsers/iostat_s.py +++ b/jc/parsers/iostat_s.py @@ -113,6 +113,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['command'] streaming = True diff --git a/jc/parsers/ip_address.py b/jc/parsers/ip_address.py index ba5f62d7..103ea31e 100644 --- a/jc/parsers/ip_address.py +++ b/jc/parsers/ip_address.py @@ -538,6 +538,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'string'] __version__ = info.version diff --git a/jc/parsers/iptables.py b/jc/parsers/iptables.py index c33f6917..7a1d45b2 100644 --- a/jc/parsers/iptables.py +++ b/jc/parsers/iptables.py @@ -169,6 +169,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['iptables'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/iso_datetime.py b/jc/parsers/iso_datetime.py index 49f9f317..b4294e9e 100644 --- a/jc/parsers/iso_datetime.py +++ b/jc/parsers/iso_datetime.py @@ -17,6 +17,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Deprecated - please use datetime-iso' compatible = ['linux', 'aix', 'freebsd', 'darwin', 'win32', 'cygwin'] + tags = ['standard', 'string'] deprecated = True diff --git a/jc/parsers/iw_scan.py b/jc/parsers/iw_scan.py index 607dca0c..8b936807 100644 --- a/jc/parsers/iw_scan.py +++ b/jc/parsers/iw_scan.py @@ -129,6 +129,7 @@ class info(): details = 'Enhancements by Philipp Schmitt (https://pschmitt.dev/)' compatible = ['linux'] magic_commands = ['iw dev'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/iwconfig.py b/jc/parsers/iwconfig.py index c380dd94..aa73d43f 100644 --- a/jc/parsers/iwconfig.py +++ b/jc/parsers/iwconfig.py @@ -93,6 +93,7 @@ class info(): author_email = 'vrince@gmail.com' compatible = ['linux'] magic_commands = ['iwconfig'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/jar_manifest.py b/jc/parsers/jar_manifest.py index 4c930fbf..d54a2901 100644 --- a/jc/parsers/jar_manifest.py +++ b/jc/parsers/jar_manifest.py @@ -83,6 +83,7 @@ class info(): author = 'Matt J' author_email = 'https://github.com/listuser' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/jobs.py b/jc/parsers/jobs.py index 8637d81b..0befa601 100644 --- a/jc/parsers/jobs.py +++ b/jc/parsers/jobs.py @@ -99,6 +99,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['jobs'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/jwt.py b/jc/parsers/jwt.py index 0f293fda..df659343 100644 --- a/jc/parsers/jwt.py +++ b/jc/parsers/jwt.py @@ -56,6 +56,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'string'] __version__ = info.version diff --git a/jc/parsers/kv.py b/jc/parsers/kv.py index 6e368535..6bcddc5b 100644 --- a/jc/parsers/kv.py +++ b/jc/parsers/kv.py @@ -1,4 +1,4 @@ -"""jc - JSON Convert `Key/Value` file parser +"""jc - JSON Convert `Key/Value` file and string parser Supports files containing simple key/value pairs. @@ -55,11 +55,12 @@ Examples: class info(): """Provides parser metadata (version, author, etc.)""" version = '1.2' - description = 'Key/Value file parser' + description = 'Key/Value file and string parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' details = 'This is a wrapper for the INI parser' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['generic', 'file', 'string'] __version__ = info.version diff --git a/jc/parsers/last.py b/jc/parsers/last.py index d3040553..36f5f95e 100644 --- a/jc/parsers/last.py +++ b/jc/parsers/last.py @@ -113,6 +113,7 @@ class info(): details = 'Enhancements by https://github.com/zerolagtime' compatible = ['linux', 'darwin', 'aix', 'freebsd'] magic_commands = ['last', 'lastb'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ls.py b/jc/parsers/ls.py index 8dad76d3..85a69e42 100644 --- a/jc/parsers/ls.py +++ b/jc/parsers/ls.py @@ -124,6 +124,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['ls', 'vdir'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ls_s.py b/jc/parsers/ls_s.py index b000255c..8cf2213d 100644 --- a/jc/parsers/ls_s.py +++ b/jc/parsers/ls_s.py @@ -82,6 +82,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + tags = ['command'] streaming = True diff --git a/jc/parsers/lsblk.py b/jc/parsers/lsblk.py index ecf28ab7..7276711c 100644 --- a/jc/parsers/lsblk.py +++ b/jc/parsers/lsblk.py @@ -281,6 +281,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['lsblk'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/lsmod.py b/jc/parsers/lsmod.py index 035108f1..b4b65d05 100644 --- a/jc/parsers/lsmod.py +++ b/jc/parsers/lsmod.py @@ -132,6 +132,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['lsmod'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/lsof.py b/jc/parsers/lsof.py index ac167fd0..0fc434ae 100644 --- a/jc/parsers/lsof.py +++ b/jc/parsers/lsof.py @@ -126,6 +126,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] magic_commands = ['lsof'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/lspci.py b/jc/parsers/lspci.py index 7cd281b6..771fe38a 100644 --- a/jc/parsers/lspci.py +++ b/jc/parsers/lspci.py @@ -129,6 +129,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['lspci'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/lsusb.py b/jc/parsers/lsusb.py index 490d5429..64b5db8a 100644 --- a/jc/parsers/lsusb.py +++ b/jc/parsers/lsusb.py @@ -275,6 +275,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['lsusb'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/m3u.py b/jc/parsers/m3u.py index 2fa3b691..76fbb4d3 100644 --- a/jc/parsers/m3u.py +++ b/jc/parsers/m3u.py @@ -71,6 +71,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/mdadm.py b/jc/parsers/mdadm.py index 5c4fde12..852911ae 100644 --- a/jc/parsers/mdadm.py +++ b/jc/parsers/mdadm.py @@ -235,6 +235,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['mdadm'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/mount.py b/jc/parsers/mount.py index cbddf4d2..eda5377a 100644 --- a/jc/parsers/mount.py +++ b/jc/parsers/mount.py @@ -81,6 +81,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['mount'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/mpstat.py b/jc/parsers/mpstat.py index 6467a36c..a89c940d 100644 --- a/jc/parsers/mpstat.py +++ b/jc/parsers/mpstat.py @@ -122,6 +122,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['mpstat'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/mpstat_s.py b/jc/parsers/mpstat_s.py index cecd6164..3a746f2b 100644 --- a/jc/parsers/mpstat_s.py +++ b/jc/parsers/mpstat_s.py @@ -106,6 +106,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['command'] streaming = True diff --git a/jc/parsers/netstat.py b/jc/parsers/netstat.py index a7416152..5e86ef27 100644 --- a/jc/parsers/netstat.py +++ b/jc/parsers/netstat.py @@ -361,6 +361,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['netstat'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/nmcli.py b/jc/parsers/nmcli.py index 91644a6a..25572eb9 100644 --- a/jc/parsers/nmcli.py +++ b/jc/parsers/nmcli.py @@ -155,6 +155,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['nmcli'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ntpq.py b/jc/parsers/ntpq.py index 7e0b79f7..ab5740e9 100644 --- a/jc/parsers/ntpq.py +++ b/jc/parsers/ntpq.py @@ -213,6 +213,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'freebsd'] magic_commands = ['ntpq'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/openvpn.py b/jc/parsers/openvpn.py index e3c5ab32..2aa3f1ad 100644 --- a/jc/parsers/openvpn.py +++ b/jc/parsers/openvpn.py @@ -161,6 +161,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/os_prober.py b/jc/parsers/os_prober.py index 701fcc8c..7712b0e2 100644 --- a/jc/parsers/os_prober.py +++ b/jc/parsers/os_prober.py @@ -48,6 +48,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['os-prober'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/passwd.py b/jc/parsers/passwd.py index 6c73f6a8..523e5d52 100644 --- a/jc/parsers/passwd.py +++ b/jc/parsers/passwd.py @@ -99,6 +99,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/pci_ids.py b/jc/parsers/pci_ids.py index 4419fd82..2deb4031 100644 --- a/jc/parsers/pci_ids.py +++ b/jc/parsers/pci_ids.py @@ -81,6 +81,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/pgpass.py b/jc/parsers/pgpass.py index 5f6c8e28..409b2c2d 100644 --- a/jc/parsers/pgpass.py +++ b/jc/parsers/pgpass.py @@ -54,6 +54,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/pidstat.py b/jc/parsers/pidstat.py index b89427d0..c6271dda 100644 --- a/jc/parsers/pidstat.py +++ b/jc/parsers/pidstat.py @@ -134,6 +134,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['pidstat'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/pidstat_s.py b/jc/parsers/pidstat_s.py index 4ac40245..dbaee0d6 100644 --- a/jc/parsers/pidstat_s.py +++ b/jc/parsers/pidstat_s.py @@ -88,6 +88,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['command'] streaming = True diff --git a/jc/parsers/ping.py b/jc/parsers/ping.py index 3ca5e51b..98129856 100644 --- a/jc/parsers/ping.py +++ b/jc/parsers/ping.py @@ -170,6 +170,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['ping', 'ping6'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ping_s.py b/jc/parsers/ping_s.py index e697e737..d68a14cb 100644 --- a/jc/parsers/ping_s.py +++ b/jc/parsers/ping_s.py @@ -90,6 +90,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] + tags = ['command'] streaming = True diff --git a/jc/parsers/pip_list.py b/jc/parsers/pip_list.py index ebaf8f0b..7bafeec2 100644 --- a/jc/parsers/pip_list.py +++ b/jc/parsers/pip_list.py @@ -54,6 +54,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] magic_commands = ['pip list', 'pip3 list'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/pip_show.py b/jc/parsers/pip_show.py index 4510e92e..8d12ebd8 100644 --- a/jc/parsers/pip_show.py +++ b/jc/parsers/pip_show.py @@ -72,6 +72,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] magic_commands = ['pip show', 'pip3 show'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index fa313069..e4e9da62 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -58,6 +58,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string', 'binary'] __version__ = info.version diff --git a/jc/parsers/postconf.py b/jc/parsers/postconf.py index bfdf999b..c4678fe0 100644 --- a/jc/parsers/postconf.py +++ b/jc/parsers/postconf.py @@ -97,6 +97,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['postconf -M'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/proc.py b/jc/parsers/proc.py index 5ac5013a..2330c6ab 100644 --- a/jc/parsers/proc.py +++ b/jc/parsers/proc.py @@ -125,6 +125,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/proc_buddyinfo.py b/jc/parsers/proc_buddyinfo.py index 1efd5680..8262ca45 100644 --- a/jc/parsers/proc_buddyinfo.py +++ b/jc/parsers/proc_buddyinfo.py @@ -108,6 +108,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_consoles.py b/jc/parsers/proc_consoles.py index 3423d0b0..3d8a95be 100644 --- a/jc/parsers/proc_consoles.py +++ b/jc/parsers/proc_consoles.py @@ -92,6 +92,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_cpuinfo.py b/jc/parsers/proc_cpuinfo.py index 5de13991..e7628e65 100644 --- a/jc/parsers/proc_cpuinfo.py +++ b/jc/parsers/proc_cpuinfo.py @@ -227,6 +227,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_crypto.py b/jc/parsers/proc_crypto.py index 93cab164..76b270c5 100644 --- a/jc/parsers/proc_crypto.py +++ b/jc/parsers/proc_crypto.py @@ -123,6 +123,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_devices.py b/jc/parsers/proc_devices.py index 4d2a4441..20c2d1a5 100644 --- a/jc/parsers/proc_devices.py +++ b/jc/parsers/proc_devices.py @@ -83,6 +83,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_diskstats.py b/jc/parsers/proc_diskstats.py index c230b2f8..1067f869 100644 --- a/jc/parsers/proc_diskstats.py +++ b/jc/parsers/proc_diskstats.py @@ -183,6 +183,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_driver_rtc.py b/jc/parsers/proc_driver_rtc.py index 2b046cbc..8356755b 100644 --- a/jc/parsers/proc_driver_rtc.py +++ b/jc/parsers/proc_driver_rtc.py @@ -106,6 +106,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_filesystems.py b/jc/parsers/proc_filesystems.py index 91170de4..2e079b98 100644 --- a/jc/parsers/proc_filesystems.py +++ b/jc/parsers/proc_filesystems.py @@ -61,6 +61,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_interrupts.py b/jc/parsers/proc_interrupts.py index 49a27b19..376c053e 100644 --- a/jc/parsers/proc_interrupts.py +++ b/jc/parsers/proc_interrupts.py @@ -113,6 +113,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_iomem.py b/jc/parsers/proc_iomem.py index 4b526d7a..9d4b2766 100644 --- a/jc/parsers/proc_iomem.py +++ b/jc/parsers/proc_iomem.py @@ -65,6 +65,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_ioports.py b/jc/parsers/proc_ioports.py index a1b6de0f..bac1c563 100644 --- a/jc/parsers/proc_ioports.py +++ b/jc/parsers/proc_ioports.py @@ -66,6 +66,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_loadavg.py b/jc/parsers/proc_loadavg.py index dc234c71..9247a41b 100644 --- a/jc/parsers/proc_loadavg.py +++ b/jc/parsers/proc_loadavg.py @@ -68,6 +68,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_locks.py b/jc/parsers/proc_locks.py index ce4c675e..01e8a3ca 100644 --- a/jc/parsers/proc_locks.py +++ b/jc/parsers/proc_locks.py @@ -110,6 +110,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_meminfo.py b/jc/parsers/proc_meminfo.py index bf378374..d8ddce64 100644 --- a/jc/parsers/proc_meminfo.py +++ b/jc/parsers/proc_meminfo.py @@ -98,6 +98,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_modules.py b/jc/parsers/proc_modules.py index e94cbb54..472c389a 100644 --- a/jc/parsers/proc_modules.py +++ b/jc/parsers/proc_modules.py @@ -112,6 +112,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_mtrr.py b/jc/parsers/proc_mtrr.py index 052414b4..fbeae40b 100644 --- a/jc/parsers/proc_mtrr.py +++ b/jc/parsers/proc_mtrr.py @@ -92,6 +92,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_arp.py b/jc/parsers/proc_net_arp.py index 1535326d..47dfa623 100644 --- a/jc/parsers/proc_net_arp.py +++ b/jc/parsers/proc_net_arp.py @@ -62,6 +62,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_dev.py b/jc/parsers/proc_net_dev.py index a72ac899..14cf4af6 100644 --- a/jc/parsers/proc_net_dev.py +++ b/jc/parsers/proc_net_dev.py @@ -108,6 +108,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_dev_mcast.py b/jc/parsers/proc_net_dev_mcast.py index 62563b9a..3984f341 100644 --- a/jc/parsers/proc_net_dev_mcast.py +++ b/jc/parsers/proc_net_dev_mcast.py @@ -86,6 +86,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_if_inet6.py b/jc/parsers/proc_net_if_inet6.py index 700e8ba0..a2e3d11e 100644 --- a/jc/parsers/proc_net_if_inet6.py +++ b/jc/parsers/proc_net_if_inet6.py @@ -69,6 +69,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_igmp.py b/jc/parsers/proc_net_igmp.py index 4f80151b..d4f26b7d 100644 --- a/jc/parsers/proc_net_igmp.py +++ b/jc/parsers/proc_net_igmp.py @@ -132,6 +132,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_igmp6.py b/jc/parsers/proc_net_igmp6.py index 6c8949ed..a1640959 100644 --- a/jc/parsers/proc_net_igmp6.py +++ b/jc/parsers/proc_net_igmp6.py @@ -106,6 +106,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_ipv6_route.py b/jc/parsers/proc_net_ipv6_route.py index ccc0be1a..b9643a8f 100644 --- a/jc/parsers/proc_net_ipv6_route.py +++ b/jc/parsers/proc_net_ipv6_route.py @@ -70,6 +70,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_netlink.py b/jc/parsers/proc_net_netlink.py index c5a65966..31a8deb6 100644 --- a/jc/parsers/proc_net_netlink.py +++ b/jc/parsers/proc_net_netlink.py @@ -111,6 +111,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_netstat.py b/jc/parsers/proc_net_netstat.py index fe84eb93..d7ae90a7 100644 --- a/jc/parsers/proc_net_netstat.py +++ b/jc/parsers/proc_net_netstat.py @@ -305,6 +305,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_packet.py b/jc/parsers/proc_net_packet.py index 37d3cf63..c2945171 100644 --- a/jc/parsers/proc_net_packet.py +++ b/jc/parsers/proc_net_packet.py @@ -76,6 +76,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_protocols.py b/jc/parsers/proc_net_protocols.py index 7703b764..11a136dd 100644 --- a/jc/parsers/proc_net_protocols.py +++ b/jc/parsers/proc_net_protocols.py @@ -138,6 +138,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_route.py b/jc/parsers/proc_net_route.py index dcbafeb3..ff7e41d7 100644 --- a/jc/parsers/proc_net_route.py +++ b/jc/parsers/proc_net_route.py @@ -90,6 +90,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_net_unix.py b/jc/parsers/proc_net_unix.py index 1f11b71e..db1f5338 100644 --- a/jc/parsers/proc_net_unix.py +++ b/jc/parsers/proc_net_unix.py @@ -81,6 +81,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pagetypeinfo.py b/jc/parsers/proc_pagetypeinfo.py index 08ef08ba..92a0ed26 100644 --- a/jc/parsers/proc_pagetypeinfo.py +++ b/jc/parsers/proc_pagetypeinfo.py @@ -121,6 +121,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_partitions.py b/jc/parsers/proc_partitions.py index 826a33a5..6afbeda0 100644 --- a/jc/parsers/proc_partitions.py +++ b/jc/parsers/proc_partitions.py @@ -81,6 +81,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_fdinfo.py b/jc/parsers/proc_pid_fdinfo.py index e8e99460..d86d6ec3 100644 --- a/jc/parsers/proc_pid_fdinfo.py +++ b/jc/parsers/proc_pid_fdinfo.py @@ -108,6 +108,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_io.py b/jc/parsers/proc_pid_io.py index 8bfe71c1..c450302d 100644 --- a/jc/parsers/proc_pid_io.py +++ b/jc/parsers/proc_pid_io.py @@ -54,6 +54,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_maps.py b/jc/parsers/proc_pid_maps.py index 22c8765b..e51c0fed 100644 --- a/jc/parsers/proc_pid_maps.py +++ b/jc/parsers/proc_pid_maps.py @@ -106,6 +106,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_mountinfo.py b/jc/parsers/proc_pid_mountinfo.py index 40847cfc..5b43bc94 100644 --- a/jc/parsers/proc_pid_mountinfo.py +++ b/jc/parsers/proc_pid_mountinfo.py @@ -151,6 +151,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_numa_maps.py b/jc/parsers/proc_pid_numa_maps.py index 812a91b4..2210d401 100644 --- a/jc/parsers/proc_pid_numa_maps.py +++ b/jc/parsers/proc_pid_numa_maps.py @@ -107,6 +107,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_smaps.py b/jc/parsers/proc_pid_smaps.py index 692feaf9..921b4a2c 100644 --- a/jc/parsers/proc_pid_smaps.py +++ b/jc/parsers/proc_pid_smaps.py @@ -172,6 +172,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_stat.py b/jc/parsers/proc_pid_stat.py index bac8e73f..44ede29f 100644 --- a/jc/parsers/proc_pid_stat.py +++ b/jc/parsers/proc_pid_stat.py @@ -207,6 +207,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_statm.py b/jc/parsers/proc_pid_statm.py index 6b96519b..a1cd2f42 100644 --- a/jc/parsers/proc_pid_statm.py +++ b/jc/parsers/proc_pid_statm.py @@ -58,6 +58,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_pid_status.py b/jc/parsers/proc_pid_status.py index 6aa214bf..aae91e2f 100644 --- a/jc/parsers/proc_pid_status.py +++ b/jc/parsers/proc_pid_status.py @@ -275,6 +275,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_slabinfo.py b/jc/parsers/proc_slabinfo.py index 9ebbde1e..15488281 100644 --- a/jc/parsers/proc_slabinfo.py +++ b/jc/parsers/proc_slabinfo.py @@ -80,6 +80,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_softirqs.py b/jc/parsers/proc_softirqs.py index d4068ec1..cac25ae9 100644 --- a/jc/parsers/proc_softirqs.py +++ b/jc/parsers/proc_softirqs.py @@ -66,6 +66,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_stat.py b/jc/parsers/proc_stat.py index 99f974cc..a09f67e9 100644 --- a/jc/parsers/proc_stat.py +++ b/jc/parsers/proc_stat.py @@ -141,6 +141,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_swaps.py b/jc/parsers/proc_swaps.py index d50387a0..97269f4b 100644 --- a/jc/parsers/proc_swaps.py +++ b/jc/parsers/proc_swaps.py @@ -72,6 +72,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_uptime.py b/jc/parsers/proc_uptime.py index abea777f..c3207e47 100644 --- a/jc/parsers/proc_uptime.py +++ b/jc/parsers/proc_uptime.py @@ -48,6 +48,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_version.py b/jc/parsers/proc_version.py index 8b888274..e0fc4626 100644 --- a/jc/parsers/proc_version.py +++ b/jc/parsers/proc_version.py @@ -60,6 +60,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_vmallocinfo.py b/jc/parsers/proc_vmallocinfo.py index f3e8a54b..4001830e 100644 --- a/jc/parsers/proc_vmallocinfo.py +++ b/jc/parsers/proc_vmallocinfo.py @@ -106,6 +106,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_vmstat.py b/jc/parsers/proc_vmstat.py index 128d0f70..522f04ba 100644 --- a/jc/parsers/proc_vmstat.py +++ b/jc/parsers/proc_vmstat.py @@ -64,6 +64,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/proc_zoneinfo.py b/jc/parsers/proc_zoneinfo.py index ec3db99b..77688b3b 100644 --- a/jc/parsers/proc_zoneinfo.py +++ b/jc/parsers/proc_zoneinfo.py @@ -314,6 +314,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['file'] hidden = True diff --git a/jc/parsers/ps.py b/jc/parsers/ps.py index b7fc7bc3..11ef2f90 100644 --- a/jc/parsers/ps.py +++ b/jc/parsers/ps.py @@ -213,6 +213,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['ps'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/route.py b/jc/parsers/route.py index 4f7bdfdd..add7cf53 100644 --- a/jc/parsers/route.py +++ b/jc/parsers/route.py @@ -115,6 +115,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['route'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/rpm_qi.py b/jc/parsers/rpm_qi.py index 21b0c3bd..bcdfa4eb 100644 --- a/jc/parsers/rpm_qi.py +++ b/jc/parsers/rpm_qi.py @@ -167,6 +167,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['rpm -qi', 'rpm -qia', 'rpm -qai'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/rsync.py b/jc/parsers/rsync.py index 55ae5599..137d2f7a 100644 --- a/jc/parsers/rsync.py +++ b/jc/parsers/rsync.py @@ -143,6 +143,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['rsync -i', 'rsync --itemize-changes'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/rsync_s.py b/jc/parsers/rsync_s.py index 07424046..8c9d07d8 100644 --- a/jc/parsers/rsync_s.py +++ b/jc/parsers/rsync_s.py @@ -93,6 +93,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] + tags = ['command'] streaming = True diff --git a/jc/parsers/semver.py b/jc/parsers/semver.py index e80fe53e..3660e3e9 100644 --- a/jc/parsers/semver.py +++ b/jc/parsers/semver.py @@ -57,6 +57,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'string'] __version__ = info.version diff --git a/jc/parsers/sfdisk.py b/jc/parsers/sfdisk.py index 440eb5ac..365414c5 100644 --- a/jc/parsers/sfdisk.py +++ b/jc/parsers/sfdisk.py @@ -209,6 +209,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['sfdisk'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/shadow.py b/jc/parsers/shadow.py index a57847ff..6b5c8dc4 100644 --- a/jc/parsers/shadow.py +++ b/jc/parsers/shadow.py @@ -106,6 +106,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'aix', 'freebsd'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/ss.py b/jc/parsers/ss.py index a4564dcd..628511b3 100644 --- a/jc/parsers/ss.py +++ b/jc/parsers/ss.py @@ -287,6 +287,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['ss'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/sshd_conf.py b/jc/parsers/sshd_conf.py index bcedccf2..b6a19e64 100644 --- a/jc/parsers/sshd_conf.py +++ b/jc/parsers/sshd_conf.py @@ -489,6 +489,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['sshd -T'] + tags = ['file'] __version__ = info.version diff --git a/jc/parsers/stat.py b/jc/parsers/stat.py index 706610a7..daa07999 100644 --- a/jc/parsers/stat.py +++ b/jc/parsers/stat.py @@ -177,6 +177,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['stat'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/stat_s.py b/jc/parsers/stat_s.py index d74b7a4e..c3e1a0b4 100644 --- a/jc/parsers/stat_s.py +++ b/jc/parsers/stat_s.py @@ -89,6 +89,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] + tags = ['command'] streaming = True diff --git a/jc/parsers/sysctl.py b/jc/parsers/sysctl.py index 22f314d8..e1bbe56e 100644 --- a/jc/parsers/sysctl.py +++ b/jc/parsers/sysctl.py @@ -63,6 +63,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['sysctl'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/syslog.py b/jc/parsers/syslog.py index 9029061a..4853bdb7 100644 --- a/jc/parsers/syslog.py +++ b/jc/parsers/syslog.py @@ -113,6 +113,8 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] + __version__ = info.version diff --git a/jc/parsers/syslog_bsd.py b/jc/parsers/syslog_bsd.py index 44677c15..87ce7236 100644 --- a/jc/parsers/syslog_bsd.py +++ b/jc/parsers/syslog_bsd.py @@ -65,6 +65,8 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] + __version__ = info.version diff --git a/jc/parsers/syslog_bsd_s.py b/jc/parsers/syslog_bsd_s.py index 838953c0..a4b05c0c 100644 --- a/jc/parsers/syslog_bsd_s.py +++ b/jc/parsers/syslog_bsd_s.py @@ -68,6 +68,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] streaming = True diff --git a/jc/parsers/syslog_s.py b/jc/parsers/syslog_s.py index 5f3e41e6..871f27cf 100644 --- a/jc/parsers/syslog_s.py +++ b/jc/parsers/syslog_s.py @@ -91,6 +91,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] streaming = True diff --git a/jc/parsers/systemctl.py b/jc/parsers/systemctl.py index 5e3265fb..9d648449 100644 --- a/jc/parsers/systemctl.py +++ b/jc/parsers/systemctl.py @@ -64,6 +64,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['systemctl'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/systemctl_lj.py b/jc/parsers/systemctl_lj.py index 531b89de..0c7ecf04 100644 --- a/jc/parsers/systemctl_lj.py +++ b/jc/parsers/systemctl_lj.py @@ -81,6 +81,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['systemctl list-jobs'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/systemctl_ls.py b/jc/parsers/systemctl_ls.py index 774ce6c9..df4baecf 100644 --- a/jc/parsers/systemctl_ls.py +++ b/jc/parsers/systemctl_ls.py @@ -57,6 +57,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['systemctl list-sockets'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/systemctl_luf.py b/jc/parsers/systemctl_luf.py index da9059cc..a7019c1d 100644 --- a/jc/parsers/systemctl_luf.py +++ b/jc/parsers/systemctl_luf.py @@ -53,6 +53,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['systemctl list-unit-files'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/systeminfo.py b/jc/parsers/systeminfo.py index 8f2e7683..04f9ac9f 100644 --- a/jc/parsers/systeminfo.py +++ b/jc/parsers/systeminfo.py @@ -218,6 +218,7 @@ class info: author_email = "jon@rebelliondefense.com" compatible = ["win32"] magic_commands = ["systeminfo"] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/time.py b/jc/parsers/time.py index b6cb9af5..1d89ac0c 100644 --- a/jc/parsers/time.py +++ b/jc/parsers/time.py @@ -137,6 +137,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/timedatectl.py b/jc/parsers/timedatectl.py index d5ecafaf..5a7d4e98 100644 --- a/jc/parsers/timedatectl.py +++ b/jc/parsers/timedatectl.py @@ -70,6 +70,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['timedatectl', 'timedatectl status'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/timestamp.py b/jc/parsers/timestamp.py index faf1226f..73c9d991 100644 --- a/jc/parsers/timestamp.py +++ b/jc/parsers/timestamp.py @@ -102,6 +102,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'aix', 'freebsd', 'darwin', 'win32', 'cygwin'] + tags = ['standard', 'string'] __version__ = info.version diff --git a/jc/parsers/top.py b/jc/parsers/top.py index ed6a7d23..5ca4717c 100644 --- a/jc/parsers/top.py +++ b/jc/parsers/top.py @@ -322,6 +322,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['top -b'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/top_s.py b/jc/parsers/top_s.py index 85e9dff3..34d76750 100644 --- a/jc/parsers/top_s.py +++ b/jc/parsers/top_s.py @@ -158,6 +158,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['command'] streaming = True diff --git a/jc/parsers/tracepath.py b/jc/parsers/tracepath.py index 25ca91bc..929ff1ec 100644 --- a/jc/parsers/tracepath.py +++ b/jc/parsers/tracepath.py @@ -138,6 +138,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['tracepath', 'tracepath6'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/traceroute.py b/jc/parsers/traceroute.py index e34cf10d..fe6824be 100644 --- a/jc/parsers/traceroute.py +++ b/jc/parsers/traceroute.py @@ -129,6 +129,7 @@ class info(): details = 'Using the trparse library by Luis Benitez at https://github.com/lbenitez000/trparse' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['traceroute', 'traceroute6'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/udevadm.py b/jc/parsers/udevadm.py index 78002083..2f51d3be 100644 --- a/jc/parsers/udevadm.py +++ b/jc/parsers/udevadm.py @@ -125,6 +125,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['udevadm info'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ufw.py b/jc/parsers/ufw.py index f91f0692..d3ca7cff 100644 --- a/jc/parsers/ufw.py +++ b/jc/parsers/ufw.py @@ -208,6 +208,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['ufw status'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/ufw_appinfo.py b/jc/parsers/ufw_appinfo.py index 7117c474..f893181e 100644 --- a/jc/parsers/ufw_appinfo.py +++ b/jc/parsers/ufw_appinfo.py @@ -144,6 +144,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['ufw app'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/uname.py b/jc/parsers/uname.py index 4d3f8356..5997d86d 100644 --- a/jc/parsers/uname.py +++ b/jc/parsers/uname.py @@ -54,6 +54,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'freebsd'] magic_commands = ['uname'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/update_alt_gs.py b/jc/parsers/update_alt_gs.py index 6d0f0eec..633e2a5c 100644 --- a/jc/parsers/update_alt_gs.py +++ b/jc/parsers/update_alt_gs.py @@ -52,6 +52,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['update-alternatives --get-selections'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/update_alt_q.py b/jc/parsers/update_alt_q.py index 14c85668..6f35e56d 100644 --- a/jc/parsers/update_alt_q.py +++ b/jc/parsers/update_alt_q.py @@ -138,6 +138,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['update-alternatives --query'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/upower.py b/jc/parsers/upower.py index ea1da122..a659717f 100644 --- a/jc/parsers/upower.py +++ b/jc/parsers/upower.py @@ -204,6 +204,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['upower'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/uptime.py b/jc/parsers/uptime.py index 3ae1e8a7..3ecd7fb5 100644 --- a/jc/parsers/uptime.py +++ b/jc/parsers/uptime.py @@ -71,6 +71,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['uptime'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/url.py b/jc/parsers/url.py index f03235e2..d211ee2d 100644 --- a/jc/parsers/url.py +++ b/jc/parsers/url.py @@ -207,6 +207,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'string'] __version__ = info.version diff --git a/jc/parsers/vmstat.py b/jc/parsers/vmstat.py index 6112b4d7..ae004bf6 100644 --- a/jc/parsers/vmstat.py +++ b/jc/parsers/vmstat.py @@ -132,6 +132,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] magic_commands = ['vmstat'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/vmstat_s.py b/jc/parsers/vmstat_s.py index 83b83d06..4ef4d847 100644 --- a/jc/parsers/vmstat_s.py +++ b/jc/parsers/vmstat_s.py @@ -105,6 +105,7 @@ class info(): author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux'] + tags = ['command'] streaming = True diff --git a/jc/parsers/w.py b/jc/parsers/w.py index 6dd7f9f4..c1b764d3 100644 --- a/jc/parsers/w.py +++ b/jc/parsers/w.py @@ -110,6 +110,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['w'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/wc.py b/jc/parsers/wc.py index 6ae1ebed..49f64a5b 100644 --- a/jc/parsers/wc.py +++ b/jc/parsers/wc.py @@ -60,6 +60,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['wc'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/who.py b/jc/parsers/who.py index 67b1af2a..be8c74e7 100644 --- a/jc/parsers/who.py +++ b/jc/parsers/who.py @@ -142,6 +142,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' compatible = ['linux', 'darwin', 'cygwin', 'aix', 'freebsd'] magic_commands = ['who'] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/x509_cert.py b/jc/parsers/x509_cert.py index b3b909d8..040e3751 100644 --- a/jc/parsers/x509_cert.py +++ b/jc/parsers/x509_cert.py @@ -414,6 +414,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the asn1crypto library at https://github.com/wbond/asn1crypto/releases/tag/1.5.1' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string', 'binary'] __version__ = info.version diff --git a/jc/parsers/xml.py b/jc/parsers/xml.py index d82e7a72..a54de22c 100644 --- a/jc/parsers/xml.py +++ b/jc/parsers/xml.py @@ -87,6 +87,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the xmltodict library at https://github.com/martinblech/xmltodict' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] __version__ = info.version diff --git a/jc/parsers/xrandr.py b/jc/parsers/xrandr.py index b17c0895..60b51802 100644 --- a/jc/parsers/xrandr.py +++ b/jc/parsers/xrandr.py @@ -141,15 +141,13 @@ import jc.utils class info: """Provides parser metadata (version, author, etc.)""" - version = "1.1" description = "`xrandr` command parser" author = "Kevin Lyter" author_email = "lyter_git at sent.com" - - # compatible options: linux, darwin, cygwin, win32, aix, freebsd compatible = ["linux", "darwin", "cygwin", "aix", "freebsd"] magic_commands = ["xrandr"] + tags = ['command'] __version__ = info.version diff --git a/jc/parsers/yaml.py b/jc/parsers/yaml.py index cc962c60..7e121b1a 100644 --- a/jc/parsers/yaml.py +++ b/jc/parsers/yaml.py @@ -93,6 +93,7 @@ class info(): author_email = 'kellyjonbrazil@gmail.com' details = 'Using the ruamel.yaml library at https://pypi.org/project/ruamel.yaml' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] + tags = ['standard', 'file', 'string'] __version__ = info.version diff --git a/jc/parsers/zipinfo.py b/jc/parsers/zipinfo.py index 953b7463..a4e957b3 100644 --- a/jc/parsers/zipinfo.py +++ b/jc/parsers/zipinfo.py @@ -83,6 +83,7 @@ class info(): author_email = 'https://github.com/listuser' compatible = ['linux', 'darwin'] magic_commands = ['zipinfo'] + tags = ['command'] __version__ = info.version From 481c6a8de205f5a68d23bc6d3c8f176c4fb68ebe Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 27 Dec 2022 14:01:03 -0800 Subject: [PATCH 17/31] doc update --- README.md | 2 +- completions/jc_zsh_completion.sh | 2 +- docs/parsers/kv.md | 2 +- man/jc.1 | 4 ++-- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index af4a162e..a2b0b1c1 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ option. | ` --jar-manifest` | Java MANIFEST.MF file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jar_manifest) | | ` --jobs` | `jobs` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jobs) | | ` --jwt` | JWT string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/jwt) | -| ` --kv` | Key/Value file parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) | +| ` --kv` | Key/Value file and string parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/kv) | | ` --last` | `last` and `lastb` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/last) | | ` --ls` | `ls` command parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls) | | ` --ls-s` | `ls` command streaming parser | [details](https://kellyjonbrazil.github.io/jc/docs/parsers/ls_s) | diff --git a/completions/jc_zsh_completion.sh b/completions/jc_zsh_completion.sh index 95424afd..1ea33871 100644 --- a/completions/jc_zsh_completion.sh +++ b/completions/jc_zsh_completion.sh @@ -160,7 +160,7 @@ _jc() { '--jar-manifest:Java MANIFEST.MF file parser' '--jobs:`jobs` command parser' '--jwt:JWT string parser' - '--kv:Key/Value file parser' + '--kv:Key/Value file and string parser' '--last:`last` and `lastb` command parser' '--ls:`ls` command parser' '--ls-s:`ls` command streaming parser' diff --git a/docs/parsers/kv.md b/docs/parsers/kv.md index ec0b79c8..84125fe9 100644 --- a/docs/parsers/kv.md +++ b/docs/parsers/kv.md @@ -3,7 +3,7 @@ # jc.parsers.kv -jc - JSON Convert `Key/Value` file parser +jc - JSON Convert `Key/Value` file and string parser Supports files containing simple key/value pairs. diff --git a/man/jc.1 b/man/jc.1 index 09f5bdb3..1ac0bd49 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2022-12-23 1.22.4 "JSON Convert" +.TH jc 1 2022-12-27 1.22.4 "JSON Convert" .SH NAME \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings .SH SYNOPSIS @@ -318,7 +318,7 @@ JWT string parser .TP .B \fB--kv\fP -Key/Value file parser +Key/Value file and string parser .TP .B From eec2583cb2751748c0caaec2bea7324dc4d53e25 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Tue, 27 Dec 2022 15:59:43 -0800 Subject: [PATCH 18/31] formatting --- completions/jc_zsh_completion.sh | 8 ++++---- jc/cli.py | 2 +- jc/cli_data.py | 6 +++--- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/completions/jc_zsh_completion.sh b/completions/jc_zsh_completion.sh index 1ea33871..648fb659 100644 --- a/completions/jc_zsh_completion.sh +++ b/completions/jc_zsh_completion.sh @@ -291,8 +291,8 @@ _jc() { ) jc_options=(--force-color -C --debug -d --monochrome -m --meta-out -M --pretty -p --quiet -q --raw -r --unbuffer -u --yaml-out -y) jc_options_describe=( - '--force-color:force color output even when using pipes (overrides -m)' - '-C:force color output even when using pipes (overrides -m)' + '--force-color:force color output (overrides -m)' + '-C:force color output (overrides -m)' '--debug:debug (double for verbose debug)' '-d:debug (double for verbose debug)' '--monochrome:monochrome output' @@ -323,8 +323,8 @@ _jc() { '-y:YAML output' '--monochrome:monochrome output' '-m:monochrome output' - '--force-color:force color output even when using pipes (overrides -m)' - '-C:force color output even when using pipes (overrides -m)' + '--force-color:force color output (overrides -m)' + '-C:force color output (overrides -m)' ) jc_help_options=(--help -h) jc_help_options_describe=( diff --git a/jc/cli.py b/jc/cli.py index c00aec29..19f039a1 100644 --- a/jc/cli.py +++ b/jc/cli.py @@ -283,7 +283,7 @@ class JcCli(): otherwise the general help text is printed. """ self.indent = 4 - self.pad = 20 + self.pad = 22 if self.show_categories: utils._safe_print(self.parser_categories_text()) diff --git a/jc/cli_data.py b/jc/cli_data.py index 6311d97f..426dc3ab 100644 --- a/jc/cli_data.py +++ b/jc/cli_data.py @@ -3,7 +3,7 @@ from typing import List, Dict long_options_map: Dict[str, List[str]] = { '--about': ['a', 'about jc'], - '--force-color': ['C', 'force color output even when using pipes (overrides -m)'], + '--force-color': ['C', 'force color output (overrides -m)'], '--debug': ['d', 'debug (double for verbose debug)'], '--help': ['h', 'help (--help --parser_name for parser documentation)'], '--monochrome': ['m', 'monochrome output'], @@ -92,6 +92,6 @@ Examples: $ jc --help --dig More Help: - $ jc -hh # show hidden parsers - $ jc -hhh # list parsers by category + $ jc -hh # show hidden parsers + $ jc -hhh # list parsers by category tags ''' \ No newline at end of file From 1589a81945d37cff628010cf78db7365a17edd2a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Wed, 28 Dec 2022 21:59:38 -0800 Subject: [PATCH 19/31] fix issue with text serial numbers in certificates --- docs/parsers/x509_cert.md | 9 +++++-- jc/parsers/x509_cert.py | 23 +++++++++++++++--- man/jc.1 | 2 +- tests/fixtures/generic/x509-ca-cert.json | 2 +- tests/fixtures/generic/x509-cert-and-key.json | 2 +- tests/fixtures/generic/x509-letsencrypt.json | 2 +- tests/fixtures/generic/x509-multi-cert.json | 2 +- .../generic/x509-string-serialnumber.der | Bin 0 -> 524 bytes .../generic/x509-string-serialnumber.json | 1 + tests/test_x509_cert.py | 14 ++++++++++- 10 files changed, 46 insertions(+), 11 deletions(-) create mode 100644 tests/fixtures/generic/x509-string-serialnumber.der create mode 100644 tests/fixtures/generic/x509-string-serialnumber.json diff --git a/docs/parsers/x509_cert.md b/docs/parsers/x509_cert.md index e5208d85..960da82b 100644 --- a/docs/parsers/x509_cert.md +++ b/docs/parsers/x509_cert.md @@ -32,6 +32,7 @@ Schema: "tbs_certificate": { "version": string, "serial_number": string, # [0] + "serial_number_str": string, "signature": { "algorithm": string, "parameters": string/null, @@ -43,7 +44,9 @@ Schema: "organization_name": array/string, "organizational_unit_name": array/string, "common_name": string, - "email_address": string + "email_address": string, + "serial_number": string, # [0] + "serial_number_str": string }, "validity": { "not_before": integer, # [1] @@ -58,7 +61,9 @@ Schema: "organization_name": array/string, "organizational_unit_name": array/string, "common_name": string, - "email_address": string + "email_address": string, + "serial_number": string, # [0] + "serial_number_str": string }, "subject_public_key_info": { "algorithm": { diff --git a/jc/parsers/x509_cert.py b/jc/parsers/x509_cert.py index 040e3751..0ef3ea05 100644 --- a/jc/parsers/x509_cert.py +++ b/jc/parsers/x509_cert.py @@ -27,6 +27,7 @@ Schema: "tbs_certificate": { "version": string, "serial_number": string, # [0] + "serial_number_str": string, "signature": { "algorithm": string, "parameters": string/null, @@ -38,7 +39,9 @@ Schema: "organization_name": array/string, "organizational_unit_name": array/string, "common_name": string, - "email_address": string + "email_address": string, + "serial_number": string, # [0] + "serial_number_str": string }, "validity": { "not_before": integer, # [1] @@ -53,7 +56,9 @@ Schema: "organization_name": array/string, "organizational_unit_name": array/string, "common_name": string, - "email_address": string + "email_address": string, + "serial_number": string, # [0] + "serial_number_str": string }, "subject_public_key_info": { "algorithm": { @@ -466,7 +471,19 @@ def _fix_objects(obj): if isinstance(obj, dict): for k, v in obj.copy().items(): if k == 'serial_number': - obj.update({k: _b2a(_i2b(v))}) + # according to the spec this field can be string or integer + if isinstance(v, int): + v_str = str(v) + v_hex = _b2a(_i2b(v)) + else: + v_str = str(v) + v_hex = _b2a(v_str.encode()) + obj.update( + { + k: v_hex, + f'{k}_str': v_str + } + ) continue if k == 'modulus': diff --git a/man/jc.1 b/man/jc.1 index 1ac0bd49..cea63963 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2022-12-27 1.22.4 "JSON Convert" +.TH jc 1 2022-12-28 1.22.4 "JSON Convert" .SH NAME \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings .SH SYNOPSIS diff --git a/tests/fixtures/generic/x509-ca-cert.json b/tests/fixtures/generic/x509-ca-cert.json index 44bc52c8..80e07910 100644 --- a/tests/fixtures/generic/x509-ca-cert.json +++ b/tests/fixtures/generic/x509-ca-cert.json @@ -1 +1 @@ -[{"tbs_certificate":{"version":"v3","serial_number":"60:01:97:b7:46:a7:ea:b4:b4:9a:d6:4b:2f:f7:90:fb","signature":{"algorithm":"sha256_rsa","parameters":null},"issuer":{"country_name":"US","organization_name":"thawte, Inc.","organizational_unit_name":["Certification Services Division","(c) 2008 thawte, Inc. - For authorized use only"],"common_name":"thawte Primary Root CA - G3"},"validity":{"not_before":1207094400,"not_after":2143324799,"not_before_iso":"2008-04-02T00:00:00+00:00","not_after_iso":"2037-12-01T23:59:59+00:00"},"subject":{"country_name":"US","organization_name":"thawte, Inc.","organizational_unit_name":["Certification Services Division","(c) 2008 thawte, Inc. - For authorized use only"],"common_name":"thawte Primary Root CA - G3"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"b2:bf:27:2c:fb:db:d8:5b:dd:78:7b:1b:9e:77:66:81:cb:3e:bc:7c:ae:f3:a6:27:9a:34:a3:68:31:71:38:33:62:e4:f3:71:66:79:b1:a9:65:a3:a5:8b:d5:8f:60:2d:3f:42:cc:aa:6b:32:c0:23:cb:2c:41:dd:e4:df:fc:61:9c:e2:73:b2:22:95:11:43:18:5f:c4:b6:1f:57:6c:0a:05:58:22:c8:36:4c:3a:7c:a5:d1:cf:86:af:88:a7:44:02:13:74:71:73:0a:42:59:02:f8:1b:14:6b:42:df:6f:5f:ba:6b:82:a2:9d:5b:e7:4a:bd:1e:01:72:db:4b:74:e8:3b:7f:7f:7d:1f:04:b4:26:9b:e0:b4:5a:ac:47:3d:55:b8:d7:b0:26:52:28:01:31:40:66:d8:d9:24:bd:f6:2a:d8:ec:21:49:5c:9b:f6:7a:e9:7f:55:35:7e:96:6b:8d:93:93:27:cb:92:bb:ea:ac:40:c0:9f:c2:f8:80:cf:5d:f4:5a:dc:ce:74:86:a6:3e:6c:0b:53:ca:bd:92:ce:19:06:72:e6:0c:5c:38:69:c7:04:d6:bc:6c:ce:5b:f6:f7:68:9c:dc:25:15:48:88:a1:e9:a9:f8:98:9c:e0:f3:d5:31:28:61:11:6c:67:96:8d:39:99:cb:c2:45:24:39","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":true,"extn_value":{"ca":true,"path_len_constraint":null}},{"extn_id":"key_usage","critical":true,"extn_value":["crl_sign","key_cert_sign"]},{"extn_id":"key_identifier","critical":false,"extn_value":"ad:6c:aa:94:60:9c:ed:e4:ff:fa:3e:0a:74:2b:63:03:f7:b6:59:bf"}]},"signature_algorithm":{"algorithm":"sha256_rsa","parameters":null},"signature_value":"1a:40:d8:95:65:ac:09:92:89:c6:39:f4:10:e5:a9:0e:66:53:5d:78:de:fa:24:91:bb:e7:44:51:df:c6:16:34:0a:ef:6a:44:51:ea:2b:07:8a:03:7a:c3:eb:3f:0a:2c:52:16:a0:2b:43:b9:25:90:3f:70:a9:33:25:6d:45:1a:28:3b:27:cf:aa:c3:29:42:1b:df:3b:4c:c0:33:34:5b:41:88:bf:6b:2b:65:af:28:ef:b2:f5:c3:aa:66:ce:7b:56:ee:b7:c8:cb:67:c1:c9:9c:1a:18:b8:c4:c3:49:03:f1:60:0e:50:cd:46:c5:f3:77:79:f7:b6:15:e0:38:db:c7:2f:28:a0:0c:3f:77:26:74:d9:25:12:da:31:da:1a:1e:dc:29:41:91:22:3c:69:a7:bb:02:f2:b6:5c:27:03:89:f4:06:ea:9b:e4:72:82:e3:a1:09:c1:e9:00:19:d3:3e:d4:70:6b:ba:71:a6:aa:58:ae:f4:bb:e9:6c:b6:ef:87:cc:9b:bb:ff:39:e6:56:61:d3:0a:a7:c4:5c:4c:60:7b:05:77:26:7a:bf:d8:07:52:2c:62:f7:70:63:d9:39:bc:6f:1c:c2:79:dc:76:29:af:ce:c5:2c:64:04:5e:88:36:6e:31:d4:40:1a:62:34:36:3f:35:01:ae:ac:63:a0"}] +[{"tbs_certificate":{"version":"v3","serial_number":"60:01:97:b7:46:a7:ea:b4:b4:9a:d6:4b:2f:f7:90:fb","signature":{"algorithm":"sha256_rsa","parameters":null},"issuer":{"country_name":"US","organization_name":"thawte, Inc.","organizational_unit_name":["Certification Services Division","(c) 2008 thawte, Inc. - For authorized use only"],"common_name":"thawte Primary Root CA - G3"},"validity":{"not_before":1207094400,"not_after":2143324799,"not_before_iso":"2008-04-02T00:00:00+00:00","not_after_iso":"2037-12-01T23:59:59+00:00"},"subject":{"country_name":"US","organization_name":"thawte, Inc.","organizational_unit_name":["Certification Services Division","(c) 2008 thawte, Inc. - For authorized use only"],"common_name":"thawte Primary Root CA - G3"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"b2:bf:27:2c:fb:db:d8:5b:dd:78:7b:1b:9e:77:66:81:cb:3e:bc:7c:ae:f3:a6:27:9a:34:a3:68:31:71:38:33:62:e4:f3:71:66:79:b1:a9:65:a3:a5:8b:d5:8f:60:2d:3f:42:cc:aa:6b:32:c0:23:cb:2c:41:dd:e4:df:fc:61:9c:e2:73:b2:22:95:11:43:18:5f:c4:b6:1f:57:6c:0a:05:58:22:c8:36:4c:3a:7c:a5:d1:cf:86:af:88:a7:44:02:13:74:71:73:0a:42:59:02:f8:1b:14:6b:42:df:6f:5f:ba:6b:82:a2:9d:5b:e7:4a:bd:1e:01:72:db:4b:74:e8:3b:7f:7f:7d:1f:04:b4:26:9b:e0:b4:5a:ac:47:3d:55:b8:d7:b0:26:52:28:01:31:40:66:d8:d9:24:bd:f6:2a:d8:ec:21:49:5c:9b:f6:7a:e9:7f:55:35:7e:96:6b:8d:93:93:27:cb:92:bb:ea:ac:40:c0:9f:c2:f8:80:cf:5d:f4:5a:dc:ce:74:86:a6:3e:6c:0b:53:ca:bd:92:ce:19:06:72:e6:0c:5c:38:69:c7:04:d6:bc:6c:ce:5b:f6:f7:68:9c:dc:25:15:48:88:a1:e9:a9:f8:98:9c:e0:f3:d5:31:28:61:11:6c:67:96:8d:39:99:cb:c2:45:24:39","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":true,"extn_value":{"ca":true,"path_len_constraint":null}},{"extn_id":"key_usage","critical":true,"extn_value":["crl_sign","key_cert_sign"]},{"extn_id":"key_identifier","critical":false,"extn_value":"ad:6c:aa:94:60:9c:ed:e4:ff:fa:3e:0a:74:2b:63:03:f7:b6:59:bf"}],"serial_number_str":"127614157056681299805556476275995414779"},"signature_algorithm":{"algorithm":"sha256_rsa","parameters":null},"signature_value":"1a:40:d8:95:65:ac:09:92:89:c6:39:f4:10:e5:a9:0e:66:53:5d:78:de:fa:24:91:bb:e7:44:51:df:c6:16:34:0a:ef:6a:44:51:ea:2b:07:8a:03:7a:c3:eb:3f:0a:2c:52:16:a0:2b:43:b9:25:90:3f:70:a9:33:25:6d:45:1a:28:3b:27:cf:aa:c3:29:42:1b:df:3b:4c:c0:33:34:5b:41:88:bf:6b:2b:65:af:28:ef:b2:f5:c3:aa:66:ce:7b:56:ee:b7:c8:cb:67:c1:c9:9c:1a:18:b8:c4:c3:49:03:f1:60:0e:50:cd:46:c5:f3:77:79:f7:b6:15:e0:38:db:c7:2f:28:a0:0c:3f:77:26:74:d9:25:12:da:31:da:1a:1e:dc:29:41:91:22:3c:69:a7:bb:02:f2:b6:5c:27:03:89:f4:06:ea:9b:e4:72:82:e3:a1:09:c1:e9:00:19:d3:3e:d4:70:6b:ba:71:a6:aa:58:ae:f4:bb:e9:6c:b6:ef:87:cc:9b:bb:ff:39:e6:56:61:d3:0a:a7:c4:5c:4c:60:7b:05:77:26:7a:bf:d8:07:52:2c:62:f7:70:63:d9:39:bc:6f:1c:c2:79:dc:76:29:af:ce:c5:2c:64:04:5e:88:36:6e:31:d4:40:1a:62:34:36:3f:35:01:ae:ac:63:a0"}] diff --git a/tests/fixtures/generic/x509-cert-and-key.json b/tests/fixtures/generic/x509-cert-and-key.json index 5adcba84..5a18ec6c 100644 --- a/tests/fixtures/generic/x509-cert-and-key.json +++ b/tests/fixtures/generic/x509-cert-and-key.json @@ -1 +1 @@ -[{"tbs_certificate":{"version":"v3","serial_number":"f7:f9:4e:5f:30:7d:ba:c6","signature":{"algorithm":"sha256_rsa","parameters":null},"issuer":{"country_name":"US","state_or_province_name":"California","locality_name":"San Francisco","organization_name":"BadSSL","common_name":"BadSSL Client Root Certificate Authority"},"validity":{"not_before":1652822124,"not_after":1715894124,"not_before_iso":"2022-05-17T21:15:24+00:00","not_after_iso":"2024-05-16T21:15:24+00:00"},"subject":{"country_name":"US","state_or_province_name":"California","locality_name":"San Francisco","organization_name":"BadSSL","common_name":"BadSSL Client Certificate"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"c7:37:5f:11:eb:1e:4e:cf:eb:ba:48:e5:cb:a3:12:2c:73:3e:46:1d:1e:9c:0d:c0:8b:83:23:da:c7:65:df:5c:77:49:b3:e8:7a:7d:3c:ba:d5:61:8c:f9:a5:c4:85:1d:92:23:06:e3:e7:df:7b:b3:7e:26:d0:cb:1b:be:42:6b:16:69:f4:2c:72:b5:7e:e4:cb:0a:28:44:12:6c:46:74:21:99:03:dc:6b:c3:11:58:02:41:23:3f:b0:fc:bf:b7:00:59:13:22:a5:81:7f:24:fe:d5:53:bc:4d:52:8f:90:4a:46:74:b0:e8:bd:93:a6:cd:90:00:4a:2f:7f:b2:3f:a3:ea:03:3b:01:a0:a2:0d:e6:53:7f:61:12:eb:a6:9b:03:9a:4e:a7:ad:10:e8:e1:1d:c2:0f:ef:09:42:5f:6a:b8:4a:0e:98:bd:b6:3d:cf:ea:a4:e8:cb:d6:38:0e:20:54:84:e7:2d:e0:c1:bc:c3:95:f0:98:a0:02:f9:57:e6:f2:d6:fb:b4:c8:94:a1:4d:32:bc:a2:8e:70:be:98:5c:15:f1:07:69:0f:70:e6:31:60:da:1b:5d:ab:df:54:11:1d:c1:2a:e3:43:b8:bf:b3:7a:3a:86:41:90:96:6f:45:ec:93:c4:b9:58:1b:97:f2:5d:c1:ae:b8:39:82:2a:8d","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"netscape_certificate_type","critical":false,"extn_value":["ssl_client"]},{"extn_id":"key_usage","critical":false,"extn_value":["digital_signature","key_encipherment","non_repudiation"]}]},"signature_algorithm":{"algorithm":"sha256_rsa","parameters":null},"signature_value":"52:34:ca:43:bc:95:21:c5:fa:1d:bd:0c:3b:94:3f:d6:c0:96:ae:3e:7b:61:86:a6:da:94:80:cd:4a:13:2c:e7:11:7d:13:af:0b:c6:63:a9:54:b8:4d:f9:c9:3f:1e:0d:74:ee:db:c9:bf:04:7b:48:6e:18:93:cf:2c:3c:e9:bf:35:48:e0:03:34:1e:11:6c:30:f2:5a:4a:49:f5:d5:54:2d:69:79:c9:a3:bc:a5:73:ea:43:0a:ac:bc:79:09:12:14:40:43:16:95:c5:65:f1:67:f0:6c:b1:33:60:f5:a1:23:68:e6:47:94:52:ef:44:85:85:92:9b:54:ba:61:aa:45:c1:0f:0d:38:6a:4a:f0:47:9d:cf:b3:7e:1c:e1:45:7e:b3:53:54:78:ed:96:7d:89:66:86:49:f6:cd:de:3a:df:69:88:a3:41:1f:7d:60:63:c1:6c:b3:f8:a0:f0:1b:5f:94:d9:a2:19:ee:15:68:06:4f:50:1c:f4:83:f1:9f:13:64:db:47:a0:cc:5b:19:f6:8b:f6:b2:bf:b9:39:16:d9:e6:19:0f:ce:c2:10:15:ea:58:06:58:0c:04:7a:5a:2b:ae:a1:f3:3f:6e:2f:9c:56:0c:7c:85:c2:7f:d0:17:fb:ab:c4:1d:42:fb:fc:4b:96:ff:3c:30:d2:d6:9d:ae:09:25:2c:b6:cc:43:51:df:4b:3e:78:f2:d8:bc:34:b9:81:6d:f2:3a:38:12:4d:64:25:32:e8:a8:8b:e5:5b:24:3a:9e:a5:67:29:3c:34:57:34:c0:b2:b2:6e:80:b5:96:0e:69:7f:fb:e0:f0:36:98:2d:93:fd:1c:2f:28:30:c9:31:9b:3a:3f:48:bb:fd:e8:83:40:59:05:64:74:35:d7:5e:17:b1:6f:5a:ab:63:24:8f:d0:51:58:c8:2c:ab:a8:84:aa:44:b2:13:09:51:26:3b:6e:35:7d:85:41:45:24:54:a9:92:7f:8f:d6:e9:20:03:06:45:64:d6:58:f3:d1:7e:01:7e:16:0b:45:e1:b9:a1:e3:2c:43:ff:1c:9a:aa:e4:c7:82:cb:80:86:d7:3f:17:2c:96:31:93:1b:d4:41:64:24:c0:36:6e:14:b9:ed:eb:da:6d:48:52:1f:31:c1:11:c0:69:71:e0:04:97:11:4f:a4:c6:fc:3a:69:93:b9:02:0a:e0:d2:6b:9e:88:0e:69:1a:e0:fd:17:37:80:01:f4:d0:27:c3:01:f4:64:c5:fc:44:ca:d7:e9:75:55:be:61:fd:5d:7c:ee:47:1d:5b:f6:15:d8:5e:00:dd:23:b3:fa:95:f4:61:79:04:6a:b6:82:97:6c:ab:be:78:c1:8d"}] +[{"tbs_certificate":{"version":"v3","serial_number":"f7:f9:4e:5f:30:7d:ba:c6","signature":{"algorithm":"sha256_rsa","parameters":null},"issuer":{"country_name":"US","state_or_province_name":"California","locality_name":"San Francisco","organization_name":"BadSSL","common_name":"BadSSL Client Root Certificate Authority"},"validity":{"not_before":1652822124,"not_after":1715894124,"not_before_iso":"2022-05-17T21:15:24+00:00","not_after_iso":"2024-05-16T21:15:24+00:00"},"subject":{"country_name":"US","state_or_province_name":"California","locality_name":"San Francisco","organization_name":"BadSSL","common_name":"BadSSL Client Certificate"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"c7:37:5f:11:eb:1e:4e:cf:eb:ba:48:e5:cb:a3:12:2c:73:3e:46:1d:1e:9c:0d:c0:8b:83:23:da:c7:65:df:5c:77:49:b3:e8:7a:7d:3c:ba:d5:61:8c:f9:a5:c4:85:1d:92:23:06:e3:e7:df:7b:b3:7e:26:d0:cb:1b:be:42:6b:16:69:f4:2c:72:b5:7e:e4:cb:0a:28:44:12:6c:46:74:21:99:03:dc:6b:c3:11:58:02:41:23:3f:b0:fc:bf:b7:00:59:13:22:a5:81:7f:24:fe:d5:53:bc:4d:52:8f:90:4a:46:74:b0:e8:bd:93:a6:cd:90:00:4a:2f:7f:b2:3f:a3:ea:03:3b:01:a0:a2:0d:e6:53:7f:61:12:eb:a6:9b:03:9a:4e:a7:ad:10:e8:e1:1d:c2:0f:ef:09:42:5f:6a:b8:4a:0e:98:bd:b6:3d:cf:ea:a4:e8:cb:d6:38:0e:20:54:84:e7:2d:e0:c1:bc:c3:95:f0:98:a0:02:f9:57:e6:f2:d6:fb:b4:c8:94:a1:4d:32:bc:a2:8e:70:be:98:5c:15:f1:07:69:0f:70:e6:31:60:da:1b:5d:ab:df:54:11:1d:c1:2a:e3:43:b8:bf:b3:7a:3a:86:41:90:96:6f:45:ec:93:c4:b9:58:1b:97:f2:5d:c1:ae:b8:39:82:2a:8d","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"netscape_certificate_type","critical":false,"extn_value":["ssl_client"]},{"extn_id":"key_usage","critical":false,"extn_value":["digital_signature","key_encipherment","non_repudiation"]}],"serial_number_str":"17868399167311559366"},"signature_algorithm":{"algorithm":"sha256_rsa","parameters":null},"signature_value":"52:34:ca:43:bc:95:21:c5:fa:1d:bd:0c:3b:94:3f:d6:c0:96:ae:3e:7b:61:86:a6:da:94:80:cd:4a:13:2c:e7:11:7d:13:af:0b:c6:63:a9:54:b8:4d:f9:c9:3f:1e:0d:74:ee:db:c9:bf:04:7b:48:6e:18:93:cf:2c:3c:e9:bf:35:48:e0:03:34:1e:11:6c:30:f2:5a:4a:49:f5:d5:54:2d:69:79:c9:a3:bc:a5:73:ea:43:0a:ac:bc:79:09:12:14:40:43:16:95:c5:65:f1:67:f0:6c:b1:33:60:f5:a1:23:68:e6:47:94:52:ef:44:85:85:92:9b:54:ba:61:aa:45:c1:0f:0d:38:6a:4a:f0:47:9d:cf:b3:7e:1c:e1:45:7e:b3:53:54:78:ed:96:7d:89:66:86:49:f6:cd:de:3a:df:69:88:a3:41:1f:7d:60:63:c1:6c:b3:f8:a0:f0:1b:5f:94:d9:a2:19:ee:15:68:06:4f:50:1c:f4:83:f1:9f:13:64:db:47:a0:cc:5b:19:f6:8b:f6:b2:bf:b9:39:16:d9:e6:19:0f:ce:c2:10:15:ea:58:06:58:0c:04:7a:5a:2b:ae:a1:f3:3f:6e:2f:9c:56:0c:7c:85:c2:7f:d0:17:fb:ab:c4:1d:42:fb:fc:4b:96:ff:3c:30:d2:d6:9d:ae:09:25:2c:b6:cc:43:51:df:4b:3e:78:f2:d8:bc:34:b9:81:6d:f2:3a:38:12:4d:64:25:32:e8:a8:8b:e5:5b:24:3a:9e:a5:67:29:3c:34:57:34:c0:b2:b2:6e:80:b5:96:0e:69:7f:fb:e0:f0:36:98:2d:93:fd:1c:2f:28:30:c9:31:9b:3a:3f:48:bb:fd:e8:83:40:59:05:64:74:35:d7:5e:17:b1:6f:5a:ab:63:24:8f:d0:51:58:c8:2c:ab:a8:84:aa:44:b2:13:09:51:26:3b:6e:35:7d:85:41:45:24:54:a9:92:7f:8f:d6:e9:20:03:06:45:64:d6:58:f3:d1:7e:01:7e:16:0b:45:e1:b9:a1:e3:2c:43:ff:1c:9a:aa:e4:c7:82:cb:80:86:d7:3f:17:2c:96:31:93:1b:d4:41:64:24:c0:36:6e:14:b9:ed:eb:da:6d:48:52:1f:31:c1:11:c0:69:71:e0:04:97:11:4f:a4:c6:fc:3a:69:93:b9:02:0a:e0:d2:6b:9e:88:0e:69:1a:e0:fd:17:37:80:01:f4:d0:27:c3:01:f4:64:c5:fc:44:ca:d7:e9:75:55:be:61:fd:5d:7c:ee:47:1d:5b:f6:15:d8:5e:00:dd:23:b3:fa:95:f4:61:79:04:6a:b6:82:97:6c:ab:be:78:c1:8d"}] diff --git a/tests/fixtures/generic/x509-letsencrypt.json b/tests/fixtures/generic/x509-letsencrypt.json index b2d3265b..92dd9456 100644 --- a/tests/fixtures/generic/x509-letsencrypt.json +++ b/tests/fixtures/generic/x509-letsencrypt.json @@ -1 +1 @@ -[{"tbs_certificate":{"version":"v3","serial_number":"04:c1:47:a5:16:71:a8:ad:84:6f:e5:cf:ec:ca:42:cc:c2:ad","signature":{"algorithm":"sha256_rsa","parameters":null},"issuer":{"country_name":"US","organization_name":"Let's Encrypt","common_name":"R3"},"validity":{"not_before":1655260836,"not_after":1663036835,"not_before_iso":"2022-06-15T02:40:36+00:00","not_after_iso":"2022-09-13T02:40:35+00:00"},"subject":{"common_name":"tls.automattic.com"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"c2:4b:45:fe:e7:f1:ec:68:7c:dd:e5:9b:b6:2d:2b:31:dd:5c:9f:d4:4f:07:19:d7:d6:c5:6a:6b:44:38:ec:e7:c5:88:ff:88:f2:75:46:ef:10:e5:28:9d:2d:cf:3f:60:d1:65:b9:69:44:9b:59:99:fb:8b:00:cd:71:88:87:0a:30:2a:17:bf:5d:97:e1:c0:56:98:ad:87:c5:00:9e:c6:bd:25:78:de:9e:d7:ee:53:5a:9f:16:23:51:5e:f3:a8:09:42:70:d1:2d:6f:11:6e:94:7e:db:1d:45:fc:0a:0d:f9:e5:a2:87:33:f4:71:d2:39:e5:22:22:9b:86:31:97:b5:3d:d1:35:68:a2:8d:75:2e:4c:ae:14:2b:51:cd:90:cf:d6:43:d4:49:80:3f:42:ab:1f:21:37:05:1e:ea:08:0d:e0:4d:e0:b6:cc:48:bb:f4:7e:8e:e9:0d:3a:02:85:89:ae:d0:f4:9a:f7:85:6b:0d:58:c9:1f:a6:db:ac:0c:d5:3d:62:b8:45:a8:77:31:3f:51:c6:84:dc:fe:1c:d8:b5:a3:93:2e:78:9d:e4:fe:72:7a:81:e9:6f:26:fe:4c:61:3a:55:6d:bd:f8:4a:38:68:5e:97:e3:36:c3:d6:bc:31:2b:c7:c8:ad:ee:64:56:3f:0f:ea:4b:f9:a5:b1:6b","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"key_usage","critical":true,"extn_value":["digital_signature","key_encipherment"]},{"extn_id":"extended_key_usage","critical":false,"extn_value":["server_auth","client_auth"]},{"extn_id":"basic_constraints","critical":true,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"key_identifier","critical":false,"extn_value":"63:cf:50:5f:d2:3e:a0:75:61:86:d9:60:1b:ec:d9:d8:dd:c7:30:5a"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"14:2e:b3:17:b7:58:56:cb:ae:50:09:40:e6:1f:af:9d:8b:14:c2:c6","authority_cert_issuer":null,"authority_cert_serial_number":null}},{"extn_id":"authority_information_access","critical":false,"extn_value":[{"access_method":"ocsp","access_location":"http://r3.o.lencr.org"},{"access_method":"ca_issuers","access_location":"http://r3.i.lencr.org/"}]},{"extn_id":"subject_alt_name","critical":false,"extn_value":["baffl.ca","blog.kellybrazil.com","bro-pa.org","competence.game.blog","dirtyroulette366.game.blog","giftsbypearl.com","giuman.me","globaltransactorsltd.com","grilltimerestaurants.com","gureametsetakolorategia.com","happyluckyenjoy.blog","healthbasedbeauty.fitness.blog","healthisknowledge.com","imake3ddesigns.com","javascript.game.blog","journeyingbacktowellness.health.blog","jquery.game.blog","kahlertregionalcancer.org","karmadesignstudios.graphics","noticia.science.blog","reyvingamer.game.blog","sailingresferber.co.uk","stardust.game.blog","sweetlove.fashion.blog","tls.automattic.com","wanderlustwatts.com","www.angelguardians.legal","www.baffl.ca","www.blog.kellybrazil.com","www.bro-pa.org","www.competence.game.blog","www.dirtyroulette366.game.blog","www.giftsbypearl.com","www.giuman.me","www.globaltransactorsltd.com","www.grilltimerestaurants.com","www.gureametsetakolorategia.com","www.happyluckyenjoy.blog","www.healthbasedbeauty.fitness.blog","www.healthisknowledge.com","www.imake3ddesigns.com","www.javascript.game.blog","www.journeyingbacktowellness.health.blog","www.jquery.game.blog","www.kahlertregionalcancer.org","www.karmadesignstudios.graphics","www.reyvingamer.game.blog","www.ruplayingboard.game.blog","www.stardust.game.blog","www.sweetlove.fashion.blog"]},{"extn_id":"certificate_policies","critical":false,"extn_value":[{"policy_identifier":"2.23.140.1.2.1","policy_qualifiers":null},{"policy_identifier":"1.3.6.1.4.1.44947.1.1.1","policy_qualifiers":[{"policy_qualifier_id":"certification_practice_statement","qualifier":"http://cps.letsencrypt.org"}]}]},{"extn_id":"signed_certificate_timestamp_list","critical":false,"extn_value":"00:f0:00:76:00:46:a5:55:eb:75:fa:91:20:30:b5:a2:89:69:f4:f3:7d:11:2c:41:74:be:fd:49:b8:85:ab:f2:fc:70:fe:6d:47:00:00:01:81:65:72:e3:44:00:00:04:03:00:47:30:45:02:21:00:de:61:59:6e:de:1c:75:7c:28:d7:3e:78:80:d3:85:5f:8a:ff:93:85:8d:e3:4a:e4:f7:2f:99:1d:36:b4:c4:62:02:20:6a:cd:23:6b:f4:27:41:a4:1d:9c:95:cd:36:be:2d:24:9b:87:aa:3c:14:15:70:5f:f5:e9:fa:d1:14:dc:df:da:00:76:00:6f:53:76:ac:31:f0:31:19:d8:99:00:a4:51:15:ff:77:15:1c:11:d9:02:c1:00:29:06:8d:b2:08:9a:37:d9:13:00:00:01:81:65:72:e4:18:00:00:04:03:00:47:30:45:02:21:00:f5:40:50:bc:99:c2:e9:a2:74:7a:83:f9:ec:6b:e5:c5:89:62:5a:37:b0:0c:51:e4:1c:11:f7:8b:bb:a4:97:d9:02:20:07:35:4e:5d:41:3b:ef:83:9f:18:e0:58:60:06:63:51:22:2d:8c:82:ae:b2:5e:a7:c2:5c:f8:2d:4b:50:14:86"}]},"signature_algorithm":{"algorithm":"sha256_rsa","parameters":null},"signature_value":"a8:ad:cb:4f:b7:ec:b9:d1:c0:50:8e:dd:e1:14:93:cb:be:e5:6e:45:07:2a:eb:92:f0:be:3d:bd:0e:e6:45:c7:8d:79:3e:09:d7:40:48:d6:8a:22:43:10:39:14:11:e8:f9:b8:a5:52:32:90:0c:92:94:74:57:bf:6e:3d:b0:3f:ce:a1:06:5f:9b:e2:0a:da:5a:ff:83:4f:28:2b:ac:cb:67:03:0b:7b:86:aa:d7:60:c2:4b:6f:fd:66:bd:8d:61:d1:48:24:29:5c:43:49:4e:79:2a:8e:3a:46:3b:ce:9b:f6:e5:9f:dc:ed:c8:ad:d4:a6:ee:e3:33:af:4c:34:41:27:de:b8:d5:63:df:45:8f:a7:11:78:71:28:a7:d8:29:5f:0b:8b:e2:07:44:c9:54:be:e0:a3:77:82:2d:07:5c:f0:4e:0a:11:06:6b:dc:90:f7:df:fb:60:28:96:f0:81:dc:4d:24:a8:53:0f:e3:d0:f0:22:fa:5e:a7:eb:a9:e4:5f:d4:cc:70:b7:c5:b9:7c:4b:e9:3a:aa:1b:a8:c8:2a:b2:87:79:d5:6d:63:b4:2e:7e:d7:24:9a:fc:0d:8f:ac:04:bb:98:ce:05:ae:6f:07:0b:49:cd:d6:ad:f9:37:7d:ff:1b:fc:e6:3a:25:9b:ea:d3:b8:bb:a7:83:44:84:6d"}] +[{"tbs_certificate":{"version":"v3","serial_number":"04:c1:47:a5:16:71:a8:ad:84:6f:e5:cf:ec:ca:42:cc:c2:ad","signature":{"algorithm":"sha256_rsa","parameters":null},"issuer":{"country_name":"US","organization_name":"Let's Encrypt","common_name":"R3"},"validity":{"not_before":1655260836,"not_after":1663036835,"not_before_iso":"2022-06-15T02:40:36+00:00","not_after_iso":"2022-09-13T02:40:35+00:00"},"subject":{"common_name":"tls.automattic.com"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"c2:4b:45:fe:e7:f1:ec:68:7c:dd:e5:9b:b6:2d:2b:31:dd:5c:9f:d4:4f:07:19:d7:d6:c5:6a:6b:44:38:ec:e7:c5:88:ff:88:f2:75:46:ef:10:e5:28:9d:2d:cf:3f:60:d1:65:b9:69:44:9b:59:99:fb:8b:00:cd:71:88:87:0a:30:2a:17:bf:5d:97:e1:c0:56:98:ad:87:c5:00:9e:c6:bd:25:78:de:9e:d7:ee:53:5a:9f:16:23:51:5e:f3:a8:09:42:70:d1:2d:6f:11:6e:94:7e:db:1d:45:fc:0a:0d:f9:e5:a2:87:33:f4:71:d2:39:e5:22:22:9b:86:31:97:b5:3d:d1:35:68:a2:8d:75:2e:4c:ae:14:2b:51:cd:90:cf:d6:43:d4:49:80:3f:42:ab:1f:21:37:05:1e:ea:08:0d:e0:4d:e0:b6:cc:48:bb:f4:7e:8e:e9:0d:3a:02:85:89:ae:d0:f4:9a:f7:85:6b:0d:58:c9:1f:a6:db:ac:0c:d5:3d:62:b8:45:a8:77:31:3f:51:c6:84:dc:fe:1c:d8:b5:a3:93:2e:78:9d:e4:fe:72:7a:81:e9:6f:26:fe:4c:61:3a:55:6d:bd:f8:4a:38:68:5e:97:e3:36:c3:d6:bc:31:2b:c7:c8:ad:ee:64:56:3f:0f:ea:4b:f9:a5:b1:6b","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"key_usage","critical":true,"extn_value":["digital_signature","key_encipherment"]},{"extn_id":"extended_key_usage","critical":false,"extn_value":["server_auth","client_auth"]},{"extn_id":"basic_constraints","critical":true,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"key_identifier","critical":false,"extn_value":"63:cf:50:5f:d2:3e:a0:75:61:86:d9:60:1b:ec:d9:d8:dd:c7:30:5a"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"14:2e:b3:17:b7:58:56:cb:ae:50:09:40:e6:1f:af:9d:8b:14:c2:c6","authority_cert_issuer":null,"authority_cert_serial_number":null}},{"extn_id":"authority_information_access","critical":false,"extn_value":[{"access_method":"ocsp","access_location":"http://r3.o.lencr.org"},{"access_method":"ca_issuers","access_location":"http://r3.i.lencr.org/"}]},{"extn_id":"subject_alt_name","critical":false,"extn_value":["baffl.ca","blog.kellybrazil.com","bro-pa.org","competence.game.blog","dirtyroulette366.game.blog","giftsbypearl.com","giuman.me","globaltransactorsltd.com","grilltimerestaurants.com","gureametsetakolorategia.com","happyluckyenjoy.blog","healthbasedbeauty.fitness.blog","healthisknowledge.com","imake3ddesigns.com","javascript.game.blog","journeyingbacktowellness.health.blog","jquery.game.blog","kahlertregionalcancer.org","karmadesignstudios.graphics","noticia.science.blog","reyvingamer.game.blog","sailingresferber.co.uk","stardust.game.blog","sweetlove.fashion.blog","tls.automattic.com","wanderlustwatts.com","www.angelguardians.legal","www.baffl.ca","www.blog.kellybrazil.com","www.bro-pa.org","www.competence.game.blog","www.dirtyroulette366.game.blog","www.giftsbypearl.com","www.giuman.me","www.globaltransactorsltd.com","www.grilltimerestaurants.com","www.gureametsetakolorategia.com","www.happyluckyenjoy.blog","www.healthbasedbeauty.fitness.blog","www.healthisknowledge.com","www.imake3ddesigns.com","www.javascript.game.blog","www.journeyingbacktowellness.health.blog","www.jquery.game.blog","www.kahlertregionalcancer.org","www.karmadesignstudios.graphics","www.reyvingamer.game.blog","www.ruplayingboard.game.blog","www.stardust.game.blog","www.sweetlove.fashion.blog"]},{"extn_id":"certificate_policies","critical":false,"extn_value":[{"policy_identifier":"2.23.140.1.2.1","policy_qualifiers":null},{"policy_identifier":"1.3.6.1.4.1.44947.1.1.1","policy_qualifiers":[{"policy_qualifier_id":"certification_practice_statement","qualifier":"http://cps.letsencrypt.org"}]}]},{"extn_id":"signed_certificate_timestamp_list","critical":false,"extn_value":"00:f0:00:76:00:46:a5:55:eb:75:fa:91:20:30:b5:a2:89:69:f4:f3:7d:11:2c:41:74:be:fd:49:b8:85:ab:f2:fc:70:fe:6d:47:00:00:01:81:65:72:e3:44:00:00:04:03:00:47:30:45:02:21:00:de:61:59:6e:de:1c:75:7c:28:d7:3e:78:80:d3:85:5f:8a:ff:93:85:8d:e3:4a:e4:f7:2f:99:1d:36:b4:c4:62:02:20:6a:cd:23:6b:f4:27:41:a4:1d:9c:95:cd:36:be:2d:24:9b:87:aa:3c:14:15:70:5f:f5:e9:fa:d1:14:dc:df:da:00:76:00:6f:53:76:ac:31:f0:31:19:d8:99:00:a4:51:15:ff:77:15:1c:11:d9:02:c1:00:29:06:8d:b2:08:9a:37:d9:13:00:00:01:81:65:72:e4:18:00:00:04:03:00:47:30:45:02:21:00:f5:40:50:bc:99:c2:e9:a2:74:7a:83:f9:ec:6b:e5:c5:89:62:5a:37:b0:0c:51:e4:1c:11:f7:8b:bb:a4:97:d9:02:20:07:35:4e:5d:41:3b:ef:83:9f:18:e0:58:60:06:63:51:22:2d:8c:82:ae:b2:5e:a7:c2:5c:f8:2d:4b:50:14:86"}],"serial_number_str":"414218872914682494204143697900622362493613"},"signature_algorithm":{"algorithm":"sha256_rsa","parameters":null},"signature_value":"a8:ad:cb:4f:b7:ec:b9:d1:c0:50:8e:dd:e1:14:93:cb:be:e5:6e:45:07:2a:eb:92:f0:be:3d:bd:0e:e6:45:c7:8d:79:3e:09:d7:40:48:d6:8a:22:43:10:39:14:11:e8:f9:b8:a5:52:32:90:0c:92:94:74:57:bf:6e:3d:b0:3f:ce:a1:06:5f:9b:e2:0a:da:5a:ff:83:4f:28:2b:ac:cb:67:03:0b:7b:86:aa:d7:60:c2:4b:6f:fd:66:bd:8d:61:d1:48:24:29:5c:43:49:4e:79:2a:8e:3a:46:3b:ce:9b:f6:e5:9f:dc:ed:c8:ad:d4:a6:ee:e3:33:af:4c:34:41:27:de:b8:d5:63:df:45:8f:a7:11:78:71:28:a7:d8:29:5f:0b:8b:e2:07:44:c9:54:be:e0:a3:77:82:2d:07:5c:f0:4e:0a:11:06:6b:dc:90:f7:df:fb:60:28:96:f0:81:dc:4d:24:a8:53:0f:e3:d0:f0:22:fa:5e:a7:eb:a9:e4:5f:d4:cc:70:b7:c5:b9:7c:4b:e9:3a:aa:1b:a8:c8:2a:b2:87:79:d5:6d:63:b4:2e:7e:d7:24:9a:fc:0d:8f:ac:04:bb:98:ce:05:ae:6f:07:0b:49:cd:d6:ad:f9:37:7d:ff:1b:fc:e6:3a:25:9b:ea:d3:b8:bb:a7:83:44:84:6d"}] diff --git a/tests/fixtures/generic/x509-multi-cert.json b/tests/fixtures/generic/x509-multi-cert.json index c78efc70..9eac0a10 100644 --- a/tests/fixtures/generic/x509-multi-cert.json +++ b/tests/fixtures/generic/x509-multi-cert.json @@ -1 +1 @@ -[{"tbs_certificate":{"version":"v3","serial_number":"01","signature":{"algorithm":"sha1_rsa","parameters":null},"issuer":{"country_name":"FR","state_or_province_name":"Alsace","locality_name":"Strasbourg","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"Freelan Sample Certificate Authority","email_address":"contact@freelan.org"},"validity":{"not_before":1335522678,"not_after":1650882678,"not_before_iso":"2012-04-27T10:31:18+00:00","not_after_iso":"2022-04-25T10:31:18+00:00"},"subject":{"country_name":"FR","state_or_province_name":"Alsace","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"alice","email_address":"contact@freelan.org"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"dd:6d:bd:f8:80:fa:d7:de:1b:1f:a7:a3:2e:b2:02:e2:16:f6:52:0a:3c:bf:a6:42:f8:ca:dc:93:67:4d:60:c3:4f:8d:c3:8a:00:1b:f1:c4:4b:41:6a:69:d2:69:e5:3f:21:8e:c5:0b:f8:22:37:ad:b6:2c:4b:55:ff:7a:03:72:bb:9a:d3:ec:96:b9:56:9f:cb:19:99:c9:32:94:6f:8f:c6:52:06:9f:45:03:df:fd:e8:97:f6:ea:d6:ba:bb:48:2b:b5:e0:34:61:4d:52:36:0f:ab:87:52:25:03:cf:87:00:87:13:f2:ca:03:29:16:9d:90:57:46:b5:f4:0e:ae:17:c8:0a:4d:92:ed:08:a6:32:23:11:71:fe:f2:2c:44:d7:6c:07:f3:0b:7b:0c:4b:dd:3b:b4:f7:37:70:9f:51:b6:88:4e:5d:6a:05:7f:8d:9b:66:7a:ab:80:20:fe:ee:6b:97:c3:49:7d:78:3b:d5:99:97:03:75:ce:8f:bc:c5:be:9c:9a:a5:12:19:70:f9:a4:bd:96:27:ed:23:02:a7:c7:57:c9:71:cf:76:94:a2:21:62:f6:b8:1d:ca:88:ee:09:ad:46:2f:b7:61:b3:2c:15:13:86:9f:a5:35:26:5a:67:f4:37:c8:e6:80:01:49:0e:c7:ed:61:d3:cd:bc:e4:f8:be:3f:c9:4e:f8:7d:97:89:ce:12:bc:ca:b5:c6:d2:e0:d9:b3:68:3c:2e:4a:9d:b4:5f:b8:53:ee:50:3d:bf:dd:d4:a2:8a:b6:a0:27:ab:98:0c:b3:b2:58:90:e2:bc:a1:ad:ff:bd:8e:55:31:0f:00:bf:68:e9:3d:a9:19:9a:f0:6d:0b:a2:14:6a:c6:4c:c6:4e:bd:63:12:a5:0b:4d:97:eb:42:09:79:53:e2:65:aa:24:34:70:b8:c1:ab:23:80:e7:9c:6c:ed:dc:82:aa:37:04:b8:43:2a:3d:2a:a8:cc:20:fc:27:5d:90:26:58:f9:b7:14:e2:9e:e2:c1:70:73:97:e9:6b:02:8e:d3:52:59:7b:00:ec:61:30:f1:56:3f:9c:c1:7c:05:c5:b1:36:c8:18:85:cf:61:40:1f:07:e8:a7:06:87:df:9a:77:0b:a9:64:72:03:f6:93:fc:e0:02:59:c1:96:ec:c0:09:42:3e:30:a2:7f:1b:48:2f:fe:e0:21:8f:53:87:25:0d:cb:ea:49:f5:4a:9b:d0:e3:5f:ee:78:18:e5:ba:71:31:a9:04:98:0f:b1:ad:67:52:a0:f2:e3:9c:ab:6a:fe:58:84:84:dd:07:3d:32:94:05:16:45:15:96:59:a0:58:6c:18:0e:e3:77:66:c7:b3:f7:99","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"2.16.840.1.113730.1.13","critical":false,"extn_value":"16:1d:4f:70:65:6e:53:53:4c:20:47:65:6e:65:72:61:74:65:64:20:43:65:72:74:69:66:69:63:61:74:65"},{"extn_id":"key_identifier","critical":false,"extn_value":"59:5f:c9:13:ba:1b:cc:b9:a8:41:4a:8a:49:79:6a:36:f6:7d:3e:d7"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53","authority_cert_issuer":null,"authority_cert_serial_number":null}}]},"signature_algorithm":{"algorithm":"sha1_rsa","parameters":null},"signature_value":"13:e7:02:45:3e:a7:ab:bd:b8:da:e7:ef:74:88:ac:62:d5:dd:10:56:d5:46:07:ec:fa:6a:80:0c:b9:62:be:aa:08:b4:be:0b:eb:9a:ef:68:b7:69:6f:4d:20:92:9d:18:63:7a:23:f4:48:87:6a:14:c3:91:98:1b:4e:08:59:3f:91:80:e9:f4:cf:fd:d5:bf:af:4b:e4:bd:78:09:71:ac:d0:81:e5:53:9f:3e:ac:44:3e:9f:f0:bf:5a:c1:70:4e:06:04:ef:dc:e8:77:05:a2:7d:c5:fa:80:58:0a:c5:10:6d:90:ca:49:26:71:84:39:b7:9a:3e:e9:6f:ae:c5:35:b6:5b:24:8c:c9:ef:41:c3:b1:17:b6:3b:4e:28:89:3c:7e:87:a8:3a:a5:6d:dc:39:03:20:20:0b:c5:80:a3:79:13:1e:f6:ec:ae:36:df:40:74:34:87:46:93:3b:a3:e0:a4:8c:2f:43:4c:b2:54:80:71:76:78:d4:ea:12:28:d8:f2:e3:80:55:11:9b:f4:65:dc:53:0e:b4:4c:e0:4c:09:b4:dc:a0:80:5c:e6:b5:3b:95:d3:69:e4:52:3d:5b:61:86:02:e5:fd:0b:00:3a:fa:b3:45:cc:c9:a3:64:f2:dc:25:59:89:58:0d:9e:6e:28:3a:55:45:50:5f:88:67:2a:d2:e2:48:cc:8b:de:9a:1b:93:ae:87:e1:f2:90:50:40:d9:0f:44:31:53:46:ad:62:4e:8d:48:86:19:77:fc:59:75:91:79:35:59:1d:e3:4e:33:5b:e2:31:d7:ee:52:28:5f:0a:70:a7:be:bb:1c:03:ca:1a:18:d0:f5:c1:5b:9c:73:04:b6:4a:e8:46:52:58:76:d4:6a:e6:67:1c:0e:dc:13:d0:61:72:a0:92:cb:05:97:47:1c:c1:c9:cf:41:7d:1f:b1:4d:93:6b:53:41:03:21:2b:93:15:63:08:3e:2c:86:9e:7b:9f:3a:09:05:6a:7d:bb:1c:a7:b7:af:96:08:cb:5b:df:07:fb:9c:f2:95:11:c0:82:81:f6:1b:bf:5a:1e:58:cd:28:ca:7d:04:eb:aa:e9:29:c4:82:51:2c:89:61:95:b6:ed:a5:86:7c:7c:48:1d:ec:54:96:47:79:ea:fc:7f:f5:10:43:0a:9b:00:ef:8a:77:2e:f4:36:66:d2:6a:a6:95:b6:9f:23:3b:12:e2:89:d5:a4:c1:2c:91:4e:cb:94:e8:3f:22:0e:21:f9:b8:4a:81:5c:4c:63:ae:3d:05:b2:5c:5c:54:a7:55:8f:98:25:55:c4:a6:90:bc:19:29:b1:14:d4:e2:b0:95:e4:ff:89:71:61:be:8a:16:85"},{"tbs_certificate":{"version":"v3","serial_number":"02","signature":{"algorithm":"sha1_rsa","parameters":null},"issuer":{"country_name":"FR","state_or_province_name":"Alsace","locality_name":"Strasbourg","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"Freelan Sample Certificate Authority","email_address":"contact@freelan.org"},"validity":{"not_before":1335524080,"not_after":1650884080,"not_before_iso":"2012-04-27T10:54:40+00:00","not_after_iso":"2022-04-25T10:54:40+00:00"},"subject":{"country_name":"FR","state_or_province_name":"Alsace","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"bob","email_address":"contact@freelan.org"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"c2:3f:43:14:4a:d4:dd:43:5a:b9:43:5e:2d:bb:89:a1:17:18:f7:ae:47:4b:7a:f4:d4:dc:a3:e1:b7:85:3a:10:20:eb:bc:51:18:d8:8b:25:c6:04:95:4f:80:e9:05:5c:00:f4:7c:23:7b:d1:ad:81:58:f1:9d:43:c3:37:ee:7f:61:03:b5:ff:29:bb:10:1a:fb:a8:77:97:9b:de:4c:7d:3f:ca:ff:53:8c:37:30:b6:88:f2:0e:be:7c:dc:92:76:c9:5f:22:96:19:0b:91:ea:9c:18:96:9f:43:d1:9d:22:9e:d9:c3:12:9f:80:05:85:1f:70:bb:87:5d:63:c1:5a:51:3d:7e:69:3d:76:6d:b0:56:ea:db:3f:ae:f0:cd:0c:19:48:b1:f2:d5:2e:e7:fa:12:dd:15:bc:8c:dc:09:c2:26:9c:dc:22:52:8e:c8:1c:c1:cd:01:bd:1a:24:c5:be:4f:18:08:f3:de:59:1c:8f:63:a6:63:1d:4f:5a:92:68:7a:49:94:26:54:d1:83:be:16:e4:5e:8f:73:2f:81:3a:3a:30:80:fd:57:a9:7f:1b:7b:e5:0f:6c:01:68:f7:1f:45:49:fe:06:3c:08:57:64:27:a5:0b:55:18:b7:30:be:08:45:70:8b:cd:43:ea:fc:80:1e:03:5c:c3:52:8d:a9:55:53:55:f4:61:2e:8b:50:64:6a:30:a7:6f:bd:b8:80:12:ee:66:98:d8:78:5f:a0:f5:65:6a:6d:f5:09:cc:62:4d:55:56:80:21:75:48:73:4d:b9:e3:f9:1d:96:c9:2c:5d:79:4d:3c:c5:7a:9e:84:ff:9d:c7:94:87:0a:3e:69:81:d2:7f:c0:5f:67:9c:06:8c:33:5c:a3:9f:52:e7:04:c7:d3:81:ef:b2:77:1e:d0:57:1f:1f:90:a5:69:c0:0d:43:c5:f6:a6:7e:f7:ea:45:7c:60:b6:68:1f:64:59:dc:60:33:c2:13:8c:b7:06:c2:2a:cd:cc:2b:02:de:a2:e9:70:0c:db:79:fe:ce:eb:5e:c0:06:eb:76:43:09:e0:2a:c7:ee:1e:6a:af:60:49:73:3c:a8:53:8c:e1:39:2c:e7:9e:fe:fd:44:20:f0:85:9a:1f:eb:c7:40:c8:5b:90:43:e6:a1:6a:00:50:4b:73:73:72:c5:39:77:13:1e:3c:95:be:a9:37:6a:d1:4e:34:3d:34:ec:87:f8:1e:6c:e7:dc:8b:7f:8e:d1:3c:78:c2:e2:09:93:d7:c0:68:ae:70:81:b9:f0:d0:f7:26:a4:e2:c0:12:1d:2f:01:63:eb:53:05:cb:aa:db:66:b0:fb:16:9b:e7:e7:be:c3:66:da:5c:c9","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"2.16.840.1.113730.1.13","critical":false,"extn_value":"16:1d:4f:70:65:6e:53:53:4c:20:47:65:6e:65:72:61:74:65:64:20:43:65:72:74:69:66:69:63:61:74:65"},{"extn_id":"key_identifier","critical":false,"extn_value":"9c:d2:71:50:35:f7:10:43:dd:e8:ce:75:29:a3:53:5d:11:a7:a8:3b"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53","authority_cert_issuer":null,"authority_cert_serial_number":null}}]},"signature_algorithm":{"algorithm":"sha1_rsa","parameters":null},"signature_value":"c3:b0:a4:82:f5:64:e5:4e:a0:e5:74:5e:c4:3d:d0:9c:f7:4e:f7:8d:af:8b:2e:80:59:63:b5:6e:2f:10:5b:66:d6:29:2a:ca:e2:01:20:68:e1:2b:ff:d6:e1:e1:f2:a6:e0:cc:f5:8f:9f:5c:72:b8:fa:81:76:7d:5c:ee:60:29:e5:d7:de:8f:4a:9c:55:3e:e5:27:1c:76:bc:35:e7:16:80:6f:32:77:fd:57:ae:51:87:fb:be:c2:a1:cc:76:9a:61:01:c9:ff:86:00:ff:d1:96:cd:ff:2c:0f:48:9e:ae:83:d8:df:d4:78:1d:4c:37:87:f5:58:5d:26:c6:ca:16:cd:fa:16:1d:6f:42:ae:57:4a:99:45:52:80:5c:1c:76:42:a8:f8:f3:15:9c:1b:3e:36:01:e0:09:5e:d8:19:b1:ed:a0:ef:3b:c7:09:a7:aa:5f:b6:2d:c1:20:84:9b:2c:87:1a:2b:35:de:9e:9c:0c:d9:0c:5e:cf:51:38:d6:d6:80:ae:91:15:b5:c6:22:df:7e:17:9f:c3:eb:bf:fd:d5:3b:4b:ea:66:00:72:a0:b5:b7:65:a8:5a:d9:a8:f1:67:c1:41:d8:79:dd:cc:2f:78:7a:9e:5e:0a:9d:77:0e:59:52:49:d2:10:94:1c:eb:f4:3c:04:0e:3c:1c:1a:75:a6:e8:23:d5:f0:73:14:90:b1:71:5a:32:57:8d:34:d7:6a:61:dc:73:1a:da:1d:1f:56:a7:2e:ef:0d:a4:f5:fb:94:0b:f4:cf:1d:d2:10:0f:07:cd:ba:9d:78:87:e8:04:63:6a:e5:7a:6b:20:bd:bd:29:c2:39:5b:fc:86:84:77:0b:e3:f8:2c:37:ac:af:1b:ed:4f:b9:d6:08:a3:ac:2f:31:07:4a:f8:8e:cf:11:dd:92:1c:c9:aa:c7:a5:b7:62:a4:77:6e:58:20:78:17:cb:5e:ef:6d:41:eb:b6:c2:1f:7f:a1:de:fa:bb:71:92:20:de:b1:5e:34:84:6c:ed:6c:e1:43:86:13:f0:3f:d7:2d:c5:ba:c0:de:37:8d:48:bc:df:c7:4f:b3:a6:a5:e5:c2:db:f1:ef:db:0c:25:69:e6:58:8d:ba:72:bd:5e:3f:cf:81:36:b6:ab:ee:a8:67:8f:ee:bb:fe:6f:c9:1f:8a:1f:ef:e9:c9:7a:52:40:ad:a0:3f:23:45:7a:63:95:98:3d:12:b8:e2:f3:0b:88:10:38:04:68:b0:f1:a7:8b:d0:61:d7:0f:2f:cf:17:51:21:eb:76:69:2d:19:e8:01:c5:33:fd:61:cd:46:64:87:89:43:e9:31:d0:be:88:a0:a2:82:0c:7f:9f:66:41:3a:9a:5a:6a"},{"tbs_certificate":{"version":"v3","serial_number":"03","signature":{"algorithm":"sha1_rsa","parameters":null},"issuer":{"country_name":"FR","state_or_province_name":"Alsace","locality_name":"Strasbourg","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"Freelan Sample Certificate Authority","email_address":"contact@freelan.org"},"validity":{"not_before":1335524093,"not_after":1650884093,"not_before_iso":"2012-04-27T10:54:53+00:00","not_after_iso":"2022-04-25T10:54:53+00:00"},"subject":{"country_name":"FR","state_or_province_name":"Alsace","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"carol","email_address":"contact@freelan.org"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"d7:c0:a7:c6:e9:48:c4:53:40:b3:76:d9:2f:37:28:3d:a3:c4:42:d0:76:cd:08:9b:50:e3:1c:51:e5:14:72:fa:2b:a0:b1:06:23:f3:c1:ad:92:7c:79:fe:15:54:d1:e5:67:62:da:ed:81:aa:7e:e2:b1:50:a9:fb:d8:29:09:da:84:4d:3c:f4:6e:13:ab:0b:d5:ee:80:63:32:7d:57:af:83:3c:1c:27:ed:ec:67:d6:fd:1c:13:2d:40:bf:d1:da:bf:7a:b6:67:7e:b0:75:3b:6d:61:9d:cc:6c:1a:97:f1:56:de:9f:80:d3:16:60:bb:8a:6f:46:9b:be:34:75:c3:4c:d2:f1:c8:f3:3e:98:28:30:e4:cb:2d:25:61:62:48:be:2e:dc:ed:90:93:ae:74:b7:fa:49:43:65:20:ac:8e:fe:52:6c:00:8e:51:3e:b6:9a:c6:4f:44:1c:7b:84:17:bd:5c:f6:36:e9:4c:91:89:6f:4e:ad:ac:10:41:c5:c5:65:8a:20:c8:f7:27:a3:ea:ac:5b:74:09:99:27:88:60:c7:44:69:18:0c:32:1a:77:f2:47:53:46:e3:12:c5:69:95:45:15:9a:14:60:76:20:a7:b5:8c:51:bf:5a:57:19:5a:c7:a8:bc:0b:c4:30:ca:0b:e6:d0:f8:c4:a8:84:d9:24:a2:92:f6:84:f2:13:ea:a4:93:97:fe:ed:77:d8:2f:75:7a:2c:39:88:3c:44:56:0a:ef:12:57:d5:9e:8f:35:8e:7f:84:e7:1a:d1:19:8d:23:db:b5:ce:c5:7f:e1:88:6d:04:d6:01:de:f0:72:3e:51:95:1d:4f:30:b6:32:0a:0f:84:b5:00:34:e4:bf:80:71:10:62:14:c1:32:5a:a9:a6:de:c2:58:e8:52:eb:66:5a:b8:5e:c2:06:7c:a6:6a:33:f2:1e:8a:41:07:53:bb:6b:41:92:59:85:79:04:a9:df:56:4c:e0:62:1e:98:87:95:07:b1:10:49:34:9c:90:4c:0b:83:25:27:9f:01:27:fb:d0:c4:6e:50:cc:f5:02:47:2c:45:9a:31:e5:ce:7d:86:8f:db:fd:83:ea:a6:00:49:71:14:44:a1:8e:9d:ba:a4:a4:cf:9d:15:20:2d:67:76:42:81:63:a2:76:4e:4b:22:b5:de:3d:d8:f8:e0:43:7f:a3:10:f0:73:fb:6e:e1:6a:37:99:dc:87:a3:05:4c:29:f5:63:14:9b:eb:a3:3a:9b:2b:b4:51:f5:05:03:de:41:e5:cb:1a:8e:76:eb:47:93:53:90:71:c5:8f:86:5f:9e:0b:4d:33:9c:3c:88:8a:90:9f:90:a6:35:90:81:f1","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"2.16.840.1.113730.1.13","critical":false,"extn_value":"16:1d:4f:70:65:6e:53:53:4c:20:47:65:6e:65:72:61:74:65:64:20:43:65:72:74:69:66:69:63:61:74:65"},{"extn_id":"key_identifier","critical":false,"extn_value":"b5:5d:0d:4f:55:f6:75:1a:23:b3:f5:8c:bc:6b:5a:b6:96:6c:ae:e0"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53","authority_cert_issuer":null,"authority_cert_serial_number":null}}]},"signature_algorithm":{"algorithm":"sha1_rsa","parameters":null},"signature_value":"bf:3f:e7:16:a2:ba:b1:cf:d6:79:f3:84:ed:a5:10:3e:60:42:0e:d5:1a:c6:e9:b1:39:86:5a:2e:dd:ae:b6:b7:16:33:33:17:3e:83:f7:a1:f7:b4:1b:09:74:8f:9b:0d:8e:4c:c7:a1:d6:66:6c:02:3a:b5:f2:72:aa:c9:e4:b3:c6:9d:6e:c0:48:dc:39:21:30:18:a0:6f:cb:09:be:de:0f:63:83:04:32:73:a7:bc:42:34:b7:a1:dc:21:21:08:86:65:bc:2e:c5:78:ae:fb:fe:ab:fb:8b:85:bf:61:e0:e2:aa:52:5f:1e:0d:19:22:13:94:7a:b4:bd:5c:30:8d:43:22:b4:e9:13:62:7e:3e:f5:e2:7a:2a:3b:da:1f:57:4a:5d:b8:6c:4c:f5:6e:34:b9:bd:b4:1f:dc:88:d0:28:20:a2:0c:31:e8:7f:3a:23:b8:60:48:c8:4e:e1:02:62:ae:00:fb:d0:a5:76:cb:ea:f3:d7:75:0d:9e:56:48:c1:2e:44:c7:0c:9f:03:b3:ac:96:c5:a2:a0:06:9e:2b:c3:eb:b5:04:15:33:79:4a:9e:28:94:1d:28:50:98:e3:eb:b5:74:69:7f:69:bc:61:72:d1:8a:cc:fb:89:be:51:34:81:11:7b:fa:8a:cf:e7:bf:81:91:34:1a:11:63:92:41:eb:62:7d:7a:2a:5a:2b:a3:85:36:5b:39:08:40:6b:0d:bc:b7:ed:36:42:60:45:ee:0c:27:f1:41:38:9e:db:99:8f:0f:ff:1b:ea:02:98:9f:19:21:33:ca:a2:47:89:cb:1d:a9:4c:94:b6:3d:b2:e2:bf:1d:f7:12:8d:01:ff:77:d6:72:65:70:ca:80:8e:a2:2d:78:0c:b2:9d:84:3a:50:f9:e8:8e:85:03:58:eb:0a:d3:5b:d3:55:d0:bd:7d:de:c8:5b:80:ea:0e:53:d6:35:86:60:10:ed:bd:06:f4:59:15:64:75:4c:bd:2f:fb:8a:fa:c1:d0:c2:d9:68:09:2b:9a:91:c4:00:b1:65:7d:6d:a8:c2:42:d1:d7:f1:71:ae:db:96:33:e7:a9:29:27:f3:89:8d:c8:ac:87:14:fa:a5:cf:ec:b6:1b:a6:03:93:d7:ef:7f:49:b0:d5:22:fe:9e:5a:1b:e1:ff:e9:e3:71:fa:e9:09:3f:b4:1a:33:ae:3a:60:27:d2:e6:2f:12:f4:32:54:be:29:be:fc:14:a5:2a:2d:99:88:e0:9d:d0:c6:07:e1:76:fb:96:60:0e:4c:d9:93:bd:26:29:2a:8f:49:d9:f6:7d:7a:bc:34:31:84:81:4f:28:e1:e8:5e:cf:45:b1:c1:8a:2b:e0:52:72:5f:19"}] +[{"tbs_certificate":{"version":"v3","serial_number":"01","signature":{"algorithm":"sha1_rsa","parameters":null},"issuer":{"country_name":"FR","state_or_province_name":"Alsace","locality_name":"Strasbourg","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"Freelan Sample Certificate Authority","email_address":"contact@freelan.org"},"validity":{"not_before":1335522678,"not_after":1650882678,"not_before_iso":"2012-04-27T10:31:18+00:00","not_after_iso":"2022-04-25T10:31:18+00:00"},"subject":{"country_name":"FR","state_or_province_name":"Alsace","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"alice","email_address":"contact@freelan.org"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"dd:6d:bd:f8:80:fa:d7:de:1b:1f:a7:a3:2e:b2:02:e2:16:f6:52:0a:3c:bf:a6:42:f8:ca:dc:93:67:4d:60:c3:4f:8d:c3:8a:00:1b:f1:c4:4b:41:6a:69:d2:69:e5:3f:21:8e:c5:0b:f8:22:37:ad:b6:2c:4b:55:ff:7a:03:72:bb:9a:d3:ec:96:b9:56:9f:cb:19:99:c9:32:94:6f:8f:c6:52:06:9f:45:03:df:fd:e8:97:f6:ea:d6:ba:bb:48:2b:b5:e0:34:61:4d:52:36:0f:ab:87:52:25:03:cf:87:00:87:13:f2:ca:03:29:16:9d:90:57:46:b5:f4:0e:ae:17:c8:0a:4d:92:ed:08:a6:32:23:11:71:fe:f2:2c:44:d7:6c:07:f3:0b:7b:0c:4b:dd:3b:b4:f7:37:70:9f:51:b6:88:4e:5d:6a:05:7f:8d:9b:66:7a:ab:80:20:fe:ee:6b:97:c3:49:7d:78:3b:d5:99:97:03:75:ce:8f:bc:c5:be:9c:9a:a5:12:19:70:f9:a4:bd:96:27:ed:23:02:a7:c7:57:c9:71:cf:76:94:a2:21:62:f6:b8:1d:ca:88:ee:09:ad:46:2f:b7:61:b3:2c:15:13:86:9f:a5:35:26:5a:67:f4:37:c8:e6:80:01:49:0e:c7:ed:61:d3:cd:bc:e4:f8:be:3f:c9:4e:f8:7d:97:89:ce:12:bc:ca:b5:c6:d2:e0:d9:b3:68:3c:2e:4a:9d:b4:5f:b8:53:ee:50:3d:bf:dd:d4:a2:8a:b6:a0:27:ab:98:0c:b3:b2:58:90:e2:bc:a1:ad:ff:bd:8e:55:31:0f:00:bf:68:e9:3d:a9:19:9a:f0:6d:0b:a2:14:6a:c6:4c:c6:4e:bd:63:12:a5:0b:4d:97:eb:42:09:79:53:e2:65:aa:24:34:70:b8:c1:ab:23:80:e7:9c:6c:ed:dc:82:aa:37:04:b8:43:2a:3d:2a:a8:cc:20:fc:27:5d:90:26:58:f9:b7:14:e2:9e:e2:c1:70:73:97:e9:6b:02:8e:d3:52:59:7b:00:ec:61:30:f1:56:3f:9c:c1:7c:05:c5:b1:36:c8:18:85:cf:61:40:1f:07:e8:a7:06:87:df:9a:77:0b:a9:64:72:03:f6:93:fc:e0:02:59:c1:96:ec:c0:09:42:3e:30:a2:7f:1b:48:2f:fe:e0:21:8f:53:87:25:0d:cb:ea:49:f5:4a:9b:d0:e3:5f:ee:78:18:e5:ba:71:31:a9:04:98:0f:b1:ad:67:52:a0:f2:e3:9c:ab:6a:fe:58:84:84:dd:07:3d:32:94:05:16:45:15:96:59:a0:58:6c:18:0e:e3:77:66:c7:b3:f7:99","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"2.16.840.1.113730.1.13","critical":false,"extn_value":"16:1d:4f:70:65:6e:53:53:4c:20:47:65:6e:65:72:61:74:65:64:20:43:65:72:74:69:66:69:63:61:74:65"},{"extn_id":"key_identifier","critical":false,"extn_value":"59:5f:c9:13:ba:1b:cc:b9:a8:41:4a:8a:49:79:6a:36:f6:7d:3e:d7"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53","authority_cert_issuer":null,"authority_cert_serial_number":null}}],"serial_number_str":"1"},"signature_algorithm":{"algorithm":"sha1_rsa","parameters":null},"signature_value":"13:e7:02:45:3e:a7:ab:bd:b8:da:e7:ef:74:88:ac:62:d5:dd:10:56:d5:46:07:ec:fa:6a:80:0c:b9:62:be:aa:08:b4:be:0b:eb:9a:ef:68:b7:69:6f:4d:20:92:9d:18:63:7a:23:f4:48:87:6a:14:c3:91:98:1b:4e:08:59:3f:91:80:e9:f4:cf:fd:d5:bf:af:4b:e4:bd:78:09:71:ac:d0:81:e5:53:9f:3e:ac:44:3e:9f:f0:bf:5a:c1:70:4e:06:04:ef:dc:e8:77:05:a2:7d:c5:fa:80:58:0a:c5:10:6d:90:ca:49:26:71:84:39:b7:9a:3e:e9:6f:ae:c5:35:b6:5b:24:8c:c9:ef:41:c3:b1:17:b6:3b:4e:28:89:3c:7e:87:a8:3a:a5:6d:dc:39:03:20:20:0b:c5:80:a3:79:13:1e:f6:ec:ae:36:df:40:74:34:87:46:93:3b:a3:e0:a4:8c:2f:43:4c:b2:54:80:71:76:78:d4:ea:12:28:d8:f2:e3:80:55:11:9b:f4:65:dc:53:0e:b4:4c:e0:4c:09:b4:dc:a0:80:5c:e6:b5:3b:95:d3:69:e4:52:3d:5b:61:86:02:e5:fd:0b:00:3a:fa:b3:45:cc:c9:a3:64:f2:dc:25:59:89:58:0d:9e:6e:28:3a:55:45:50:5f:88:67:2a:d2:e2:48:cc:8b:de:9a:1b:93:ae:87:e1:f2:90:50:40:d9:0f:44:31:53:46:ad:62:4e:8d:48:86:19:77:fc:59:75:91:79:35:59:1d:e3:4e:33:5b:e2:31:d7:ee:52:28:5f:0a:70:a7:be:bb:1c:03:ca:1a:18:d0:f5:c1:5b:9c:73:04:b6:4a:e8:46:52:58:76:d4:6a:e6:67:1c:0e:dc:13:d0:61:72:a0:92:cb:05:97:47:1c:c1:c9:cf:41:7d:1f:b1:4d:93:6b:53:41:03:21:2b:93:15:63:08:3e:2c:86:9e:7b:9f:3a:09:05:6a:7d:bb:1c:a7:b7:af:96:08:cb:5b:df:07:fb:9c:f2:95:11:c0:82:81:f6:1b:bf:5a:1e:58:cd:28:ca:7d:04:eb:aa:e9:29:c4:82:51:2c:89:61:95:b6:ed:a5:86:7c:7c:48:1d:ec:54:96:47:79:ea:fc:7f:f5:10:43:0a:9b:00:ef:8a:77:2e:f4:36:66:d2:6a:a6:95:b6:9f:23:3b:12:e2:89:d5:a4:c1:2c:91:4e:cb:94:e8:3f:22:0e:21:f9:b8:4a:81:5c:4c:63:ae:3d:05:b2:5c:5c:54:a7:55:8f:98:25:55:c4:a6:90:bc:19:29:b1:14:d4:e2:b0:95:e4:ff:89:71:61:be:8a:16:85"},{"tbs_certificate":{"version":"v3","serial_number":"02","signature":{"algorithm":"sha1_rsa","parameters":null},"issuer":{"country_name":"FR","state_or_province_name":"Alsace","locality_name":"Strasbourg","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"Freelan Sample Certificate Authority","email_address":"contact@freelan.org"},"validity":{"not_before":1335524080,"not_after":1650884080,"not_before_iso":"2012-04-27T10:54:40+00:00","not_after_iso":"2022-04-25T10:54:40+00:00"},"subject":{"country_name":"FR","state_or_province_name":"Alsace","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"bob","email_address":"contact@freelan.org"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"c2:3f:43:14:4a:d4:dd:43:5a:b9:43:5e:2d:bb:89:a1:17:18:f7:ae:47:4b:7a:f4:d4:dc:a3:e1:b7:85:3a:10:20:eb:bc:51:18:d8:8b:25:c6:04:95:4f:80:e9:05:5c:00:f4:7c:23:7b:d1:ad:81:58:f1:9d:43:c3:37:ee:7f:61:03:b5:ff:29:bb:10:1a:fb:a8:77:97:9b:de:4c:7d:3f:ca:ff:53:8c:37:30:b6:88:f2:0e:be:7c:dc:92:76:c9:5f:22:96:19:0b:91:ea:9c:18:96:9f:43:d1:9d:22:9e:d9:c3:12:9f:80:05:85:1f:70:bb:87:5d:63:c1:5a:51:3d:7e:69:3d:76:6d:b0:56:ea:db:3f:ae:f0:cd:0c:19:48:b1:f2:d5:2e:e7:fa:12:dd:15:bc:8c:dc:09:c2:26:9c:dc:22:52:8e:c8:1c:c1:cd:01:bd:1a:24:c5:be:4f:18:08:f3:de:59:1c:8f:63:a6:63:1d:4f:5a:92:68:7a:49:94:26:54:d1:83:be:16:e4:5e:8f:73:2f:81:3a:3a:30:80:fd:57:a9:7f:1b:7b:e5:0f:6c:01:68:f7:1f:45:49:fe:06:3c:08:57:64:27:a5:0b:55:18:b7:30:be:08:45:70:8b:cd:43:ea:fc:80:1e:03:5c:c3:52:8d:a9:55:53:55:f4:61:2e:8b:50:64:6a:30:a7:6f:bd:b8:80:12:ee:66:98:d8:78:5f:a0:f5:65:6a:6d:f5:09:cc:62:4d:55:56:80:21:75:48:73:4d:b9:e3:f9:1d:96:c9:2c:5d:79:4d:3c:c5:7a:9e:84:ff:9d:c7:94:87:0a:3e:69:81:d2:7f:c0:5f:67:9c:06:8c:33:5c:a3:9f:52:e7:04:c7:d3:81:ef:b2:77:1e:d0:57:1f:1f:90:a5:69:c0:0d:43:c5:f6:a6:7e:f7:ea:45:7c:60:b6:68:1f:64:59:dc:60:33:c2:13:8c:b7:06:c2:2a:cd:cc:2b:02:de:a2:e9:70:0c:db:79:fe:ce:eb:5e:c0:06:eb:76:43:09:e0:2a:c7:ee:1e:6a:af:60:49:73:3c:a8:53:8c:e1:39:2c:e7:9e:fe:fd:44:20:f0:85:9a:1f:eb:c7:40:c8:5b:90:43:e6:a1:6a:00:50:4b:73:73:72:c5:39:77:13:1e:3c:95:be:a9:37:6a:d1:4e:34:3d:34:ec:87:f8:1e:6c:e7:dc:8b:7f:8e:d1:3c:78:c2:e2:09:93:d7:c0:68:ae:70:81:b9:f0:d0:f7:26:a4:e2:c0:12:1d:2f:01:63:eb:53:05:cb:aa:db:66:b0:fb:16:9b:e7:e7:be:c3:66:da:5c:c9","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"2.16.840.1.113730.1.13","critical":false,"extn_value":"16:1d:4f:70:65:6e:53:53:4c:20:47:65:6e:65:72:61:74:65:64:20:43:65:72:74:69:66:69:63:61:74:65"},{"extn_id":"key_identifier","critical":false,"extn_value":"9c:d2:71:50:35:f7:10:43:dd:e8:ce:75:29:a3:53:5d:11:a7:a8:3b"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53","authority_cert_issuer":null,"authority_cert_serial_number":null}}],"serial_number_str":"2"},"signature_algorithm":{"algorithm":"sha1_rsa","parameters":null},"signature_value":"c3:b0:a4:82:f5:64:e5:4e:a0:e5:74:5e:c4:3d:d0:9c:f7:4e:f7:8d:af:8b:2e:80:59:63:b5:6e:2f:10:5b:66:d6:29:2a:ca:e2:01:20:68:e1:2b:ff:d6:e1:e1:f2:a6:e0:cc:f5:8f:9f:5c:72:b8:fa:81:76:7d:5c:ee:60:29:e5:d7:de:8f:4a:9c:55:3e:e5:27:1c:76:bc:35:e7:16:80:6f:32:77:fd:57:ae:51:87:fb:be:c2:a1:cc:76:9a:61:01:c9:ff:86:00:ff:d1:96:cd:ff:2c:0f:48:9e:ae:83:d8:df:d4:78:1d:4c:37:87:f5:58:5d:26:c6:ca:16:cd:fa:16:1d:6f:42:ae:57:4a:99:45:52:80:5c:1c:76:42:a8:f8:f3:15:9c:1b:3e:36:01:e0:09:5e:d8:19:b1:ed:a0:ef:3b:c7:09:a7:aa:5f:b6:2d:c1:20:84:9b:2c:87:1a:2b:35:de:9e:9c:0c:d9:0c:5e:cf:51:38:d6:d6:80:ae:91:15:b5:c6:22:df:7e:17:9f:c3:eb:bf:fd:d5:3b:4b:ea:66:00:72:a0:b5:b7:65:a8:5a:d9:a8:f1:67:c1:41:d8:79:dd:cc:2f:78:7a:9e:5e:0a:9d:77:0e:59:52:49:d2:10:94:1c:eb:f4:3c:04:0e:3c:1c:1a:75:a6:e8:23:d5:f0:73:14:90:b1:71:5a:32:57:8d:34:d7:6a:61:dc:73:1a:da:1d:1f:56:a7:2e:ef:0d:a4:f5:fb:94:0b:f4:cf:1d:d2:10:0f:07:cd:ba:9d:78:87:e8:04:63:6a:e5:7a:6b:20:bd:bd:29:c2:39:5b:fc:86:84:77:0b:e3:f8:2c:37:ac:af:1b:ed:4f:b9:d6:08:a3:ac:2f:31:07:4a:f8:8e:cf:11:dd:92:1c:c9:aa:c7:a5:b7:62:a4:77:6e:58:20:78:17:cb:5e:ef:6d:41:eb:b6:c2:1f:7f:a1:de:fa:bb:71:92:20:de:b1:5e:34:84:6c:ed:6c:e1:43:86:13:f0:3f:d7:2d:c5:ba:c0:de:37:8d:48:bc:df:c7:4f:b3:a6:a5:e5:c2:db:f1:ef:db:0c:25:69:e6:58:8d:ba:72:bd:5e:3f:cf:81:36:b6:ab:ee:a8:67:8f:ee:bb:fe:6f:c9:1f:8a:1f:ef:e9:c9:7a:52:40:ad:a0:3f:23:45:7a:63:95:98:3d:12:b8:e2:f3:0b:88:10:38:04:68:b0:f1:a7:8b:d0:61:d7:0f:2f:cf:17:51:21:eb:76:69:2d:19:e8:01:c5:33:fd:61:cd:46:64:87:89:43:e9:31:d0:be:88:a0:a2:82:0c:7f:9f:66:41:3a:9a:5a:6a"},{"tbs_certificate":{"version":"v3","serial_number":"03","signature":{"algorithm":"sha1_rsa","parameters":null},"issuer":{"country_name":"FR","state_or_province_name":"Alsace","locality_name":"Strasbourg","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"Freelan Sample Certificate Authority","email_address":"contact@freelan.org"},"validity":{"not_before":1335524093,"not_after":1650884093,"not_before_iso":"2012-04-27T10:54:53+00:00","not_after_iso":"2022-04-25T10:54:53+00:00"},"subject":{"country_name":"FR","state_or_province_name":"Alsace","organization_name":"www.freelan.org","organizational_unit_name":"freelan","common_name":"carol","email_address":"contact@freelan.org"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"d7:c0:a7:c6:e9:48:c4:53:40:b3:76:d9:2f:37:28:3d:a3:c4:42:d0:76:cd:08:9b:50:e3:1c:51:e5:14:72:fa:2b:a0:b1:06:23:f3:c1:ad:92:7c:79:fe:15:54:d1:e5:67:62:da:ed:81:aa:7e:e2:b1:50:a9:fb:d8:29:09:da:84:4d:3c:f4:6e:13:ab:0b:d5:ee:80:63:32:7d:57:af:83:3c:1c:27:ed:ec:67:d6:fd:1c:13:2d:40:bf:d1:da:bf:7a:b6:67:7e:b0:75:3b:6d:61:9d:cc:6c:1a:97:f1:56:de:9f:80:d3:16:60:bb:8a:6f:46:9b:be:34:75:c3:4c:d2:f1:c8:f3:3e:98:28:30:e4:cb:2d:25:61:62:48:be:2e:dc:ed:90:93:ae:74:b7:fa:49:43:65:20:ac:8e:fe:52:6c:00:8e:51:3e:b6:9a:c6:4f:44:1c:7b:84:17:bd:5c:f6:36:e9:4c:91:89:6f:4e:ad:ac:10:41:c5:c5:65:8a:20:c8:f7:27:a3:ea:ac:5b:74:09:99:27:88:60:c7:44:69:18:0c:32:1a:77:f2:47:53:46:e3:12:c5:69:95:45:15:9a:14:60:76:20:a7:b5:8c:51:bf:5a:57:19:5a:c7:a8:bc:0b:c4:30:ca:0b:e6:d0:f8:c4:a8:84:d9:24:a2:92:f6:84:f2:13:ea:a4:93:97:fe:ed:77:d8:2f:75:7a:2c:39:88:3c:44:56:0a:ef:12:57:d5:9e:8f:35:8e:7f:84:e7:1a:d1:19:8d:23:db:b5:ce:c5:7f:e1:88:6d:04:d6:01:de:f0:72:3e:51:95:1d:4f:30:b6:32:0a:0f:84:b5:00:34:e4:bf:80:71:10:62:14:c1:32:5a:a9:a6:de:c2:58:e8:52:eb:66:5a:b8:5e:c2:06:7c:a6:6a:33:f2:1e:8a:41:07:53:bb:6b:41:92:59:85:79:04:a9:df:56:4c:e0:62:1e:98:87:95:07:b1:10:49:34:9c:90:4c:0b:83:25:27:9f:01:27:fb:d0:c4:6e:50:cc:f5:02:47:2c:45:9a:31:e5:ce:7d:86:8f:db:fd:83:ea:a6:00:49:71:14:44:a1:8e:9d:ba:a4:a4:cf:9d:15:20:2d:67:76:42:81:63:a2:76:4e:4b:22:b5:de:3d:d8:f8:e0:43:7f:a3:10:f0:73:fb:6e:e1:6a:37:99:dc:87:a3:05:4c:29:f5:63:14:9b:eb:a3:3a:9b:2b:b4:51:f5:05:03:de:41:e5:cb:1a:8e:76:eb:47:93:53:90:71:c5:8f:86:5f:9e:0b:4d:33:9c:3c:88:8a:90:9f:90:a6:35:90:81:f1","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"basic_constraints","critical":false,"extn_value":{"ca":false,"path_len_constraint":null}},{"extn_id":"2.16.840.1.113730.1.13","critical":false,"extn_value":"16:1d:4f:70:65:6e:53:53:4c:20:47:65:6e:65:72:61:74:65:64:20:43:65:72:74:69:66:69:63:61:74:65"},{"extn_id":"key_identifier","critical":false,"extn_value":"b5:5d:0d:4f:55:f6:75:1a:23:b3:f5:8c:bc:6b:5a:b6:96:6c:ae:e0"},{"extn_id":"authority_key_identifier","critical":false,"extn_value":{"key_identifier":"23:6c:2d:3d:3e:29:5d:78:b8:6c:3e:aa:e2:bb:2e:1e:6c:87:f2:53","authority_cert_issuer":null,"authority_cert_serial_number":null}}],"serial_number_str":"3"},"signature_algorithm":{"algorithm":"sha1_rsa","parameters":null},"signature_value":"bf:3f:e7:16:a2:ba:b1:cf:d6:79:f3:84:ed:a5:10:3e:60:42:0e:d5:1a:c6:e9:b1:39:86:5a:2e:dd:ae:b6:b7:16:33:33:17:3e:83:f7:a1:f7:b4:1b:09:74:8f:9b:0d:8e:4c:c7:a1:d6:66:6c:02:3a:b5:f2:72:aa:c9:e4:b3:c6:9d:6e:c0:48:dc:39:21:30:18:a0:6f:cb:09:be:de:0f:63:83:04:32:73:a7:bc:42:34:b7:a1:dc:21:21:08:86:65:bc:2e:c5:78:ae:fb:fe:ab:fb:8b:85:bf:61:e0:e2:aa:52:5f:1e:0d:19:22:13:94:7a:b4:bd:5c:30:8d:43:22:b4:e9:13:62:7e:3e:f5:e2:7a:2a:3b:da:1f:57:4a:5d:b8:6c:4c:f5:6e:34:b9:bd:b4:1f:dc:88:d0:28:20:a2:0c:31:e8:7f:3a:23:b8:60:48:c8:4e:e1:02:62:ae:00:fb:d0:a5:76:cb:ea:f3:d7:75:0d:9e:56:48:c1:2e:44:c7:0c:9f:03:b3:ac:96:c5:a2:a0:06:9e:2b:c3:eb:b5:04:15:33:79:4a:9e:28:94:1d:28:50:98:e3:eb:b5:74:69:7f:69:bc:61:72:d1:8a:cc:fb:89:be:51:34:81:11:7b:fa:8a:cf:e7:bf:81:91:34:1a:11:63:92:41:eb:62:7d:7a:2a:5a:2b:a3:85:36:5b:39:08:40:6b:0d:bc:b7:ed:36:42:60:45:ee:0c:27:f1:41:38:9e:db:99:8f:0f:ff:1b:ea:02:98:9f:19:21:33:ca:a2:47:89:cb:1d:a9:4c:94:b6:3d:b2:e2:bf:1d:f7:12:8d:01:ff:77:d6:72:65:70:ca:80:8e:a2:2d:78:0c:b2:9d:84:3a:50:f9:e8:8e:85:03:58:eb:0a:d3:5b:d3:55:d0:bd:7d:de:c8:5b:80:ea:0e:53:d6:35:86:60:10:ed:bd:06:f4:59:15:64:75:4c:bd:2f:fb:8a:fa:c1:d0:c2:d9:68:09:2b:9a:91:c4:00:b1:65:7d:6d:a8:c2:42:d1:d7:f1:71:ae:db:96:33:e7:a9:29:27:f3:89:8d:c8:ac:87:14:fa:a5:cf:ec:b6:1b:a6:03:93:d7:ef:7f:49:b0:d5:22:fe:9e:5a:1b:e1:ff:e9:e3:71:fa:e9:09:3f:b4:1a:33:ae:3a:60:27:d2:e6:2f:12:f4:32:54:be:29:be:fc:14:a5:2a:2d:99:88:e0:9d:d0:c6:07:e1:76:fb:96:60:0e:4c:d9:93:bd:26:29:2a:8f:49:d9:f6:7d:7a:bc:34:31:84:81:4f:28:e1:e8:5e:cf:45:b1:c1:8a:2b:e0:52:72:5f:19"}] diff --git a/tests/fixtures/generic/x509-string-serialnumber.der b/tests/fixtures/generic/x509-string-serialnumber.der new file mode 100644 index 0000000000000000000000000000000000000000..77bec2ee5518accf66983e95cf6d6a198d0923f8 GIT binary patch literal 524 zcmXqLV&X7pV%)TVnTe5!NhI^`*^k?Ha5ta%Jj-r&SM1-GKa zyyVQ{u5N(JZq+=9fsN|+!s4_9twab`)XUUGh}p@0D&$OKkl_Fz95H-mu(lOw~X0Ja-aRxz#@ z`qusW!g=tjj+3C2*Mh%KRbOg9Y|*XuoNi`(_3y>Ib~nG<%HONpceelH@zP|KyhmTB zK1oh(v(RT^W@KPoEMWi%OIbk{UIXqX&Yb+@#GH)$;u2(AfziV3$Y8ddN%)~oS&8+> zD{9d#KcrPZJ$&ExXZ@kJCN1tiX3xLsOw4V2H|z1vTdyu8O@AlOY+=hak Date: Thu, 29 Dec 2022 09:25:31 -0800 Subject: [PATCH 20/31] doc update --- CHANGELOG | 3 ++- docs/parsers/x509_cert.md | 2 +- jc/parsers/x509_cert.py | 2 +- man/jc.1 | 2 +- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 12971cdd..92f14387 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,8 +1,9 @@ jc changelog -20221227 v1.23.4 +20221229 v1.23.4 - Add `iwconfig` command parser - Fix `proc` parser magic signature detection for `/proc/pid/stat` hacks +- Fix `x509-cert` parser for string serial numbers - Add category tags to parser metadata - Add "list parsers by category" view to help - Fix python 3.6-related issues diff --git a/docs/parsers/x509_cert.md b/docs/parsers/x509_cert.md index 960da82b..0b593a79 100644 --- a/docs/parsers/x509_cert.md +++ b/docs/parsers/x509_cert.md @@ -433,4 +433,4 @@ Returns: ### Parser Information Compatibility: linux, darwin, cygwin, win32, aix, freebsd -Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/jc/parsers/x509_cert.py b/jc/parsers/x509_cert.py index 0ef3ea05..e89fccb5 100644 --- a/jc/parsers/x509_cert.py +++ b/jc/parsers/x509_cert.py @@ -413,7 +413,7 @@ from jc.parsers.asn1crypto import pem, x509 class info(): """Provides parser metadata (version, author, etc.)""" - version = '1.0' + version = '1.1' description = 'X.509 PEM and DER certificate file parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' diff --git a/man/jc.1 b/man/jc.1 index cea63963..f418aa46 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2022-12-28 1.22.4 "JSON Convert" +.TH jc 1 2022-12-29 1.22.4 "JSON Convert" .SH NAME \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings .SH SYNOPSIS From ba89092e123f3b56e2bd1d1a5183a73e701f4a71 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Thu, 29 Dec 2022 14:52:33 -0800 Subject: [PATCH 21/31] formatting --- jc/parsers/x509_cert.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/x509_cert.py b/jc/parsers/x509_cert.py index e89fccb5..e8932539 100644 --- a/jc/parsers/x509_cert.py +++ b/jc/parsers/x509_cert.py @@ -514,7 +514,7 @@ def _fix_objects(obj): continue if isinstance(v, list): - newlist =[] + newlist = [] for i in v: newlist.append(_fix_objects(i)) obj.update({k: newlist}) From b1358a7ecabcd91b88836a5e9a3324b74c2c74d3 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 10:27:03 -0800 Subject: [PATCH 22/31] add nextstep plist support. using the pbPlist library --- CHANGELOG | 5 +- docs/parsers/plist.md | 4 +- jc/parsers/pbPlist/StrParse.py | 366 +++++++++++++ jc/parsers/pbPlist/Switch.py | 50 ++ jc/parsers/pbPlist/__init__.py | 32 ++ jc/parsers/pbPlist/pbItem.py | 262 ++++++++++ jc/parsers/pbPlist/pbParser.py | 291 +++++++++++ jc/parsers/pbPlist/pbPlist.py | 55 ++ jc/parsers/pbPlist/pbRoot.py | 110 ++++ jc/parsers/pbPlist/pbSerializer.py | 75 +++ jc/parsers/plist.py | 21 +- man/jc.1 | 2 +- tests/fixtures/generic/plist-nextstep.json | 1 + tests/fixtures/generic/plist-nextstep.plist | 508 +++++++++++++++++++ tests/fixtures/generic/plist-nextstep2.json | 1 + tests/fixtures/generic/plist-nextstep2.plist | 9 + tests/test_plist.py | 25 + 17 files changed, 1809 insertions(+), 8 deletions(-) create mode 100644 jc/parsers/pbPlist/StrParse.py create mode 100644 jc/parsers/pbPlist/Switch.py create mode 100644 jc/parsers/pbPlist/__init__.py create mode 100644 jc/parsers/pbPlist/pbItem.py create mode 100644 jc/parsers/pbPlist/pbParser.py create mode 100644 jc/parsers/pbPlist/pbPlist.py create mode 100644 jc/parsers/pbPlist/pbRoot.py create mode 100644 jc/parsers/pbPlist/pbSerializer.py create mode 100644 tests/fixtures/generic/plist-nextstep.json create mode 100644 tests/fixtures/generic/plist-nextstep.plist create mode 100644 tests/fixtures/generic/plist-nextstep2.json create mode 100644 tests/fixtures/generic/plist-nextstep2.plist diff --git a/CHANGELOG b/CHANGELOG index 92f14387..c8572ba6 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,10 +1,11 @@ jc changelog -20221229 v1.23.4 +20221229 v1.22.4 - Add `iwconfig` command parser +- Add NeXTSTEP format support to the PLIST file parser - Fix `proc` parser magic signature detection for `/proc/pid/stat` hacks - Fix `x509-cert` parser for string serial numbers -- Add category tags to parser metadata +- Add category tags to parser metadata: generic, standard, file, string, binary, command - Add "list parsers by category" view to help - Fix python 3.6-related issues - Add python 3.6 to automated tests diff --git a/docs/parsers/plist.md b/docs/parsers/plist.md index 14b34c78..d8515e27 100644 --- a/docs/parsers/plist.md +++ b/docs/parsers/plist.md @@ -5,7 +5,7 @@ jc - JSON Convert PLIST file parser -Converts binary and XML PLIST files. +Converts binary, XML, and NeXTSTEP PLIST files. Binary values are converted into an ASCII hex representation. @@ -74,4 +74,4 @@ Returns: ### Parser Information Compatibility: linux, darwin, cygwin, win32, aix, freebsd -Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 1.1 by Kelly Brazil (kellyjonbrazil@gmail.com) diff --git a/jc/parsers/pbPlist/StrParse.py b/jc/parsers/pbPlist/StrParse.py new file mode 100644 index 00000000..5c981bfb --- /dev/null +++ b/jc/parsers/pbPlist/StrParse.py @@ -0,0 +1,366 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pbPlist +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +import sys +import string + +if sys.version_info >= (3, 0): + def unichr(character): # pylint: disable=redefined-builtin + return chr(character) + +def ConvertNEXTSTEPToUnicode(hex_digits): + # taken from http://ftp.unicode.org/Public/MAPPINGS/VENDORS/NEXT/NEXTSTEP.TXT + conversion = { + "80": "a0", # NO-BREAK SPACE + "81": "c0", # LATIN CAPITAL LETTER A WITH GRAVE + "82": "c1", # LATIN CAPITAL LETTER A WITH ACUTE + "83": "c2", # LATIN CAPITAL LETTER A WITH CIRCUMFLEX + "84": "c3", # LATIN CAPITAL LETTER A WITH TILDE + "85": "c4", # LATIN CAPITAL LETTER A WITH DIAERESIS + "86": "c5", # LATIN CAPITAL LETTER A WITH RING + "87": "c7", # LATIN CAPITAL LETTER C WITH CEDILLA + "88": "c8", # LATIN CAPITAL LETTER E WITH GRAVE + "89": "c9", # LATIN CAPITAL LETTER E WITH ACUTE + "8a": "ca", # LATIN CAPITAL LETTER E WITH CIRCUMFLEX + "8b": "cb", # LATIN CAPITAL LETTER E WITH DIAERESIS + "8c": "cc", # LATIN CAPITAL LETTER I WITH GRAVE + "8d": "cd", # LATIN CAPITAL LETTER I WITH ACUTE + "8e": "ce", # LATIN CAPITAL LETTER I WITH CIRCUMFLEX + "8f": "cf", # LATIN CAPITAL LETTER I WITH DIAERESIS + "90": "d0", # LATIN CAPITAL LETTER ETH + "91": "d1", # LATIN CAPITAL LETTER N WITH TILDE + "92": "d2", # LATIN CAPITAL LETTER O WITH GRAVE + "93": "d3", # LATIN CAPITAL LETTER O WITH ACUTE + "94": "d4", # LATIN CAPITAL LETTER O WITH CIRCUMFLEX + "95": "d5", # LATIN CAPITAL LETTER O WITH TILDE + "96": "d6", # LATIN CAPITAL LETTER O WITH DIAERESIS + "97": "d9", # LATIN CAPITAL LETTER U WITH GRAVE + "98": "da", # LATIN CAPITAL LETTER U WITH ACUTE + "99": "db", # LATIN CAPITAL LETTER U WITH CIRCUMFLEX + "9a": "dc", # LATIN CAPITAL LETTER U WITH DIAERESIS + "9b": "dd", # LATIN CAPITAL LETTER Y WITH ACUTE + "9c": "de", # LATIN CAPITAL LETTER THORN + "9d": "b5", # MICRO SIGN + "9e": "d7", # MULTIPLICATION SIGN + "9f": "f7", # DIVISION SIGN + "a0": "a9", # COPYRIGHT SIGN + "a1": "a1", # INVERTED EXCLAMATION MARK + "a2": "a2", # CENT SIGN + "a3": "a3", # POUND SIGN + "a4": "44", # FRACTION SLASH + "a5": "a5", # YEN SIGN + "a6": "92", # LATIN SMALL LETTER F WITH HOOK + "a7": "a7", # SECTION SIGN + "a8": "a4", # CURRENCY SIGN + "a9": "19", # RIGHT SINGLE QUOTATION MARK + "aa": "1c", # LEFT DOUBLE QUOTATION MARK + "ab": "ab", # LEFT-POINTING DOUBLE ANGLE QUOTATION MARK + "ac": "39", # SINGLE LEFT-POINTING ANGLE QUOTATION MARK + "ad": "3a", # SINGLE RIGHT-POINTING ANGLE QUOTATION MARK + "ae": "01", # LATIN SMALL LIGATURE FI + "af": "02", # LATIN SMALL LIGATURE FL + "b0": "ae", # REGISTERED SIGN + "b1": "13", # EN DASH + "b2": "20", # DAGGER + "b3": "21", # DOUBLE DAGGER + "b4": "b7", # MIDDLE DOT + "b5": "a6", # BROKEN BAR + "b6": "b6", # PILCROW SIGN + "b7": "22", # BULLET + "b8": "1a", # SINGLE LOW-9 QUOTATION MARK + "b9": "1e", # DOUBLE LOW-9 QUOTATION MARK + "ba": "1d", # RIGHT DOUBLE QUOTATION MARK + "bb": "bb", # RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK + "bc": "26", # HORIZONTAL ELLIPSIS + "bd": "30", # PER MILLE SIGN + "be": "ac", # NOT SIGN + "bf": "bf", # INVERTED QUESTION MARK + "c0": "b9", # SUPERSCRIPT ONE + "c1": "cb", # MODIFIER LETTER GRAVE ACCENT + "c2": "b4", # ACUTE ACCENT + "c3": "c6", # MODIFIER LETTER CIRCUMFLEX ACCENT + "c4": "dc", # SMALL TILDE + "c5": "af", # MACRON + "c6": "d8", # BREVE + "c7": "d9", # DOT ABOVE + "c8": "a8", # DIAERESIS + "c9": "b2", # SUPERSCRIPT TWO + "ca": "da", # RING ABOVE + "cb": "b8", # CEDILLA + "cc": "b3", # SUPERSCRIPT THREE + "cd": "dd", # DOUBLE ACUTE ACCENT + "ce": "db", # OGONEK + "cf": "c7", # CARON + "d0": "14", # EM DASH + "d1": "b1", # PLUS-MINUS SIGN + "d2": "bc", # VULGAR FRACTION ONE QUARTER + "d3": "bd", # VULGAR FRACTION ONE HALF + "d4": "be", # VULGAR FRACTION THREE QUARTERS + "d5": "e0", # LATIN SMALL LETTER A WITH GRAVE + "d6": "e1", # LATIN SMALL LETTER A WITH ACUTE + "d7": "e2", # LATIN SMALL LETTER A WITH CIRCUMFLEX + "d8": "e3", # LATIN SMALL LETTER A WITH TILDE + "d9": "e4", # LATIN SMALL LETTER A WITH DIAERESIS + "da": "e5", # LATIN SMALL LETTER A WITH RING ABOVE + "db": "e7", # LATIN SMALL LETTER C WITH CEDILLA + "dc": "e8", # LATIN SMALL LETTER E WITH GRAVE + "dd": "e9", # LATIN SMALL LETTER E WITH ACUTE + "de": "ea", # LATIN SMALL LETTER E WITH CIRCUMFLEX + "df": "eb", # LATIN SMALL LETTER E WITH DIAERESIS + "e0": "ec", # LATIN SMALL LETTER I WITH GRAVE + "e1": "c6", # LATIN CAPITAL LETTER AE + "e2": "ed", # LATIN SMALL LETTER I WITH ACUTE + "e3": "aa", # FEMININE ORDINAL INDICATOR + "e4": "ee", # LATIN SMALL LETTER I WITH CIRCUMFLEX + "e5": "ef", # LATIN SMALL LETTER I WITH DIAERESIS + "e6": "f0", # LATIN SMALL LETTER ETH + "e7": "f1", # LATIN SMALL LETTER N WITH TILDE + "e8": "41", # LATIN CAPITAL LETTER L WITH STROKE + "e9": "d8", # LATIN CAPITAL LETTER O WITH STROKE + "ea": "52", # LATIN CAPITAL LIGATURE OE + "eb": "ba", # MASCULINE ORDINAL INDICATOR + "ec": "f2", # LATIN SMALL LETTER O WITH GRAVE + "ed": "f3", # LATIN SMALL LETTER O WITH ACUTE + "ee": "f4", # LATIN SMALL LETTER O WITH CIRCUMFLEX + "ef": "f5", # LATIN SMALL LETTER O WITH TILDE + "f0": "f6", # LATIN SMALL LETTER O WITH DIAERESIS + "f1": "e6", # LATIN SMALL LETTER AE + "f2": "f9", # LATIN SMALL LETTER U WITH GRAVE + "f3": "fa", # LATIN SMALL LETTER U WITH ACUTE + "f4": "fb", # LATIN SMALL LETTER U WITH CIRCUMFLEX + "f5": "31", # LATIN SMALL LETTER DOTLESS I + "f6": "fc", # LATIN SMALL LETTER U WITH DIAERESIS + "f7": "fd", # LATIN SMALL LETTER Y WITH ACUTE + "f8": "42", # LATIN SMALL LETTER L WITH STROKE + "f9": "f8", # LATIN SMALL LETTER O WITH STROKE + "fa": "53", # LATIN SMALL LIGATURE OE + "fb": "df", # LATIN SMALL LETTER SHARP S + "fc": "fe", # LATIN SMALL LETTER THORN + "fd": "ff", # LATIN SMALL LETTER Y WITH DIAERESIS + "fe": "fd", # .notdef, REPLACEMENT CHARACTER + "ff": "fd", # .notdef, REPLACEMENT CHARACTER + } + return conversion[hex_digits] + +def IsOctalNumber(character): + oct_digits = set(string.octdigits) + return set(character).issubset(oct_digits) + +def IsHexNumber(character): + hex_digits = set(string.hexdigits) + return set(character).issubset(hex_digits) + +def SanitizeCharacter(character): + char = character + escaped_characters = { + '\a': '\\a', + '\b': '\\b', + '\f': '\\f', + '\n': '\\n', + '\r': '\\r', + '\t': '\\t', + '\v': '\\v', + '\"': '\\"', + } + if character in escaped_characters.keys(): + char = escaped_characters[character] + return char + +# http://www.opensource.apple.com/source/CF/CF-744.19/CFOldStylePList.c See `getSlashedChar()` +def UnQuotifyString(string_data, start_index, end_index): # pylint: disable=too-many-locals,too-many-branches,too-many-statements + formatted_string = '' + extracted_string = string_data[start_index:end_index] + string_length = len(extracted_string) + all_cases = ['0', '1', '2', '3', '4', '5', '6', '7', 'a', 'b', 'f', 'n', 'r', 't', 'v', '\"', '\n', 'U'] + index = 0 + while index < string_length: # pylint: disable=too-many-nested-blocks + current_char = extracted_string[index] + if current_char == '\\': + next_char = extracted_string[index+1] + if next_char in all_cases: + index += 1 + if next_char == 'a': + formatted_string += '\a' + if next_char == 'b': + formatted_string += '\b' + if next_char == 'f': + formatted_string += '\f' + if next_char == 'n': + formatted_string += '\n' + if next_char == 'r': + formatted_string += '\r' + if next_char == 't': + formatted_string += '\t' + if next_char == 'v': + formatted_string += '\v' + if next_char == '"': + formatted_string += '\"' + if next_char == '\n': + formatted_string += '\n' + if next_char == 'U': + starting_index = index + 1 + ending_index = starting_index + 4 + unicode_numbers = extracted_string[starting_index:ending_index] + for number in unicode_numbers: + index += 1 + if IsHexNumber(number) is False: # pragma: no cover + message = 'Invalid unicode sequence on line '+str(LineNumberForIndex(string_data, start_index+index)) + raise Exception(message) + formatted_string += unichr(int(unicode_numbers, 16)) + if IsOctalNumber(next_char) is True: # https://twitter.com/Catfish_Man/status/658014170055507968 + starting_index = index + ending_index = starting_index + 1 + for oct_index in range(3): + test_index = starting_index + oct_index + test_oct = extracted_string[test_index] + if IsOctalNumber(test_oct) is True: + ending_index += 1 + octal_numbers = extracted_string[starting_index:ending_index] + hex_number = int(octal_numbers, 8) + hex_str = format(hex_number, 'x') + if hex_number >= 0x80: + hex_str = ConvertNEXTSTEPToUnicode(hex_str) + formatted_string += unichr(int('00'+hex_str, 16)) + else: + formatted_string += current_char + index += 1 + formatted_string += next_char + else: + formatted_string += current_char + index += 1 + return formatted_string + +def LineNumberForIndex(string_data, current_index): + line_number = 1 + index = 0 + string_length = len(string_data) + while (index < current_index) and (index < string_length): + current_char = string_data[index] + if IsNewline(current_char) is True: + line_number += 1 + index += 1 + return line_number + +def IsValidUnquotedStringCharacter(character): + if len(character) == 1: + valid_characters = set(string.ascii_letters+string.digits+'_$/:.-') + return set(character).issubset(valid_characters) + else: # pragma: no cover + message = 'The function "IsValidUnquotedStringCharacter()" can only take single characters!' + raise ValueError(message) + +def IsSpecialWhitespace(character): + value = ord(character) + result = (value >= 9 and value <= 13) # tab, newline, vt, form feed, carriage return + return result + +def IsUnicodeSeparator(character): + value = ord(character) + result = (value == 8232 or value == 8233) + return result + +def IsRegularWhitespace(character): + value = ord(character) + result = (value == 32 or IsUnicodeSeparator(character)) # space and Unicode line sep, para sep + return result + +def IsDataFormattingWhitespace(character): + value = ord(character) + result = (IsNewline(character) or IsRegularWhitespace(character) or value == 9) + return result + +def IsNewline(character): + value = ord(character) + result = (value == 13 or value == 10) + return result + +def IsEndOfLine(character): + result = (IsNewline(character) or IsUnicodeSeparator(character)) + return result + +def IndexOfNextNonSpace(string_data, current_index): # pylint: disable=too-many-branches,too-many-statements + successful = False + found_index = current_index + string_length = len(string_data) + annotation_string = '' + while found_index < string_length: # pylint: disable=too-many-nested-blocks + current_char = string_data[found_index] + if IsSpecialWhitespace(current_char) is True: + found_index += 1 + continue + if IsRegularWhitespace(current_char) is True: + found_index += 1 + continue + if current_char == '/': + next_index = found_index + 1 + if next_index >= string_length: + successful = True + break + else: + next_character = string_data[next_index] + if next_character == '/': # found a line comment "//" + found_index += 1 + next_index = found_index + first_pass = True + while next_index < string_length: + test_char = string_data[next_index] + if IsEndOfLine(test_char) is True: + break + else: + if first_pass is False: + annotation_string += test_char + else: + first_pass = False + next_index += 1 + found_index = next_index + elif next_character == '*': # found a block comment "/* ... */" + found_index += 1 + next_index = found_index + first_pass = True + while next_index < string_length: + test_char = string_data[next_index] + if test_char == '*' and (next_index+1 < string_length) and string_data[next_index+1] == '/': + next_index += 2 + break + else: + if first_pass != True: + annotation_string += test_char + else: + first_pass = False + next_index += 1 + found_index = next_index + else: + successful = True + break + else: + successful = True + break + result = (successful, found_index, annotation_string) + return result diff --git a/jc/parsers/pbPlist/Switch.py b/jc/parsers/pbPlist/Switch.py new file mode 100644 index 00000000..7547c7e7 --- /dev/null +++ b/jc/parsers/pbPlist/Switch.py @@ -0,0 +1,50 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pylocalizer +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +# Original code taken from http://code.activestate.com/recipes/410692/ + +class Switch(object): + def __init__(self, value): + self.value = value + self.fall = False + + def __iter__(self): + """Return the match method once, then stop""" + yield self.match + + def match(self, *args): + """Indicate whether or not to enter a case suite""" + result = False + if self.fall or not args: + result = True + elif self.value in args: # changed for v1.5, see below + self.fall = True + result = True + return result diff --git a/jc/parsers/pbPlist/__init__.py b/jc/parsers/pbPlist/__init__.py new file mode 100644 index 00000000..4b03e3a6 --- /dev/null +++ b/jc/parsers/pbPlist/__init__.py @@ -0,0 +1,32 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pbPlist +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +from . import pbPlist +__version__ = '1.0.4' \ No newline at end of file diff --git a/jc/parsers/pbPlist/pbItem.py b/jc/parsers/pbPlist/pbItem.py new file mode 100644 index 00000000..f1003462 --- /dev/null +++ b/jc/parsers/pbPlist/pbItem.py @@ -0,0 +1,262 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pbPlist +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +from . import StrParse + +def PushIndent(indent_level): + return indent_level + 1 + +def PopIndent(indent_level): + return indent_level - 1 + +def WriteIndent(level=0): + output = '' + for _index in range(level): + output += '\t' + return output + +def WriteNewline(level=0, indent=True): + output = '\n' + if indent: + output += WriteIndent(level) + return output + +class pbItem(object): + def __init__(self, value=None, type_name=None, annotation=None): + if value != None and type_name != None: + self.value = value + if type_name not in KnownTypes.keys(): # pragma: no cover + message = 'Unknown type "'+type_name+'" passed to '+self.__class__.__name__+' initializer!' + raise TypeError(message) + self.type_name = type_name + self.annotation = annotation + else: # pragma: no cover + message = 'The class "'+self.__class__.__name__+'" must be initialized with a non-None value' + raise ValueError(message) + + def __eq__(self, other): + is_equal = False + if isinstance(other, pbItem): + other = other.value + if type(other) is type(self.value): + is_equal = self.value.__eq__(other) + return is_equal + + def __hash__(self): + return self.value.__hash__() + + def __repr__(self): + return self.value.__repr__() + + def __iter__(self): + return self.value.__iter__() + + def __getattr__(self, attrib): + return self.value.__getattr__(attrib) + + def __str__(self): + return self.writeStringRep(0, False)[0] + + def __getitem__(self, key): + return self.value.__getitem__(key) + + def __setitem__(self, key, value): + self.value.__setitem__(key, value) + + def __len__(self): + return self.value.__len__() + + def __contains__(self, item): + return self.value.__contains__(item) + + def __get__(self, obj, objtype): + return self.value.__get__(obj, objtype) + + def writeStringRep(self, indent_level=0, pretty=True): + return self.writeString(indent_level, pretty) + + def writeString(self, indent_level=0, pretty=True): # pylint: disable=no-self-use,unused-variable,unused-argument ; # pragma: no cover + message = 'This is a base class, it cannot write!' + raise Exception(message) + + def nativeType(self): + return self.value + + def writeAnnotation(self): + output_string = '' + if self.annotation != None and len(self.annotation) > 0: + output_string += ' ' + output_string += '/*' + output_string += self.annotation + output_string += '*/' + return output_string + +class pbString(pbItem): + def writeString(self, indent_level=0, pretty=True): + string_string = '' + string_string += self.value + if pretty is True: + string_string += self.writeAnnotation() + return (string_string, indent_level) + +class pbQString(pbItem): + def writeStringRep(self, indent_level=0, pretty=True): + qstring_string = '' + for character in self.value: + qstring_string += StrParse.SanitizeCharacter(character) + return (qstring_string, indent_level) + + def writeString(self, indent_level=0, pretty=True): + qstring_string = '' + qstring_string += '"' + string_rep, indent_level = self.writeStringRep(indent_level, pretty) + qstring_string += string_rep + qstring_string += '"' + if pretty is True: + qstring_string += self.writeAnnotation() + return (qstring_string, indent_level) + +class pbData(pbItem): + def writeString(self, indent_level=0, pretty=True): + data_string = '' + indent_level = PushIndent(indent_level) + data_string += '<' + grouping_byte_counter = 0 + grouping_line_counter = 0 + for hex_byte in map(ord, self.value.decode()): + data_string += format(hex_byte, 'x') + grouping_byte_counter += 1 + # write a space every 4th byte + if grouping_byte_counter == 4: + data_string += ' ' + grouping_byte_counter = 0 + grouping_line_counter += 1 + # write a newline every 4th grouping of bytes + if grouping_line_counter == 4: + data_string += WriteNewline(indent_level) + data_string += ' ' # indent an additional space to make the byte groupings line up + grouping_line_counter = 0 + data_string += '>' + if pretty is True: + data_string += self.writeAnnotation() + indent_level = PopIndent(indent_level) + return (data_string, indent_level) + +class pbDictionary(pbItem): + def nativeType(self): + new_value = dict() + for key in self.keys(): + value = self[key] + new_value[str(key)] = value.nativeType() + return new_value + def writeString(self, indent_level=0, pretty=True): + dictionary_string = '' + dictionary_string += '{' + has_sorted_keys, keys_array = self.value.sortedKeys() + dictionary_string += WriteNewline(indent_level, not has_sorted_keys) + indent_level = PushIndent(indent_level) + previous_value_type = None + if len(keys_array) == 0: + indent_level = PopIndent(indent_level) + else: + if not has_sorted_keys: + dictionary_string += '\t' + for key in keys_array: + if has_sorted_keys: + current_value_type = str(self.value[key]['isa']) + if previous_value_type != current_value_type: + if previous_value_type != None: + dictionary_string += '/* End '+previous_value_type+' section */' + dictionary_string += WriteNewline(indent_level, False) + previous_value_type = current_value_type + dictionary_string += '\n/* Begin '+current_value_type+' section */' + dictionary_string += WriteNewline(indent_level) + else: + dictionary_string += WriteIndent(indent_level) + write_string, indent_level = key.writeString(indent_level, pretty) + dictionary_string += write_string + dictionary_string += ' = ' + write_string, indent_level = self.value[key].writeString(indent_level, pretty) + dictionary_string += write_string + dictionary_string += ';' + should_indent = True + is_last_key = (key == keys_array[-1]) + if is_last_key: + if has_sorted_keys: + dictionary_string += WriteNewline(indent_level, False) + dictionary_string += '/* End '+previous_value_type+' section */' + indent_level = PopIndent(indent_level) + else: + if has_sorted_keys: + should_indent = False + dictionary_string += WriteNewline(indent_level, should_indent) + dictionary_string += '}' + return (dictionary_string, indent_level) + +class pbArray(pbItem): + def nativeType(self): + new_value = [item.nativeType() for item in self.value] + return new_value + def writeString(self, indent_level=0, pretty=True): + array_string = '' + array_string += '(' + array_string += WriteNewline(indent_level) + indent_level = PushIndent(indent_level) + values_array = list(self.value) + if len(values_array) == 0: + indent_level = PopIndent(indent_level) + else: + array_string += '\t' + for value in values_array: + write_string, indent_level = value.writeString(indent_level, pretty) + array_string += write_string + if value != values_array[-1]: + array_string += ',' + else: + indent_level = PopIndent(indent_level) + array_string += WriteNewline(indent_level) + array_string += ')' + return (array_string, indent_level) + +KnownTypes = { + 'string': pbString, + 'qstring': pbQString, + 'data': pbData, + 'dictionary': pbDictionary, + 'array': pbArray, +} + +def pbItemResolver(obj, type_name): + initializer = KnownTypes[type_name] + if initializer: + return initializer(obj, type_name) + else: # pragma: no cover + message = 'Unknown type "'+type_name+'" passed to pbItemResolver!' + raise TypeError(message) diff --git a/jc/parsers/pbPlist/pbParser.py b/jc/parsers/pbPlist/pbParser.py new file mode 100644 index 00000000..ab209a2a --- /dev/null +++ b/jc/parsers/pbPlist/pbParser.py @@ -0,0 +1,291 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pbPlist +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import print_function +import os +import sys +import codecs +from . import StrParse +from . import pbRoot +from . import pbItem +from .Switch import Switch + +def GetFileEncoding(path): + encoding = 'utf-8-sig' + + size = os.path.getsize(path) + if size > 2: + file_descriptor = OpenFile(path) + first_two_bytes = file_descriptor.read(2) + file_descriptor.close() + + for case in Switch(first_two_bytes): + if case(codecs.BOM_UTF16): + encoding = 'utf-16' + break + if case(codecs.BOM_UTF16_LE): + encoding = 'utf-16-le' + break + if case(codecs.BOM_UTF16_BE): + encoding = 'utf-16-be' + break + if case(): + break # pragma: no cover + + return encoding + +def OpenFileWithEncoding(file_path, encoding): + return codecs.open(file_path, 'r', encoding=encoding, errors='ignore') + +if sys.version_info < (3, 0): + def OpenFile(file_path): + return open(file_path, 'rb') +else: + def OpenFile(file_path): + return open(file_path, 'br') + +class PBParser(object): + + def __init__(self, file_path=None): + self.index = 0 + self.string_encoding = None + self.file_path = file_path + self.file_type = None + try: + encoding = GetFileEncoding(self.file_path) + file_descriptor = OpenFileWithEncoding(self.file_path, encoding) + self.data = file_descriptor.read() + if self.file_path.endswith('.strings'): + self.data = '{'+self.data+'}' + file_descriptor.close() + except IOError as exception: # pragma: no cover + print('I/O error({0}): {1}'.format(exception.errno, exception.strerror)) + except: # pragma: no cover + print('Unexpected error:'+str(sys.exc_info()[0])) + raise + + def read(self): + parsed_plist = None + prefix = self.data[0:6] + for case in Switch(prefix): + if case('bplist'): + self.file_type = 'binary' + import biplist + parsed_plist = biplist.readPlist(self.file_path) + break + if case('= string_length: # pragma: no cover + message = 'Unterminated quoted string starting on line %s in file %s' % (str(StrParse.LineNumberForIndex(self.data, start_index)), self.file_path) + raise Exception(message) + else: + string_without_quotes = StrParse.UnQuotifyString(self.data, start_index, self.index) + self.index += 1 # advance past quote character + return string_without_quotes + + def __parseData(self): + string_length = len(self.data) + self.index += 1 # skip over "<" + start_index = self.index + end_index = 0 + byte_stream = '' + while self.index < string_length: + current_char = self.data[self.index] + if current_char == '>': + self.index += 1 # move past the ">" + end_index = self.index + break + if StrParse.IsHexNumber(current_char) is True: + byte_stream += current_char + else: + if not StrParse.IsDataFormattingWhitespace(current_char): # pragma: no cover + message = 'Malformed data byte group (invalid hex) at line %s in file %s' % (str(StrParse.LineNumberForIndex(self.data, start_index)), self.file_path) + raise Exception(message) + self.index += 1 + if (len(byte_stream) % 2) == 1: # pragma: no cover + message = 'Malformed data byte group (uneven length) at line %s in file %s' % (str(StrParse.LineNumberForIndex(self.data, start_index)), self.file_path) + raise Exception(message) + if end_index == 0: # pragma: no cover + message = 'Expected terminating >" for data at line %s in file %s' % (str(StrParse.LineNumberForIndex(self.data, start_index)), self.file_path) + raise Exception(message) + data_object = bytearray.fromhex(byte_stream) + return data_object + + def __parseArray(self): + array_objects = list() + self.index += 1 # move past the "(" + start_index = self.index + new_object = self.__readTest(False) + while new_object is not None: + can_parse, self.index, new_object.annotation = StrParse.IndexOfNextNonSpace(self.data, self.index) + _can_parse = can_parse # pylint: disable=unused-variable + array_objects.append(new_object) + current_char = self.data[self.index] + if current_char == ',': + self.index += 1 + new_object = self.__readTest(False) + current_char = self.data[self.index] + if current_char != ')': # pragma: no cover + message = 'Expected terminating ")" for array at line %s in file %s' % (str(StrParse.LineNumberForIndex(self.data, start_index)), self.file_path) + raise Exception(message) + self.index += 1 # skip over ending ")" + return array_objects + + def __parseDict(self): + dictionary = pbRoot.pbRoot() + self.index += 1 # move past the "{" + start_index = self.index + new_object = self.__readTest(False) + while new_object is not None: + can_parse, self.index, new_object.annotation = StrParse.IndexOfNextNonSpace(self.data, self.index) + _can_parse = can_parse # pylint: disable=unused-variable + key_object = new_object + current_char = self.data[self.index] + value_object = None + for case in Switch(current_char): + if case('='): + self.index += 1 + value_object = self.__readTest(True) + break + if case(';'): + # this is for strings files where the key and the value may be the same thing + self.index += 1 + value_object = pbItem.pbItemResolver(new_object.value, new_object.type_name) + value_object.annotation = new_object.annotation + break + if case(): # pragma: no cover + message = 'Missing ";" or "=" on line %s in file %s' % (str(StrParse.LineNumberForIndex(self.data, start_index)), self.file_path) + raise Exception(message) + can_parse, self.index, annotation = StrParse.IndexOfNextNonSpace(self.data, self.index) + _can_parse = can_parse # pylint: disable=unused-variable + if value_object.annotation is None: # this is to prevent losing the annotation of the key when parsing strings dicts + value_object.annotation = annotation + dictionary[key_object] = value_object + current_char = self.data[self.index] + if current_char == ';': + self.index += 1 # advancing to the next key + new_object = self.__readTest(False) + current_char = self.data[self.index] + if current_char != '}': # pragma: no cover + message = 'Expected terminating "}" for dictionary at line %s in file %s' % (str(StrParse.LineNumberForIndex(self.data, start_index)), self.file_path) + raise Exception(message) + self.index += 1 # skip over ending "}" + return dictionary diff --git a/jc/parsers/pbPlist/pbPlist.py b/jc/parsers/pbPlist/pbPlist.py new file mode 100644 index 00000000..0239631f --- /dev/null +++ b/jc/parsers/pbPlist/pbPlist.py @@ -0,0 +1,55 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pbPlist +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +import os +from .pbParser import PBParser +from .pbSerializer import PBSerializer + +class PBPlist(object): + + def __init__(self, file_path): + self.root = None + if self.__checkFile(file_path) is True: + parser = PBParser(self.file_path) + self.root = parser.read() + self.string_encoding = parser.string_encoding + self.file_type = parser.file_type + + def write(self, file_path=None): + if file_path is None: + file_path = self.file_path + serializer = PBSerializer(file_path, self.string_encoding, self.file_type) + serializer.write(self.root) + + def __checkFile(self, file_path): + can_access_file = os.path.exists(file_path) + if can_access_file is True: + self.file_path = file_path + return can_access_file diff --git a/jc/parsers/pbPlist/pbRoot.py b/jc/parsers/pbPlist/pbRoot.py new file mode 100644 index 00000000..972a7d86 --- /dev/null +++ b/jc/parsers/pbPlist/pbRoot.py @@ -0,0 +1,110 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pbPlist +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +from functools import cmp_to_key +import collections +from . import pbItem + +def StringCmp(obj1, obj2): + result = -1 + if obj1 > obj2: + result = 1 + elif obj1 == obj2: + result = 0 + return result + +def KeySorter(obj1, obj2): + result = 0 + if str(obj1) == 'isa': + result = -1 + elif str(obj2) == 'isa': + result = 1 + else: + result = StringCmp(str(obj1), str(obj2)) + return result + +class pbRoot(collections.MutableMapping): + + def __init__(self, *args, **kwargs): + self.store = dict() + self.key_storage = list() + self.update(dict(*args, **kwargs)) # use the free update to set keys + + def __internalKeyCheck(self, key): # pylint: disable=no-self-use + safe_key = key + if isinstance(safe_key, str): + safe_key = pbItem.pbItemResolver(safe_key, 'qstring') + return safe_key + + def __getitem__(self, key): + return self.store[key] + + def __setitem__(self, key, value): + if key not in self.key_storage: + self.key_storage.append(self.__internalKeyCheck(key)) + self.store[key] = value + + def __delitem__(self, key): + if key in self.key_storage: + self.key_storage.remove(key) + del self.store[key] + + def __iter__(self): + return self.key_storage.__iter__() + + def __len__(self): + return self.key_storage.__len__() + + def __str__(self): + return self.store.__str__() + + def __contains__(self, item): + return item in self.key_storage + + def __getattr__(self, attrib): + return getattr(self.store, attrib) + + def __keytransform__(self, key): # pylint: disable=no-self-use + result = key + if isinstance(key, pbItem.pbItem): + result = key.value + return result + + def sortedKeys(self): + unsorted_keys = self.key_storage + sorted_keys = sorted(unsorted_keys, key=cmp_to_key(KeySorter)) + can_sort = False + if len(sorted_keys) > 0: + all_dictionaries = all((isinstance(self[key].value, dict) or isinstance(self[key].value, pbRoot)) for key in unsorted_keys) + if all_dictionaries: + can_sort = all(self[key].get('isa', None) is not None for key in unsorted_keys) + if can_sort: + sorted_keys = sorted(unsorted_keys, key=lambda k: str(self[k]['isa'])) + return (can_sort, sorted_keys) diff --git a/jc/parsers/pbPlist/pbSerializer.py b/jc/parsers/pbPlist/pbSerializer.py new file mode 100644 index 00000000..47b35b4e --- /dev/null +++ b/jc/parsers/pbPlist/pbSerializer.py @@ -0,0 +1,75 @@ +# Copyright (c) 2016, Samantha Marshall (http://pewpewthespells.com) +# All rights reserved. +# +# https://github.com/samdmarshall/pbPlist +# +# Redistribution and use in source and binary forms, with or without modification, +# are permitted provided that the following conditions are met: +# +# 1. Redistributions of source code must retain the above copyright notice, this +# list of conditions and the following disclaimer. +# +# 2. Redistributions in binary form must reproduce the above copyright notice, +# this list of conditions and the following disclaimer in the documentation and/or +# other materials provided with the distribution. +# +# 3. Neither the name of Samantha Marshall nor the names of its contributors may +# be used to endorse or promote products derived from this software without +# specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +# IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, +# INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR +# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED +# OF THE POSSIBILITY OF SUCH DAMAGE. + +from __future__ import print_function +import sys +from .Switch import Switch + +class PBSerializer(object): + + def __init__(self, file_path=None, encoding=None, file_type=None): + self.string_encoding = encoding + self.file_path = file_path + self.file_type = file_type + + def write(self, obj=None): + for case in Switch(self.file_type): + if case('ascii'): + try: + file_descriptor = open(self.file_path, 'w') + self.__writeObject(file_descriptor, obj) + file_descriptor.close() + except IOError as exception: # pragma: no cover + print('I/O error({0}): {1}'.format(exception.errno, exception.strerror)) + except: # pragma: no cover + print('Unexpected error:'+str(sys.exc_info()[0])) + raise + break + if case('binary'): + import biplist + biplist.writePlist(obj, self.file_path) + break + if case('xml'): + import plistlib + plistlib.writePlist(obj, self.file_path) + break + if case(): + break + + def __writeObject(self, file_descriptor=None, obj=None): + if file_descriptor is None: # pragma: no cover + message = 'Fatal error, file descriptor is None' + raise TypeError(message) + if self.string_encoding is not None: + file_descriptor.write('// !$*'+self.string_encoding+'*$!\n') + if obj is not None: + write_string, indent_level = obj.writeString() + _ = indent_level + file_descriptor.write(write_string) diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index e4e9da62..f39eb8b9 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -1,6 +1,6 @@ """jc - JSON Convert PLIST file parser -Converts binary and XML PLIST files. +Converts binary, XML, and NeXTSTEP PLIST files. Binary values are converted into an ASCII hex representation. @@ -48,15 +48,17 @@ from typing import Dict, Union import plistlib import binascii from datetime import datetime +from jc.parsers.pbPlist.pbPlist import PBPlist import jc.utils class info(): """Provides parser metadata (version, author, etc.)""" - version = '1.0' + version = '1.1' description = 'PLIST file parser' author = 'Kelly Brazil' author_email = 'kellyjonbrazil@gmail.com' + details = 'Using the pbPlist library from https://github.com/samdmarshall/pbPlist/releases/tag/v1.0.4 for NeXTSTEP support' compatible = ['linux', 'darwin', 'cygwin', 'win32', 'aix', 'freebsd'] tags = ['standard', 'file', 'string', 'binary'] @@ -157,7 +159,20 @@ def parse( if isinstance(data, str): data = bytes(data, 'utf-8') - raw_output = plistlib.loads(data) + try: + raw_output = plistlib.loads(data) + + except Exception: + # Try parsing as an old-style NeXTSTEP Plist format + # pbPlist library only works on file paths, not strings :( + import tempfile + with tempfile.NamedTemporaryFile(mode='w+') as plist_file: + data = data.decode() + plist_file.write(data) + plist_file.seek(0) + parsed_plist = PBPlist(plist_file.name) + raw_output = parsed_plist.root.nativeType() + raw_output = _fix_objects(raw_output) return raw_output if raw else _process(raw_output) diff --git a/man/jc.1 b/man/jc.1 index f418aa46..38a59f03 100644 --- a/man/jc.1 +++ b/man/jc.1 @@ -1,4 +1,4 @@ -.TH jc 1 2022-12-29 1.22.4 "JSON Convert" +.TH jc 1 2022-12-30 1.22.4 "JSON Convert" .SH NAME \fBjc\fP \- JSON Convert JSONifies the output of many CLI tools, file-types, and strings .SH SYNOPSIS diff --git a/tests/fixtures/generic/plist-nextstep.json b/tests/fixtures/generic/plist-nextstep.json new file mode 100644 index 00000000..ac6c3886 --- /dev/null +++ b/tests/fixtures/generic/plist-nextstep.json @@ -0,0 +1 @@ +{"archiveVersion":"1","classes":{},"objectVersion":"46","objects":{"15D488AC1BD3D6C700EC46B1":{"isa":"PBXAggregateTarget","buildConfigurationList":"15D488AD1BD3D6C700EC46B1","buildPhases":["15D488B01BD3D6D000EC46B1"],"dependencies":[],"name":"Lemon Setup","productName":"Lemon Setup"},"15D488BB1BD3D7A900EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488BA1BD3D7A900EC46B1"},"15D488BD1BD3D7A900EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488BC1BD3D7A900EC46B1"},"15D488C41BD3D7A900EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488C31BD3D7A900EC46B1"},"15D488C71BD3D7A900EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488C61BD3D7A900EC46B1"},"15D488D01BD3D9CD00EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488CC1BD3D9CD00EC46B1"},"15D488D11BD3D9CD00EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488CD1BD3D9CD00EC46B1"},"15D488D21BD3D9CD00EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488CE1BD3D9CD00EC46B1"},"15D488D71BD3DA8800EC46B1":{"isa":"PBXBuildFile","fileRef":"15D488D61BD3DA8800EC46B1"},"15D488B11BD3D6F400EC46B1":{"isa":"PBXContainerItemProxy","containerPortal":"15D488951BD3D59200EC46B1","proxyType":"1","remoteGlobalIDString":"15D488AC1BD3D6C700EC46B1","remoteInfo":"Lemon Setup"},"15D488D81BD3DA9C00EC46B1":{"isa":"PBXContainerItemProxy","containerPortal":"15D488951BD3D59200EC46B1","proxyType":"1","remoteGlobalIDString":"15D488991BD3D59200EC46B1","remoteInfo":"Lemon"},"158E53E61BD3E6A600F75AAD":{"isa":"PBXFileReference","lastKnownFileType":"net.daringfireball.markdown","path":"PluginUsage.md","sourceTree":""},"15D488A01BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text.script.sh","path":"autogen.sh","sourceTree":""},"15D488A11BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text","path":"citrus.pc.in","sourceTree":""},"15D488A21BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text","path":"configure.ac","sourceTree":""},"15D488A31BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"sourcecode.c.c","path":"lemon.c","sourceTree":""},"15D488A41BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"sourcecode.c.c","path":"lempar.c","sourceTree":""},"15D488A51BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"sourcecode.c.h","path":"lempar.h","sourceTree":""},"15D488A61BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text","path":"LICENSE","sourceTree":""},"15D488A71BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text","path":"Makefile.am","sourceTree":""},"15D488A81BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"net.daringfireball.markdown","path":"README.md","sourceTree":""},"15D488A91BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text","path":"VERSION","sourceTree":""},"15D488AA1BD3D62C00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text","path":"version.h.in","sourceTree":""},"15D488B71BD3D7A900EC46B1":{"isa":"PBXFileReference","explicitFileType":"wrapper.cfbundle","includeInIndex":"0","path":"CitrusPlugin.xcplugin","sourceTree":"BUILT_PRODUCTS_DIR"},"15D488BA1BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"wrapper.framework","name":"AppKit.framework","path":"/System/Library/Frameworks/AppKit.framework","sourceTree":""},"15D488BC1BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"wrapper.framework","name":"Foundation.framework","path":"/System/Library/Frameworks/Foundation.framework","sourceTree":""},"15D488C01BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"text.xml","name":"CitrusPlugin.xcscheme","path":"CitrusPlugin.xcodeproj/xcshareddata/xcschemes/CitrusPlugin.xcscheme","sourceTree":"SOURCE_ROOT"},"15D488C21BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"sourcecode.c.h","path":"CitrusPlugin.h","sourceTree":""},"15D488C31BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"sourcecode.c.objc","path":"CitrusPlugin.m","sourceTree":""},"15D488C51BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"sourcecode.c.h","path":"NSObject_Extension.h","sourceTree":""},"15D488C61BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"sourcecode.c.objc","path":"NSObject_Extension.m","sourceTree":""},"15D488C81BD3D7A900EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"text.plist.xml","path":"Info.plist","sourceTree":""},"15D488CC1BD3D9CD00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text.plist.pbfilespec","path":"Lemon.pbfilespec","sourceTree":""},"15D488CD1BD3D9CD00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text.plist.strings","path":"Lemon.strings","sourceTree":""},"15D488CE1BD3D9CD00EC46B1":{"isa":"PBXFileReference","fileEncoding":"4","lastKnownFileType":"text.plist.xcspec","path":"Lemon.xcspec","sourceTree":""},"15D488D61BD3DA8800EC46B1":{"isa":"PBXFileReference","lastKnownFileType":"compiled.mach-o.executable","path":"lemon","sourceTree":""},"15D488B51BD3D7A900EC46B1":{"isa":"PBXFrameworksBuildPhase","buildActionMask":"2147483647","files":["15D488BB1BD3D7A900EC46B1","15D488BD1BD3D7A900EC46B1"],"runOnlyForDeploymentPostprocessing":"0"},"15D488941BD3D59200EC46B1":{"isa":"PBXGroup","children":["158E53E61BD3E6A600F75AAD","15D4889F1BD3D5D500EC46B1","15D488BE1BD3D7A900EC46B1","15D488B91BD3D7A900EC46B1","15D488B81BD3D7A900EC46B1","15D488D61BD3DA8800EC46B1"],"sourceTree":""},"15D4889F1BD3D5D500EC46B1":{"isa":"PBXGroup","children":["15D488A01BD3D62C00EC46B1","15D488A11BD3D62C00EC46B1","15D488A21BD3D62C00EC46B1","15D488A31BD3D62C00EC46B1","15D488A41BD3D62C00EC46B1","15D488A51BD3D62C00EC46B1","15D488A61BD3D62C00EC46B1","15D488A71BD3D62C00EC46B1","15D488A81BD3D62C00EC46B1","15D488A91BD3D62C00EC46B1","15D488AA1BD3D62C00EC46B1"],"name":"lemon","sourceTree":""},"15D488B81BD3D7A900EC46B1":{"isa":"PBXGroup","children":["15D488B71BD3D7A900EC46B1"],"name":"Products","sourceTree":""},"15D488B91BD3D7A900EC46B1":{"isa":"PBXGroup","children":["15D488BA1BD3D7A900EC46B1","15D488BC1BD3D7A900EC46B1"],"name":"Frameworks","sourceTree":""},"15D488BE1BD3D7A900EC46B1":{"isa":"PBXGroup","children":["15D488D41BD3D9D000EC46B1","15D488C21BD3D7A900EC46B1","15D488C31BD3D7A900EC46B1","15D488C51BD3D7A900EC46B1","15D488C61BD3D7A900EC46B1","15D488C81BD3D7A900EC46B1","15D488BF1BD3D7A900EC46B1"],"path":"CitrusPlugin","sourceTree":""},"15D488BF1BD3D7A900EC46B1":{"isa":"PBXGroup","children":["15D488C01BD3D7A900EC46B1"],"name":"Supporting Files","sourceTree":""},"15D488D41BD3D9D000EC46B1":{"isa":"PBXGroup","children":["15D488CC1BD3D9CD00EC46B1","15D488CD1BD3D9CD00EC46B1","15D488CE1BD3D9CD00EC46B1"],"name":"Resources","sourceTree":""},"15D488991BD3D59200EC46B1":{"isa":"PBXLegacyTarget","buildArgumentsString":"${ACTION}","buildConfigurationList":"15D4889C1BD3D59200EC46B1","buildPhases":[],"buildToolPath":"/usr/bin/make","buildWorkingDirectory":"$(PROJECT_DIR)","dependencies":["15D488B21BD3D6F400EC46B1"],"name":"Lemon","passBuildSettingsInEnvironment":"1","productName":"CitrusPlugin"},"15D488B61BD3D7A900EC46B1":{"isa":"PBXNativeTarget","buildConfigurationList":"15D488C91BD3D7A900EC46B1","buildPhases":["15D488B31BD3D7A900EC46B1","15D488B41BD3D7A900EC46B1","15D488B51BD3D7A900EC46B1","15D488DC1BD3DBBB00EC46B1"],"buildRules":[],"dependencies":["15D488D91BD3DA9C00EC46B1"],"name":"CitrusPlugin","productName":"CitrusPlugin","productReference":"15D488B71BD3D7A900EC46B1","productType":"com.apple.product-type.bundle"},"15D488951BD3D59200EC46B1":{"isa":"PBXProject","attributes":{"LastUpgradeCheck":"0700","ORGANIZATIONNAME":"Samantha Marshall","TargetAttributes":{"15D488991BD3D59200EC46B1":{"CreatedOnToolsVersion":"7.0.1"},"15D488AC1BD3D6C700EC46B1":{"CreatedOnToolsVersion":"7.0.1"},"15D488B61BD3D7A900EC46B1":{"CreatedOnToolsVersion":"7.0.1"}}},"buildConfigurationList":"15D488981BD3D59200EC46B1","compatibilityVersion":"Xcode 3.2","developmentRegion":"English","hasScannedForEncodings":"0","knownRegions":["en"],"mainGroup":"15D488941BD3D59200EC46B1","productRefGroup":"15D488B81BD3D7A900EC46B1","projectDirPath":"","projectRoot":"","targets":["15D488AC1BD3D6C700EC46B1","15D488991BD3D59200EC46B1","15D488B61BD3D7A900EC46B1"]},"15D488B41BD3D7A900EC46B1":{"isa":"PBXResourcesBuildPhase","buildActionMask":"2147483647","files":["15D488D71BD3DA8800EC46B1","15D488D01BD3D9CD00EC46B1","15D488D11BD3D9CD00EC46B1","15D488D21BD3D9CD00EC46B1"],"runOnlyForDeploymentPostprocessing":"0"},"15D488B01BD3D6D000EC46B1":{"isa":"PBXShellScriptBuildPhase","buildActionMask":"2147483647","files":[],"inputPaths":[],"name":"make distclean ; ./autogen.sh ; ./configure","outputPaths":[],"runOnlyForDeploymentPostprocessing":"0","shellPath":"/bin/sh","shellScript":"cd ${PROJECT_DIR}\n\nif [ -e Makefile ]; then\n\tmake distclean\nfi\n\n./autogen.sh\n./configure"},"15D488DC1BD3DBBB00EC46B1":{"isa":"PBXShellScriptBuildPhase","buildActionMask":"2147483647","files":[],"inputPaths":[],"name":"Copying default template into bundle because Xcode has a bug","outputPaths":[],"runOnlyForDeploymentPostprocessing":"0","shellPath":"/bin/sh","shellScript":"cp ${PROJECT_DIR}/lempar.c ${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.${WRAPPER_EXTENSION}/Contents/Resources\ncp ${PROJECT_DIR}/lempar.h ${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.${WRAPPER_EXTENSION}/Contents/Resources"},"15D488B31BD3D7A900EC46B1":{"isa":"PBXSourcesBuildPhase","buildActionMask":"2147483647","files":["15D488C41BD3D7A900EC46B1","15D488C71BD3D7A900EC46B1"],"runOnlyForDeploymentPostprocessing":"0"},"15D488B21BD3D6F400EC46B1":{"isa":"PBXTargetDependency","target":"15D488AC1BD3D6C700EC46B1","targetProxy":"15D488B11BD3D6F400EC46B1"},"15D488D91BD3DA9C00EC46B1":{"isa":"PBXTargetDependency","target":"15D488991BD3D59200EC46B1","targetProxy":"15D488D81BD3DA9C00EC46B1"},"15D4889A1BD3D59200EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"ALWAYS_SEARCH_USER_PATHS":"NO","CLANG_CXX_LANGUAGE_STANDARD":"gnu++0x","CLANG_CXX_LIBRARY":"libc++","CLANG_ENABLE_MODULES":"YES","CLANG_ENABLE_OBJC_ARC":"YES","CLANG_WARN_BOOL_CONVERSION":"YES","CLANG_WARN_CONSTANT_CONVERSION":"YES","CLANG_WARN_DIRECT_OBJC_ISA_USAGE":"YES_ERROR","CLANG_WARN_EMPTY_BODY":"YES","CLANG_WARN_ENUM_CONVERSION":"YES","CLANG_WARN_INT_CONVERSION":"YES","CLANG_WARN_OBJC_ROOT_CLASS":"YES_ERROR","CLANG_WARN_UNREACHABLE_CODE":"YES","CLANG_WARN__DUPLICATE_METHOD_MATCH":"YES","COPY_PHASE_STRIP":"NO","DEBUG_INFORMATION_FORMAT":"dwarf","ENABLE_STRICT_OBJC_MSGSEND":"YES","ENABLE_TESTABILITY":"YES","GCC_C_LANGUAGE_STANDARD":"gnu99","GCC_DYNAMIC_NO_PIC":"NO","GCC_NO_COMMON_BLOCKS":"YES","GCC_OPTIMIZATION_LEVEL":"0","GCC_PREPROCESSOR_DEFINITIONS":["DEBUG=1","$(inherited)"],"GCC_WARN_64_TO_32_BIT_CONVERSION":"YES","GCC_WARN_ABOUT_RETURN_TYPE":"YES_ERROR","GCC_WARN_UNDECLARED_SELECTOR":"YES","GCC_WARN_UNINITIALIZED_AUTOS":"YES_AGGRESSIVE","GCC_WARN_UNUSED_FUNCTION":"YES","GCC_WARN_UNUSED_VARIABLE":"YES","MTL_ENABLE_DEBUG_INFO":"YES","ONLY_ACTIVE_ARCH":"NO","SDKROOT":"macosx"},"name":"Debug"},"15D4889B1BD3D59200EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"ALWAYS_SEARCH_USER_PATHS":"NO","CLANG_CXX_LANGUAGE_STANDARD":"gnu++0x","CLANG_CXX_LIBRARY":"libc++","CLANG_ENABLE_MODULES":"YES","CLANG_ENABLE_OBJC_ARC":"YES","CLANG_WARN_BOOL_CONVERSION":"YES","CLANG_WARN_CONSTANT_CONVERSION":"YES","CLANG_WARN_DIRECT_OBJC_ISA_USAGE":"YES_ERROR","CLANG_WARN_EMPTY_BODY":"YES","CLANG_WARN_ENUM_CONVERSION":"YES","CLANG_WARN_INT_CONVERSION":"YES","CLANG_WARN_OBJC_ROOT_CLASS":"YES_ERROR","CLANG_WARN_UNREACHABLE_CODE":"YES","CLANG_WARN__DUPLICATE_METHOD_MATCH":"YES","COPY_PHASE_STRIP":"NO","DEBUG_INFORMATION_FORMAT":"dwarf-with-dsym","ENABLE_NS_ASSERTIONS":"NO","ENABLE_STRICT_OBJC_MSGSEND":"YES","GCC_C_LANGUAGE_STANDARD":"gnu99","GCC_NO_COMMON_BLOCKS":"YES","GCC_WARN_64_TO_32_BIT_CONVERSION":"YES","GCC_WARN_ABOUT_RETURN_TYPE":"YES_ERROR","GCC_WARN_UNDECLARED_SELECTOR":"YES","GCC_WARN_UNINITIALIZED_AUTOS":"YES_AGGRESSIVE","GCC_WARN_UNUSED_FUNCTION":"YES","GCC_WARN_UNUSED_VARIABLE":"YES","MTL_ENABLE_DEBUG_INFO":"NO","ONLY_ACTIVE_ARCH":"NO","SDKROOT":"macosx"},"name":"Release"},"15D4889D1BD3D59200EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"DEBUGGING_SYMBOLS":"YES","DEBUG_INFORMATION_FORMAT":"dwarf","GCC_GENERATE_DEBUGGING_SYMBOLS":"YES","GCC_OPTIMIZATION_LEVEL":"0","OTHER_CFLAGS":"","OTHER_LDFLAGS":"","PRODUCT_NAME":"$(TARGET_NAME)"},"name":"Debug"},"15D4889E1BD3D59200EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"DEBUG_INFORMATION_FORMAT":"dwarf-with-dsym","OTHER_CFLAGS":"","OTHER_LDFLAGS":"","PRODUCT_NAME":"$(TARGET_NAME)"},"name":"Release"},"15D488AE1BD3D6C700EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"PRODUCT_NAME":"$(TARGET_NAME)"},"name":"Debug"},"15D488AF1BD3D6C700EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"PRODUCT_NAME":"$(TARGET_NAME)"},"name":"Release"},"15D488CA1BD3D7A900EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"CODE_SIGN_IDENTITY":"Developer ID Application: Samantha Marshall (329DAD2G44)","COMBINE_HIDPI_IMAGES":"YES","DEPLOYMENT_LOCATION":"YES","DSTROOT":"$(HOME)","INFOPLIST_FILE":"CitrusPlugin/Info.plist","INSTALL_PATH":"/Library/Application Support/Developer/Shared/Xcode/Plug-ins","MACOSX_DEPLOYMENT_TARGET":"10.9","PRODUCT_BUNDLE_IDENTIFIER":"com.samdmarshall.CitrusPlugin","PRODUCT_NAME":"$(TARGET_NAME)","WRAPPER_EXTENSION":"xcplugin"},"name":"Debug"},"15D488CB1BD3D7A900EC46B1":{"isa":"XCBuildConfiguration","buildSettings":{"CODE_SIGN_IDENTITY":"Developer ID Application: Samantha Marshall (329DAD2G44)","COMBINE_HIDPI_IMAGES":"YES","DEPLOYMENT_LOCATION":"YES","DSTROOT":"$(HOME)","INFOPLIST_FILE":"CitrusPlugin/Info.plist","INSTALL_PATH":"/Library/Application Support/Developer/Shared/Xcode/Plug-ins","MACOSX_DEPLOYMENT_TARGET":"10.9","PRODUCT_BUNDLE_IDENTIFIER":"com.samdmarshall.CitrusPlugin","PRODUCT_NAME":"$(TARGET_NAME)","WRAPPER_EXTENSION":"xcplugin"},"name":"Release"},"15D488981BD3D59200EC46B1":{"isa":"XCConfigurationList","buildConfigurations":["15D4889A1BD3D59200EC46B1","15D4889B1BD3D59200EC46B1"],"defaultConfigurationIsVisible":"0","defaultConfigurationName":"Release"},"15D4889C1BD3D59200EC46B1":{"isa":"XCConfigurationList","buildConfigurations":["15D4889D1BD3D59200EC46B1","15D4889E1BD3D59200EC46B1"],"defaultConfigurationIsVisible":"0","defaultConfigurationName":"Release"},"15D488AD1BD3D6C700EC46B1":{"isa":"XCConfigurationList","buildConfigurations":["15D488AE1BD3D6C700EC46B1","15D488AF1BD3D6C700EC46B1"],"defaultConfigurationIsVisible":"0","defaultConfigurationName":"Release"},"15D488C91BD3D7A900EC46B1":{"isa":"XCConfigurationList","buildConfigurations":["15D488CA1BD3D7A900EC46B1","15D488CB1BD3D7A900EC46B1"],"defaultConfigurationIsVisible":"0","defaultConfigurationName":"Release"}},"rootObject":"15D488951BD3D59200EC46B1"} diff --git a/tests/fixtures/generic/plist-nextstep.plist b/tests/fixtures/generic/plist-nextstep.plist new file mode 100644 index 00000000..40c285ed --- /dev/null +++ b/tests/fixtures/generic/plist-nextstep.plist @@ -0,0 +1,508 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + 15D488AC1BD3D6C700EC46B1 /* Lemon Setup */ = { + isa = PBXAggregateTarget; + buildConfigurationList = 15D488AD1BD3D6C700EC46B1 /* Build configuration list for PBXAggregateTarget "Lemon Setup" */; + buildPhases = ( + 15D488B01BD3D6D000EC46B1 /* make distclean ; ./autogen.sh ; ./configure */, + ); + dependencies = ( + ); + name = "Lemon Setup"; + productName = "Lemon Setup"; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + 15D488BB1BD3D7A900EC46B1 /* AppKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15D488BA1BD3D7A900EC46B1 /* AppKit.framework */; }; + 15D488BD1BD3D7A900EC46B1 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 15D488BC1BD3D7A900EC46B1 /* Foundation.framework */; }; + 15D488C41BD3D7A900EC46B1 /* CitrusPlugin.m in Sources */ = {isa = PBXBuildFile; fileRef = 15D488C31BD3D7A900EC46B1 /* CitrusPlugin.m */; }; + 15D488C71BD3D7A900EC46B1 /* NSObject_Extension.m in Sources */ = {isa = PBXBuildFile; fileRef = 15D488C61BD3D7A900EC46B1 /* NSObject_Extension.m */; }; + 15D488D01BD3D9CD00EC46B1 /* Lemon.pbfilespec in Resources */ = {isa = PBXBuildFile; fileRef = 15D488CC1BD3D9CD00EC46B1 /* Lemon.pbfilespec */; }; + 15D488D11BD3D9CD00EC46B1 /* Lemon.strings in Resources */ = {isa = PBXBuildFile; fileRef = 15D488CD1BD3D9CD00EC46B1 /* Lemon.strings */; }; + 15D488D21BD3D9CD00EC46B1 /* Lemon.xcspec in Resources */ = {isa = PBXBuildFile; fileRef = 15D488CE1BD3D9CD00EC46B1 /* Lemon.xcspec */; }; + 15D488D71BD3DA8800EC46B1 /* lemon in Resources */ = {isa = PBXBuildFile; fileRef = 15D488D61BD3DA8800EC46B1 /* lemon */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 15D488B11BD3D6F400EC46B1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15D488951BD3D59200EC46B1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15D488AC1BD3D6C700EC46B1; + remoteInfo = "Lemon Setup"; + }; + 15D488D81BD3DA9C00EC46B1 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 15D488951BD3D59200EC46B1 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 15D488991BD3D59200EC46B1; + remoteInfo = Lemon; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 158E53E61BD3E6A600F75AAD /* PluginUsage.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = PluginUsage.md; sourceTree = ""; }; + 15D488A01BD3D62C00EC46B1 /* autogen.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = autogen.sh; sourceTree = ""; }; + 15D488A11BD3D62C00EC46B1 /* citrus.pc.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = citrus.pc.in; sourceTree = ""; }; + 15D488A21BD3D62C00EC46B1 /* configure.ac */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = configure.ac; sourceTree = ""; }; + 15D488A31BD3D62C00EC46B1 /* lemon.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lemon.c; sourceTree = ""; }; + 15D488A41BD3D62C00EC46B1 /* lempar.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = lempar.c; sourceTree = ""; }; + 15D488A51BD3D62C00EC46B1 /* lempar.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = lempar.h; sourceTree = ""; }; + 15D488A61BD3D62C00EC46B1 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + 15D488A71BD3D62C00EC46B1 /* Makefile.am */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = Makefile.am; sourceTree = ""; }; + 15D488A81BD3D62C00EC46B1 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 15D488A91BD3D62C00EC46B1 /* VERSION */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = VERSION; sourceTree = ""; }; + 15D488AA1BD3D62C00EC46B1 /* version.h.in */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = version.h.in; sourceTree = ""; }; + 15D488B71BD3D7A900EC46B1 /* CitrusPlugin.xcplugin */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CitrusPlugin.xcplugin; sourceTree = BUILT_PRODUCTS_DIR; }; + 15D488BA1BD3D7A900EC46B1 /* AppKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = AppKit.framework; path = /System/Library/Frameworks/AppKit.framework; sourceTree = ""; }; + 15D488BC1BD3D7A900EC46B1 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = /System/Library/Frameworks/Foundation.framework; sourceTree = ""; }; + 15D488C01BD3D7A900EC46B1 /* CitrusPlugin.xcscheme */ = {isa = PBXFileReference; lastKnownFileType = text.xml; name = CitrusPlugin.xcscheme; path = CitrusPlugin.xcodeproj/xcshareddata/xcschemes/CitrusPlugin.xcscheme; sourceTree = SOURCE_ROOT; }; + 15D488C21BD3D7A900EC46B1 /* CitrusPlugin.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = CitrusPlugin.h; sourceTree = ""; }; + 15D488C31BD3D7A900EC46B1 /* CitrusPlugin.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = CitrusPlugin.m; sourceTree = ""; }; + 15D488C51BD3D7A900EC46B1 /* NSObject_Extension.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = NSObject_Extension.h; sourceTree = ""; }; + 15D488C61BD3D7A900EC46B1 /* NSObject_Extension.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = NSObject_Extension.m; sourceTree = ""; }; + 15D488C81BD3D7A900EC46B1 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 15D488CC1BD3D9CD00EC46B1 /* Lemon.pbfilespec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.pbfilespec; path = Lemon.pbfilespec; sourceTree = ""; }; + 15D488CD1BD3D9CD00EC46B1 /* Lemon.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Lemon.strings; sourceTree = ""; }; + 15D488CE1BD3D9CD00EC46B1 /* Lemon.xcspec */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xcspec; path = Lemon.xcspec; sourceTree = ""; }; + 15D488D61BD3DA8800EC46B1 /* lemon */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.executable"; path = lemon; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 15D488B51BD3D7A900EC46B1 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 15D488BB1BD3D7A900EC46B1 /* AppKit.framework in Frameworks */, + 15D488BD1BD3D7A900EC46B1 /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 15D488941BD3D59200EC46B1 = { + isa = PBXGroup; + children = ( + 158E53E61BD3E6A600F75AAD /* PluginUsage.md */, + 15D4889F1BD3D5D500EC46B1 /* lemon */, + 15D488BE1BD3D7A900EC46B1 /* CitrusPlugin */, + 15D488B91BD3D7A900EC46B1 /* Frameworks */, + 15D488B81BD3D7A900EC46B1 /* Products */, + 15D488D61BD3DA8800EC46B1 /* lemon */, + ); + sourceTree = ""; + }; + 15D4889F1BD3D5D500EC46B1 /* lemon */ = { + isa = PBXGroup; + children = ( + 15D488A01BD3D62C00EC46B1 /* autogen.sh */, + 15D488A11BD3D62C00EC46B1 /* citrus.pc.in */, + 15D488A21BD3D62C00EC46B1 /* configure.ac */, + 15D488A31BD3D62C00EC46B1 /* lemon.c */, + 15D488A41BD3D62C00EC46B1 /* lempar.c */, + 15D488A51BD3D62C00EC46B1 /* lempar.h */, + 15D488A61BD3D62C00EC46B1 /* LICENSE */, + 15D488A71BD3D62C00EC46B1 /* Makefile.am */, + 15D488A81BD3D62C00EC46B1 /* README.md */, + 15D488A91BD3D62C00EC46B1 /* VERSION */, + 15D488AA1BD3D62C00EC46B1 /* version.h.in */, + ); + name = lemon; + sourceTree = ""; + }; + 15D488B81BD3D7A900EC46B1 /* Products */ = { + isa = PBXGroup; + children = ( + 15D488B71BD3D7A900EC46B1 /* CitrusPlugin.xcplugin */, + ); + name = Products; + sourceTree = ""; + }; + 15D488B91BD3D7A900EC46B1 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 15D488BA1BD3D7A900EC46B1 /* AppKit.framework */, + 15D488BC1BD3D7A900EC46B1 /* Foundation.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 15D488BE1BD3D7A900EC46B1 /* CitrusPlugin */ = { + isa = PBXGroup; + children = ( + 15D488D41BD3D9D000EC46B1 /* Resources */, + 15D488C21BD3D7A900EC46B1 /* CitrusPlugin.h */, + 15D488C31BD3D7A900EC46B1 /* CitrusPlugin.m */, + 15D488C51BD3D7A900EC46B1 /* NSObject_Extension.h */, + 15D488C61BD3D7A900EC46B1 /* NSObject_Extension.m */, + 15D488C81BD3D7A900EC46B1 /* Info.plist */, + 15D488BF1BD3D7A900EC46B1 /* Supporting Files */, + ); + path = CitrusPlugin; + sourceTree = ""; + }; + 15D488BF1BD3D7A900EC46B1 /* Supporting Files */ = { + isa = PBXGroup; + children = ( + 15D488C01BD3D7A900EC46B1 /* CitrusPlugin.xcscheme */, + ); + name = "Supporting Files"; + sourceTree = ""; + }; + 15D488D41BD3D9D000EC46B1 /* Resources */ = { + isa = PBXGroup; + children = ( + 15D488CC1BD3D9CD00EC46B1 /* Lemon.pbfilespec */, + 15D488CD1BD3D9CD00EC46B1 /* Lemon.strings */, + 15D488CE1BD3D9CD00EC46B1 /* Lemon.xcspec */, + ); + name = Resources; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXLegacyTarget section */ + 15D488991BD3D59200EC46B1 /* Lemon */ = { + isa = PBXLegacyTarget; + buildArgumentsString = "${ACTION}"; + buildConfigurationList = 15D4889C1BD3D59200EC46B1 /* Build configuration list for PBXLegacyTarget "Lemon" */; + buildPhases = ( + ); + buildToolPath = /usr/bin/make; + buildWorkingDirectory = "$(PROJECT_DIR)"; + dependencies = ( + 15D488B21BD3D6F400EC46B1 /* PBXTargetDependency */, + ); + name = Lemon; + passBuildSettingsInEnvironment = 1; + productName = CitrusPlugin; + }; +/* End PBXLegacyTarget section */ + +/* Begin PBXNativeTarget section */ + 15D488B61BD3D7A900EC46B1 /* CitrusPlugin */ = { + isa = PBXNativeTarget; + buildConfigurationList = 15D488C91BD3D7A900EC46B1 /* Build configuration list for PBXNativeTarget "CitrusPlugin" */; + buildPhases = ( + 15D488B31BD3D7A900EC46B1 /* Sources */, + 15D488B41BD3D7A900EC46B1 /* Resources */, + 15D488B51BD3D7A900EC46B1 /* Frameworks */, + 15D488DC1BD3DBBB00EC46B1 /* Copying default template into bundle because Xcode has a bug */, + ); + buildRules = ( + ); + dependencies = ( + 15D488D91BD3DA9C00EC46B1 /* PBXTargetDependency */, + ); + name = CitrusPlugin; + productName = CitrusPlugin; + productReference = 15D488B71BD3D7A900EC46B1 /* CitrusPlugin.xcplugin */; + productType = "com.apple.product-type.bundle"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 15D488951BD3D59200EC46B1 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 0700; + ORGANIZATIONNAME = "Samantha Marshall"; + TargetAttributes = { + 15D488991BD3D59200EC46B1 = { + CreatedOnToolsVersion = 7.0.1; + }; + 15D488AC1BD3D6C700EC46B1 = { + CreatedOnToolsVersion = 7.0.1; + }; + 15D488B61BD3D7A900EC46B1 = { + CreatedOnToolsVersion = 7.0.1; + }; + }; + }; + buildConfigurationList = 15D488981BD3D59200EC46B1 /* Build configuration list for PBXProject "CitrusPlugin" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = English; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = 15D488941BD3D59200EC46B1; + productRefGroup = 15D488B81BD3D7A900EC46B1 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 15D488AC1BD3D6C700EC46B1 /* Lemon Setup */, + 15D488991BD3D59200EC46B1 /* Lemon */, + 15D488B61BD3D7A900EC46B1 /* CitrusPlugin */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 15D488B41BD3D7A900EC46B1 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15D488D71BD3DA8800EC46B1 /* lemon in Resources */, + 15D488D01BD3D9CD00EC46B1 /* Lemon.pbfilespec in Resources */, + 15D488D11BD3D9CD00EC46B1 /* Lemon.strings in Resources */, + 15D488D21BD3D9CD00EC46B1 /* Lemon.xcspec in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 15D488B01BD3D6D000EC46B1 /* make distclean ; ./autogen.sh ; ./configure */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "make distclean ; ./autogen.sh ; ./configure"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd ${PROJECT_DIR}\n\nif [ -e Makefile ]; then\n\tmake distclean\nfi\n\n./autogen.sh\n./configure"; + }; + 15D488DC1BD3DBBB00EC46B1 /* Copying default template into bundle because Xcode has a bug */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "Copying default template into bundle because Xcode has a bug"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cp ${PROJECT_DIR}/lempar.c ${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.${WRAPPER_EXTENSION}/Contents/Resources\ncp ${PROJECT_DIR}/lempar.h ${CONFIGURATION_BUILD_DIR}/${PRODUCT_NAME}.${WRAPPER_EXTENSION}/Contents/Resources"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 15D488B31BD3D7A900EC46B1 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 15D488C41BD3D7A900EC46B1 /* CitrusPlugin.m in Sources */, + 15D488C71BD3D7A900EC46B1 /* NSObject_Extension.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 15D488B21BD3D6F400EC46B1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15D488AC1BD3D6C700EC46B1 /* Lemon Setup */; + targetProxy = 15D488B11BD3D6F400EC46B1 /* PBXContainerItemProxy */; + }; + 15D488D91BD3DA9C00EC46B1 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 15D488991BD3D59200EC46B1 /* Lemon */; + targetProxy = 15D488D81BD3DA9C00EC46B1 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 15D4889A1BD3D59200EC46B1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = YES; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = macosx; + }; + name = Debug; + }; + 15D4889B1BD3D59200EC46B1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + MTL_ENABLE_DEBUG_INFO = NO; + ONLY_ACTIVE_ARCH = NO; + SDKROOT = macosx; + }; + name = Release; + }; + 15D4889D1BD3D59200EC46B1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUGGING_SYMBOLS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + GCC_GENERATE_DEBUGGING_SYMBOLS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 15D4889E1BD3D59200EC46B1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + OTHER_CFLAGS = ""; + OTHER_LDFLAGS = ""; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 15D488AE1BD3D6C700EC46B1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Debug; + }; + 15D488AF1BD3D6C700EC46B1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + 15D488CA1BD3D7A900EC46B1 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "Developer ID Application: Samantha Marshall (329DAD2G44)"; + COMBINE_HIDPI_IMAGES = YES; + DEPLOYMENT_LOCATION = YES; + DSTROOT = "$(HOME)"; + INFOPLIST_FILE = CitrusPlugin/Info.plist; + INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = com.samdmarshall.CitrusPlugin; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = xcplugin; + }; + name = Debug; + }; + 15D488CB1BD3D7A900EC46B1 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CODE_SIGN_IDENTITY = "Developer ID Application: Samantha Marshall (329DAD2G44)"; + COMBINE_HIDPI_IMAGES = YES; + DEPLOYMENT_LOCATION = YES; + DSTROOT = "$(HOME)"; + INFOPLIST_FILE = CitrusPlugin/Info.plist; + INSTALL_PATH = "/Library/Application Support/Developer/Shared/Xcode/Plug-ins"; + MACOSX_DEPLOYMENT_TARGET = 10.9; + PRODUCT_BUNDLE_IDENTIFIER = com.samdmarshall.CitrusPlugin; + PRODUCT_NAME = "$(TARGET_NAME)"; + WRAPPER_EXTENSION = xcplugin; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 15D488981BD3D59200EC46B1 /* Build configuration list for PBXProject "CitrusPlugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15D4889A1BD3D59200EC46B1 /* Debug */, + 15D4889B1BD3D59200EC46B1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15D4889C1BD3D59200EC46B1 /* Build configuration list for PBXLegacyTarget "Lemon" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15D4889D1BD3D59200EC46B1 /* Debug */, + 15D4889E1BD3D59200EC46B1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15D488AD1BD3D6C700EC46B1 /* Build configuration list for PBXAggregateTarget "Lemon Setup" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15D488AE1BD3D6C700EC46B1 /* Debug */, + 15D488AF1BD3D6C700EC46B1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 15D488C91BD3D7A900EC46B1 /* Build configuration list for PBXNativeTarget "CitrusPlugin" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 15D488CA1BD3D7A900EC46B1 /* Debug */, + 15D488CB1BD3D7A900EC46B1 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = 15D488951BD3D59200EC46B1 /* Project object */; +} diff --git a/tests/fixtures/generic/plist-nextstep2.json b/tests/fixtures/generic/plist-nextstep2.json new file mode 100644 index 00000000..8cbfd5f9 --- /dev/null +++ b/tests/fixtures/generic/plist-nextstep2.json @@ -0,0 +1 @@ +{"key1":"Value1","key2":"value3","DateOfStateChange":"2022-12-18 05:51:30 +0000","Destination":"F134545","Progress":{"Percent":"0.4563777755358589"}} diff --git a/tests/fixtures/generic/plist-nextstep2.plist b/tests/fixtures/generic/plist-nextstep2.plist new file mode 100644 index 00000000..8708ff6c --- /dev/null +++ b/tests/fixtures/generic/plist-nextstep2.plist @@ -0,0 +1,9 @@ +{ + key1 = Value1; + key2 = "value3"; + DateOfStateChange = "2022-12-18 05:51:30 +0000"; + Destination = "F134545"; + Progress = { + Percent = "0.4563777755358589"; + }; +} diff --git a/tests/test_plist.py b/tests/test_plist.py index ce38856e..99831798 100644 --- a/tests/test_plist.py +++ b/tests/test_plist.py @@ -21,6 +21,12 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/plist-alltypes-bin.plist'), 'rb') as f: generic_alltypes_bin = f.read() + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/plist-nextstep.plist'), 'rb') as f: + nextstep = f.read() + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/plist-nextstep2.plist'), 'rb') as f: + nextstep2 = f.read() + # output with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/plist-garageband-info.json'), 'r', encoding='utf-8') as f: generic_garageband_json = json.loads(f.read()) @@ -34,6 +40,12 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/plist-alltypes-bin.json'), 'r', encoding='utf-8') as f: generic_alltypes_bin_json = json.loads(f.read()) + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/plist-nextstep.json'), 'r', encoding='utf-8') as f: + nextstep_json = json.loads(f.read()) + + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/plist-nextstep2.json'), 'r', encoding='utf-8') as f: + nextstep2_json = json.loads(f.read()) + def test_plist_nodata(self): """ @@ -70,5 +82,18 @@ class MyTests(unittest.TestCase): self.assertEqual(jc.parsers.plist.parse(self.generic_alltypes_bin, quiet=True), self.generic_alltypes_bin_json) + def test_plist_nextstep(self): + """ + Test NeXTSTEP style plist file + """ + self.assertEqual(jc.parsers.plist.parse(self.nextstep, quiet=True), self.nextstep_json) + + def test_plist_nextstep2(self): + """ + Test NeXTSTEP style plist file simple + """ + self.assertEqual(jc.parsers.plist.parse(self.nextstep2, quiet=True), self.nextstep2_json) + + if __name__ == '__main__': unittest.main() From 5e32a6d828285542f9ec3a6bc4d9401824865e0f Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 10:37:12 -0800 Subject: [PATCH 23/31] fix for python 3.10+ compatibility --- jc/parsers/pbPlist/pbRoot.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/jc/parsers/pbPlist/pbRoot.py b/jc/parsers/pbPlist/pbRoot.py index 972a7d86..8f314b6f 100644 --- a/jc/parsers/pbPlist/pbRoot.py +++ b/jc/parsers/pbPlist/pbRoot.py @@ -28,7 +28,14 @@ # OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED # OF THE POSSIBILITY OF SUCH DAMAGE. +import sys from functools import cmp_to_key + +# for python 3.10+ compatibility +if sys.version_info.major == 3 and sys.version_info.minor >= 10: + import collections + setattr(collections, "MutableMapping", collections.abc.MutableMapping) + import collections from . import pbItem From e49c621e59e354dbe17160ea36ee190b44400fcb Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 10:44:46 -0800 Subject: [PATCH 24/31] try writing to file as bytes for windows compatibility --- jc/parsers/plist.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index f39eb8b9..83f203aa 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -166,8 +166,7 @@ def parse( # Try parsing as an old-style NeXTSTEP Plist format # pbPlist library only works on file paths, not strings :( import tempfile - with tempfile.NamedTemporaryFile(mode='w+') as plist_file: - data = data.decode() + with tempfile.NamedTemporaryFile(mode='w+b') as plist_file: plist_file.write(data) plist_file.seek(0) parsed_plist = PBPlist(plist_file.name) From 47d8e163de229d927f62f839613c3b9e51d4cda5 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 11:06:45 -0800 Subject: [PATCH 25/31] patch for windows compatibility: can only open tempfile once --- jc/parsers/pbPlist/pbPlist.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/jc/parsers/pbPlist/pbPlist.py b/jc/parsers/pbPlist/pbPlist.py index 0239631f..65293465 100644 --- a/jc/parsers/pbPlist/pbPlist.py +++ b/jc/parsers/pbPlist/pbPlist.py @@ -36,11 +36,13 @@ class PBPlist(object): def __init__(self, file_path): self.root = None - if self.__checkFile(file_path) is True: - parser = PBParser(self.file_path) - self.root = parser.read() - self.string_encoding = parser.string_encoding - self.file_type = parser.file_type + # don't use __checkFile since we can only open the file once in Windows + # if self.__checkFile(file_path) is True: + self.file_path = file_path + parser = PBParser(self.file_path) + self.root = parser.read() + self.string_encoding = parser.string_encoding + self.file_type = parser.file_type def write(self, file_path=None): if file_path is None: From 177d10577ffb499d26a91566320a3a12c8b0d9b5 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 11:17:52 -0800 Subject: [PATCH 26/31] create NamedTemporaryFile with delete=False for Windows compatibilty --- jc/parsers/pbPlist/pbPlist.py | 13 ++++++------- jc/parsers/plist.py | 2 +- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/jc/parsers/pbPlist/pbPlist.py b/jc/parsers/pbPlist/pbPlist.py index 65293465..a503c136 100644 --- a/jc/parsers/pbPlist/pbPlist.py +++ b/jc/parsers/pbPlist/pbPlist.py @@ -36,13 +36,12 @@ class PBPlist(object): def __init__(self, file_path): self.root = None - # don't use __checkFile since we can only open the file once in Windows - # if self.__checkFile(file_path) is True: - self.file_path = file_path - parser = PBParser(self.file_path) - self.root = parser.read() - self.string_encoding = parser.string_encoding - self.file_type = parser.file_type + if self.__checkFile(file_path) is True: + self.file_path = file_path + parser = PBParser(self.file_path) + self.root = parser.read() + self.string_encoding = parser.string_encoding + self.file_type = parser.file_type def write(self, file_path=None): if file_path is None: diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index 83f203aa..a7858ccb 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -166,7 +166,7 @@ def parse( # Try parsing as an old-style NeXTSTEP Plist format # pbPlist library only works on file paths, not strings :( import tempfile - with tempfile.NamedTemporaryFile(mode='w+b') as plist_file: + with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as plist_file: plist_file.write(data) plist_file.seek(0) parsed_plist = PBPlist(plist_file.name) From d43863ee9fc39908999e6a66732bd0d58770de14 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 11:32:14 -0800 Subject: [PATCH 27/31] delete the tempfile after use --- jc/parsers/plist.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index a7858ccb..a4048cdc 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -166,12 +166,20 @@ def parse( # Try parsing as an old-style NeXTSTEP Plist format # pbPlist library only works on file paths, not strings :( import tempfile + import os + + # use delete=False for windows compatibility with tempfile.NamedTemporaryFile(mode='w+b', delete=False) as plist_file: + plist_file_name = plist_file.name plist_file.write(data) plist_file.seek(0) - parsed_plist = PBPlist(plist_file.name) + parsed_plist = PBPlist(plist_file_name) raw_output = parsed_plist.root.nativeType() + # try to delete the temp file + if os.path.exists(plist_file_name): + os.remove(plist_file_name) + raw_output = _fix_objects(raw_output) return raw_output if raw else _process(raw_output) From b6b42d90715207964d26b1a9db40a1e9a206f09a Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 11:46:40 -0800 Subject: [PATCH 28/31] doc update --- docs/parsers/plist.md | 2 +- jc/parsers/plist.py | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/parsers/plist.md b/docs/parsers/plist.md index d8515e27..8481642f 100644 --- a/docs/parsers/plist.md +++ b/docs/parsers/plist.md @@ -69,7 +69,7 @@ Parameters: Returns: - List of Dictionaries. Raw or processed structured data. + Dictionary. Raw or processed structured data. ### Parser Information Compatibility: linux, darwin, cygwin, win32, aix, freebsd diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index a4048cdc..90c89a64 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -72,11 +72,11 @@ def _process(proc_data: Dict) -> Dict: Parameters: - proc_data: (List of Dictionaries) raw structured data to process + proc_data: (Dictionary) raw structured data to process Returns: - List of Dictionaries. Structured to conform to the schema. + Dictionary. Structured to conform to the schema. """ return proc_data @@ -148,7 +148,7 @@ def parse( Returns: - List of Dictionaries. Raw or processed structured data. + Dictionary. Raw or processed structured data. """ jc.utils.compatibility(__name__, info.compatible, quiet) From 048f8c1f4b5832298dcd370e9bc05a7f6369f28d Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 11:49:20 -0800 Subject: [PATCH 29/31] formatting --- jc/parsers/plist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index 90c89a64..9157f04e 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -117,7 +117,7 @@ def _fix_objects(obj): continue if isinstance(v, list): - newlist =[] + newlist = [] for i in v: newlist.append(_fix_objects(i)) obj.update({k: newlist}) From cf11c718319e2cd517236faea7769296eb41a50c Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 11:53:09 -0800 Subject: [PATCH 30/31] move import to except block --- jc/parsers/plist.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/jc/parsers/plist.py b/jc/parsers/plist.py index 9157f04e..c1ec872a 100644 --- a/jc/parsers/plist.py +++ b/jc/parsers/plist.py @@ -48,7 +48,6 @@ from typing import Dict, Union import plistlib import binascii from datetime import datetime -from jc.parsers.pbPlist.pbPlist import PBPlist import jc.utils @@ -165,6 +164,7 @@ def parse( except Exception: # Try parsing as an old-style NeXTSTEP Plist format # pbPlist library only works on file paths, not strings :( + from jc.parsers.pbPlist.pbPlist import PBPlist import tempfile import os From 63161ffdbb8a353f9a3bffa433b04a9770a50676 Mon Sep 17 00:00:00 2001 From: Kelly Brazil Date: Fri, 30 Dec 2022 13:11:54 -0800 Subject: [PATCH 31/31] doc update --- CHANGELOG | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG b/CHANGELOG index c8572ba6..fe4f758c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ jc changelog -20221229 v1.22.4 +20221230 v1.22.4 - Add `iwconfig` command parser - Add NeXTSTEP format support to the PLIST file parser - Fix `proc` parser magic signature detection for `/proc/pid/stat` hacks