Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Change plain text form to YAML #32

Merged
merged 1 commit into from
May 19, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading