Skip to content

Commit

Permalink
Merge pull request #32 from Matmaus/yaml-print
Browse files Browse the repository at this point in the history
Change plain text form to YAML
  • Loading branch information
Matmaus committed May 19, 2024
2 parents caef2ed + 9415746 commit 55052de
Show file tree
Hide file tree
Showing 6 changed files with 270 additions and 429 deletions.
248 changes: 53 additions & 195 deletions LnkParse3/lnk_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@
import datetime
import argparse
from subprocess import list2cmdline
import re
import textwrap

import yaml

from LnkParse3.lnk_header import LnkHeader
from LnkParse3.lnk_targets import LnkTargets
Expand Down Expand Up @@ -88,208 +92,61 @@ def process(self):
self.extras = ExtraData(indata=self.indata[index:], cp=self.cp)

def print_lnk_file(self, print_all=False):
def cprint(text, level=0):
SPACING = 3
UNWANTED_TRAITS = ["offset", "reserved", "size"]
text = str(text)
if print_all or all(x not in text.lower() for x in UNWANTED_TRAITS):
print(" " * (level * SPACING) + text) # add leading spaces

def nice_id(identifier):
res = self.get_json(print_all)

def nice_id(identifier, uppercase=False):
identifier = re.sub("^r_", "", identifier, 1)
if uppercase or identifier.upper() == identifier:
return identifier.upper().replace("_", " ")
return identifier.capitalize().replace("_", " ")

# TODO recursive nice print
cprint("Windows Shortcut Information:")
cprint("Header Size: %s" % self.header.size(), 1)
cprint("Link CLSID: %s" % self.header.link_cls_id(), 1)
cprint(
"Link Flags: %s - (%s)"
% (self.format_linkFlags(), self.header.r_link_flags()),
1,
)
cprint(
"File Flags: %s - (%s)"
% (self.format_fileFlags(), self.header.r_file_flags()),
1,
def make_keys_nice(input, uppercase=False):
if isinstance(input, list):
return [make_keys_nice(item) for item in input]
if isinstance(input, dict):
if "class" in input:
key = input.pop("class")
return {key: make_keys_nice(input)}
result = {}
for key, value in input.items():
result[nice_id(key, uppercase)] = make_keys_nice(value)
return result
return input

# remove r_hotkey from header and reformat flags
res["header"].pop("r_hotkey")
res["header"]["link_flags"] = self.format_linkFlags()
res["header"]["file_flags"] = self.format_fileFlags()

res_json = make_keys_nice(res, uppercase=True)

# insert placeholders for empty lines
res_json = {"EMPTY_LINE_PLACEHOLDER" + k: v for k, v in res_json.items()}

# remove header key
new_res_json = res_json["EMPTY_LINE_PLACEHOLDERHEADER"]
res_json.pop("EMPTY_LINE_PLACEHOLDERHEADER")
new_res_json.update(res_json)

res_yaml = yaml.dump(
new_res_json, indent=3, sort_keys=False, width=132, allow_unicode=True
)
cprint("")
cprint("Creation Timestamp: %s" % (self.header.creation_time()), 1)
cprint("Modified Timestamp: %s" % (self.header.write_time()), 1)
cprint("Accessed Timestamp: %s" % (self.header.access_time()), 1)
cprint("")
cprint(
"File Size: %s (r: %s)"
% (str(self.header.file_size()), str(len(self.indata))),
1,
)
cprint("Icon Index: %s " % (str(self.header.icon_index())), 1)
cprint("Window Style: %s " % (str(self.header.window_style())), 1)
cprint("HotKey: %s " % (str(self.header.hot_key())), 1)
cprint("Reserved0: %s" % self.header.reserved0(), 1)
cprint("Reserved1: %s" % self.header.reserved1(), 1)
cprint("Reserved2: %s" % self.header.reserved2(), 1)
cprint("")

if self.targets:
cprint("TARGETS:", 1)
cprint("Size: %s" % self.targets.id_list_size(), 2)
cprint("Index: %s" % self._target_index, 2)
cprint("ITEMS:", 2)
for target in self.targets.as_list():
cprint(target["class"], 3)
for key, value in target.items():
if key != "class":
cprint(f"{nice_id(key)}: {value}", 4)
cprint("")
# replace palceholders for empty lines
res_yaml = res_yaml.replace("EMPTY_LINE_PLACEHOLDER", "\n")

if self.info:
cprint("LINK INFO:", 1)
cprint("Link info size: %s" % self.info.size(), 2)
cprint("Link info header size: %s" % self.info.header_size(), 2)
cprint("Link info flags: %s" % self.info.flags(), 2)
cprint("Volume ID offset: %s" % self.info.volume_id_offset(), 2)
cprint("Local base path offset: %s" % self.info.local_base_path_offset(), 2)
cprint(
"Common network relative link offset: %s"
% self.info.common_network_relative_link_offset(),
2,
)
cprint(
"Common path suffix offset: %s" % self.info.common_path_suffix_offset(),
2,
)
if self.info.local_base_path_offset():
cprint("Local base path: %s" % self.info.local_base_path(), 2)
if self.info.common_path_suffix_offset():
cprint("Common path suffix: %s" % self.info.common_path_suffix(), 2)
if self.info.local_base_path_offset_unicode():
cprint(
"Local base path offset unicode: %s"
% self.info.local_base_path_offset_unicode(),
2,
)
cprint(
"Local base unicode: %s" % self.info.local_base_path_unicode(), 2
)
if self.info.common_path_suffix_offset_unicode():
cprint(
"Common path suffix offset unicode: %s"
% self.info.common_path_suffix_offset_unicode(),
2,
)
cprint(
"Common path suffix unicode: %s"
% self.info.common_path_suffix_unicode(),
2,
)
if type(self.info).__name__ == "Local":
cprint("LOCAL:", 2)
cprint("Volume ID size: %s" % self.info.volume_id_size(), 3)
cprint("Drive type: %s" % self.info.r_drive_type(), 3)
cprint("Volume label offset: %s" % self.info.volume_label_offset(), 3)
cprint("Drive serial number: %s" % self.info.drive_serial_number(), 3)
cprint("Drive type: %s" % self.info.drive_type(), 3)
cprint("Volume label: %s" % self.info.volume_label(), 3)
if self.info.common_network_relative_link():
cprint(
"Common network relative link: %s"
% self.info.common_network_relative_link(),
3,
)
if self.info.volume_label_unicode_offset():
cprint(
"Volume label unicode offset: %s"
% self.info.volume_label_unicode_offset(),
3,
)
cprint(
"Volume label unicode: %s" % self.info.volume_label_unicode(), 3
)
elif type(self.info).__name__ == "Network":
cprint(
"Common network relative link size: %s"
% self.info.common_network_relative_link_size(),
3,
)
cprint(
"Common network relative link flags: %s"
% self.info.common_network_relative_link_flags(),
3,
)
cprint("Net name offset: %s" % self.info.net_name_offset(), 3)
cprint("Device name offset: %s" % self.info.device_name_offset(), 3)
cprint(
"Network provider type: %s" % self.info.r_network_provider_type(), 3
)
if self.info.network_provider_type():
cprint(
"Network provider type: %s" % self.info.network_provider_type(),
3,
)
if self.info.net_name_offset_unicode():
cprint(
"Net name offset unicode: %s"
% self.info.net_name_offset_unicode(),
3,
)
cprint("Net name unicode: %s" % self.info.net_name_unicode(), 3)
if self.info.device_name_offset_unicode():
cprint(
"Device name offset unicode: %s"
% self.info.device_name_offset_unicode(),
3,
)
cprint(
"Device name unicode: %s" % self.info.device_name_unicode(), 3
)
if self.info.net_name():
cprint("Net name: %s" % self.info.net_name(), 3)
if self.info.device_name():
cprint("Device name: %s" % self.info.device_name(), 3)
cprint("")

cprint("DATA", 1)
for key, value in self.string_data.as_dict().items():
cprint("%s: %s" % (nice_id(key), value), 2)
cprint("")

cprint("EXTRA BLOCKS:", 1)
for extra_key, extra_value in self.extras.as_dict().items():
cprint(f"{extra_key}", 2)
if extra_key == "UNKNOWN_BLOCK":
for list_value in extra_value:
cprint("Block:", 3)
for key, value in list_value.items():
cprint(f"{nice_id(key)}: {value}", 4)
else:
for key, value in extra_value.items():
if extra_key == "METADATA_PROPERTIES_BLOCK" and isinstance(
value, list
):
cprint(f"{nice_id(key)}:", 3)
for storage in value:
cprint("Storage:", 4)
for storage_key, storage_value in storage.items():
if isinstance(storage_value, list):
cprint(f"{nice_id(storage_key)}:", 5)
for item in storage_value:
cprint("Property:", 6)
for item_key, item_value in item.items():
cprint(
f"{nice_id(item_key)}: {item_value}", 7
)
else:
cprint(
f"{nice_id(storage_key)}: {storage_value}", 5
)
else:
cprint(f"{nice_id(key)}: {value}", 3)
print("Windows Shortcut Information:")
print(textwrap.indent(res_yaml, " "))

def format_linkFlags(self):
return " | ".join(self.header.link_flags())
raw_flags = self.header.r_link_flags()
suffix = f" - ({raw_flags})" if raw_flags else f"({raw_flags})"
return " | ".join(self.header.link_flags()) + suffix

def format_fileFlags(self):
return " | ".join(self.header.file_flags())
raw_flags = self.header.r_file_flags()
suffix = f" - ({raw_flags})" if raw_flags else f"({raw_flags})"
return " | ".join(self.header.file_flags()) + suffix

# FIXME: Simple concat of path and arguments
@property
Expand Down Expand Up @@ -352,8 +209,6 @@ def get_json(self, get_all=False):
"reserved1": self.header.reserved1(),
"reserved2": self.header.reserved2(),
},
"data": self.string_data.as_dict(),
"extra": self.extras.as_dict(),
}

if self.targets:
Expand Down Expand Up @@ -452,6 +307,9 @@ def get_json(self, get_all=False):
"device_name"
] = self.info.device_name()

res["data"] = self.string_data.as_dict()
res["extra"] = self.extras.as_dict()

if not get_all:
res["header"].pop("header_size", None)
res["header"].pop("reserved0", None)
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
author_email='[email protected]',
license='MIT',
packages=find_packages(exclude=["tests*"]),
install_requires=['pyyaml'],
classifiers=[
"Programming Language :: Python :: 3",
"License :: OSI Approved :: MIT License",
Expand Down
Loading

0 comments on commit 55052de

Please sign in to comment.