diff --git a/CHANGELOG b/CHANGELOG index 6c71ee95..fe4f758c 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,15 @@ jc changelog +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 +- Fix `x509-cert` parser for string serial numbers +- 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 + 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..a2b0b1c1 100644 --- a/README.md +++ b/README.md @@ -206,10 +206,11 @@ 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) | -| ` --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_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..648fb659 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,10 +156,11 @@ _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' - '--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' @@ -289,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' @@ -321,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/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/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/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/docs/parsers/plist.md b/docs/parsers/plist.md index 14b34c78..8481642f 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. @@ -69,9 +69,9 @@ 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 -Version 1.0 by Kelly Brazil (kellyjonbrazil@gmail.com) +Version 1.1 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/docs/parsers/x509_cert.md b/docs/parsers/x509_cert.md index e5208d85..0b593a79 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": { @@ -428,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/cli.py b/jc/cli.py index 8525f6dd..19f039a1 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 = 22 + + 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..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'], @@ -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 tags ''' \ 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/lib.py b/jc/lib.py index ab0a544c..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', @@ -65,6 +65,7 @@ parsers: List[str] = [ 'iptables', 'iso-datetime', 'iw-scan', + 'iwconfig', 'jar-manifest', 'jobs', 'jwt', 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 3b32344a..4d18c4f1 100644 --- a/jc/parsers/findmnt.py +++ b/jc/parsers/findmnt.py @@ -93,12 +93,13 @@ 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' compatible = ['linux'] magic_commands = ['findmnt'] + tags = ['command'] __version__ = info.version @@ -140,7 +141,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 + '/' 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 1daa3f42..f8bc9503 100644 --- a/jc/parsers/ifconfig.py +++ b/jc/parsers/ifconfig.py @@ -219,12 +219,13 @@ 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' compatible = ['linux', 'aix', 'freebsd', 'darwin'] magic_commands = ['ifconfig'] + tags = ['command'] __version__ = info.version @@ -326,7 +327,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: 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 new file mode 100644 index 00000000..aa73d43f --- /dev/null +++ b/jc/parsers/iwconfig.py @@ -0,0 +1,204 @@ +"""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 + } + ] + +""" +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'] + tags = ['command'] + + +__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 = [ + '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, + 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] = [] + + 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 + ] + + 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 is not None: + raw_output.append(interface_item) + + 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 + + # 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 is not None: + raw_output.append(interface_item) + + return raw_output if raw else _process(raw_output) 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/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..a503c136 --- /dev/null +++ b/jc/parsers/pbPlist/pbPlist.py @@ -0,0 +1,56 @@ +# 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: + 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: + 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..8f314b6f --- /dev/null +++ b/jc/parsers/pbPlist/pbRoot.py @@ -0,0 +1,117 @@ +# 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 +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 + +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/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..c1ec872a 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. @@ -53,11 +53,13 @@ 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'] __version__ = info.version @@ -69,11 +71,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 @@ -114,7 +116,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}) @@ -145,7 +147,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) @@ -156,7 +158,28 @@ 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 :( + from jc.parsers.pbPlist.pbPlist import PBPlist + 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) + 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) 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 147a201c..2330c6ab 100644 --- a/jc/parsers/proc.py +++ b/jc/parsers/proc.py @@ -120,11 +120,12 @@ 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' compatible = ['linux'] + tags = ['file'] __version__ = info.version @@ -201,7 +202,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/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..e8932539 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": { @@ -408,12 +413,13 @@ 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' 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 @@ -465,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': @@ -496,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}) 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 diff --git a/man/jc.1 b/man/jc.1 index 9c65cd60..38a59f03 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-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 @@ -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 @@ -313,7 +318,7 @@ JWT string parser .TP .B \fB--kv\fP -Key/Value file parser +Key/Value file and string parser .TP .B 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.', 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/fixtures/generic/iwconfig-raw.json b/tests/fixtures/generic/iwconfig-raw.json new file mode 100644 index 00000000..6f824ed2 --- /dev/null +++ b/tests/fixtures/generic/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/generic/iwconfig.json b/tests/fixtures/generic/iwconfig.json new file mode 100644 index 00000000..511884e3 --- /dev/null +++ b/tests/fixtures/generic/iwconfig.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.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 + } +] \ No newline at end of file diff --git a/tests/fixtures/generic/iwconfig.out b/tests/fixtures/generic/iwconfig.out new file mode 100644 index 00000000..eaec4add --- /dev/null +++ b/tests/fixtures/generic/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/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/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 00000000..77bec2ee Binary files /dev/null and b/tests/fixtures/generic/x509-string-serialnumber.der differ diff --git a/tests/fixtures/generic/x509-string-serialnumber.json b/tests/fixtures/generic/x509-string-serialnumber.json new file mode 100644 index 00000000..8eaee387 --- /dev/null +++ b/tests/fixtures/generic/x509-string-serialnumber.json @@ -0,0 +1 @@ +[{"tbs_certificate":{"version":"v3","serial_number":"69:dd:cd:f1:b6:b8:0b:83:cc:f3:9a:44:84:a8:02:54:42:9d:ef:c1","signature":{"algorithm":"sha256_rsa","parameters":null},"issuer":{"country_name":"US","state_or_province_name":"California","locality_name":"San Francisco","organization_name":"My Company","common_name":"mysite.com","serial_number":"53:4e:2d:31:32:33:34","serial_number_str":"SN-1234"},"validity":{"not_before":1672253909,"not_after":1758653909,"not_before_iso":"2022-12-28T18:58:29+00:00","not_after_iso":"2025-09-23T18:58:29+00:00"},"subject":{"country_name":"US","state_or_province_name":"California","locality_name":"San Francisco","organization_name":"My Company","common_name":"mysite.com","serial_number":"53:4e:2d:31:32:33:34","serial_number_str":"SN-1234"},"subject_public_key_info":{"algorithm":{"algorithm":"rsa","parameters":null},"public_key":{"modulus":"b2:50:06:d8:1a:3a:5c:45:d0:8e:ae:fa:f4:09:c1:d5:2c:42:11:1a:4a:a0:fd:e5:25:e9:2b:e1:84:2d:7b:49:97:36:33:d5:fd:d1:dd:3e:d9:f7:3d:1f:de:79:be:cd:8f:d1:c7:75:63:24:6e:e2:f5:95:e4:63:65:86:38:2f","public_exponent":65537}},"issuer_unique_id":null,"subject_unique_id":null,"extensions":[{"extn_id":"subject_alt_name","critical":false,"extn_value":["localhost"]}],"serial_number_str":"604390435894919967959141219654963640512005664705"},"signature_algorithm":{"algorithm":"sha256_rsa","parameters":null},"signature_value":"36:a7:02:13:e1:2c:76:74:3b:f1:d4:26:5b:84:f8:1b:25:f2:e1:ef:86:fc:af:c2:86:82:2a:0b:fc:36:e7:f5:2c:91:6d:86:ee:9a:e3:b9:da:ea:d0:62:97:ee:17:03:38:3d:0a:91:0e:3b:b6:2f:2f:08:da:09:dd:25:40:5b"}] 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_iwconfig.py b/tests/test_iwconfig.py new file mode 100644 index 00000000..b3b9fee0 --- /dev/null +++ b/tests/test_iwconfig.py @@ -0,0 +1,54 @@ +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/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()) + + 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): + """ + Test 'iwconfig' with no data + """ + self.assertEqual(jc.parsers.iwconfig.parse('', quiet=True), []) + + def test_iwconfig_raw(self): + """ + Test 'iwconfig' raw + """ + self.assertEqual(jc.parsers.iwconfig.parse(self.iwconfig_output, quiet=True, raw=True), self.iwconfig_raw_json) + + def test_iwconfig(self): + """ + Test 'iwconfig' + """ + 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() 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 = { 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() 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() diff --git a/tests/test_x509_cert.py b/tests/test_x509_cert.py index 4286e9de..0695015f 100644 --- a/tests/test_x509_cert.py +++ b/tests/test_x509_cert.py @@ -21,6 +21,9 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/x509-multi-cert.pem'), 'r', encoding='utf-8') as f: x509_multi_cert = f.read() + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/x509-string-serialnumber.der'), 'rb') as f: + x509_string_serialnumber = f.read() + # output with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/x509-ca-cert.json'), 'r', encoding='utf-8') as f: x509_ca_cert_json = json.loads(f.read()) @@ -34,6 +37,9 @@ class MyTests(unittest.TestCase): with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/x509-multi-cert.json'), 'r', encoding='utf-8') as f: x509_multi_cert_json = json.loads(f.read()) + with open(os.path.join(THIS_DIR, os.pardir, 'tests/fixtures/generic/x509-string-serialnumber.json'), 'r', encoding='utf-8') as f: + x509_string_serialnumber_json = json.loads(f.read()) + def test_x509_cert_nodata(self): """ @@ -63,7 +69,13 @@ class MyTests(unittest.TestCase): """ Test 'cat x509-multi-cert.pem' (PEM file with multiple certificates) """ - self.assertEqual(jc.parsers.x509_cert.parse(self.x509_letsencrypt, quiet=True), self.x509_letsencrypt_json) + self.assertEqual(jc.parsers.x509_cert.parse(self.x509_multi_cert, quiet=True), self.x509_multi_cert_json) + + def test_x509_string_serialnumber(self): + """ + Test 'cat x509-string-serialnumber.der' (DER file with string serial numbers) + """ + self.assertEqual(jc.parsers.x509_cert.parse(self.x509_multi_cert, quiet=True), self.x509_multi_cert_json) if __name__ == '__main__':