Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
  • Loading branch information
itsnebulalol committed Sep 1, 2022
2 parents 465462d + 85f4a10 commit 445bbbc
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 51 deletions.
45 changes: 13 additions & 32 deletions permasigner/bundled/constrictor/dpkg.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,15 +23,15 @@ class DPKGBuilder(object):
Finds files to use, builds tar archive and then archives into ar format. Builds + includes debian control files.
"""

def __init__(self, output_directory, bundle, data_dirs, control, scripts):
def __init__(self, output_directory, executables, data_dirs, control, scripts):
self.output_directory = Path(output_directory).expanduser()
self.bundle = bundle
self.data_dirs = data_dirs or []
self.control = control
self.maintainer_scripts = scripts
self.executables = executables
self.seen_data_dirs = set()
self.seen_executables = set()
self.working_dir = None
self.executable_path = ''

@staticmethod
def path_matches_glob_list(glob_list, path):
Expand Down Expand Up @@ -79,32 +79,18 @@ def add_directory_root_to_archive(self, archive, file_path):

self.seen_data_dirs.add(directory)

def filter_tar_info(self, tar_info, dir_conf):
if self.executable_path == '':
app_name = self.bundle['name'].replace(' ', '')
destination = dir_conf['destination']
executable = dir_conf['executable']
executable_path = f'.{destination}/{app_name}.app/{executable}'

if tar_info.name == executable_path:
tar_info.mode = TAR_DEFAULT_MODE
tar_info.uname = 'root'
tar_info.gname = 'wheel'
self.executable_path = executable_path
def filter_tar_info(self, tar_info):
if tar_info.name in self.executables:
tar_info.mode = TAR_DEFAULT_MODE
tar_info.uname = 'root'
tar_info.gname = 'wheel'

return tar_info

@property
def data_archive_path(self):
return self.working_dir / 'data.tar.xz'

@property
def default_output_name(self):
name = self.bundle['name'].replace(' ', '')
version = self.bundle['version']

return f"{name}_{version}.deb"

@staticmethod
def open_tar_file(path):
tf = tarfile.open(path, 'w:xz')
Expand All @@ -126,7 +112,7 @@ def build_data_archive(self):
self.add_directory_root_to_archive(data_tar_file, archive_path)

data_tar_file.add(source_file_path, arcname=archive_path, recursive=False,
filter=lambda ti: self.filter_tar_info(ti, dir_conf))
filter=lambda ti: self.filter_tar_info(ti))

data_tar_file.close()

Expand Down Expand Up @@ -160,25 +146,20 @@ def build_control_archive(self, maintainer_scripts):
control_tar.close()

def assemble_deb_archive(self, control_archive_path, data_archive_path):
if not self.output_directory.exists():
self.output_directory.mkdir()
if not self.output_directory.parent.exists():
self.output_directory.parent.mkdir()

pkg_path = self.output_directory / f"{self.default_output_name}"

with open(pkg_path, 'wb') as ar_fp:
with open(self.output_directory, 'wb') as ar_fp:
ar_writer = ARWriter(ar_fp)

ar_writer.archive_text("debian-binary", f"{DEBIAN_BINARY_VERSION}\n", int(time.time()), 0, 0,
AR_DEFAULT_MODE)
ar_writer.archive_file(control_archive_path, int(time.time()), 0, 0, AR_DEFAULT_MODE)
ar_writer.archive_file(data_archive_path, int(time.time()), 0, 0, AR_DEFAULT_MODE)

return pkg_path

def build_package(self):
with tempfile.TemporaryDirectory() as tmpfolder:
self.working_dir = Path(tmpfolder)
self.build_data_archive()
self.build_control_archive(self.maintainer_scripts)

return self.assemble_deb_archive(self.control_archive_path, self.data_archive_path)
self.assemble_deb_archive(self.control_archive_path, self.data_archive_path)
40 changes: 23 additions & 17 deletions permasigner/dpkg.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import array
import subprocess
import tarfile
from pathlib import Path
Expand All @@ -10,59 +11,64 @@
class Dpkg:
def __init__(self,
bundle: dict,
executables: set,
tmpfolder: Path,
output_path: Path,
dpkg: bool,
in_package: bool,
debug: bool) -> None:
self.bundle = bundle
self.executables = executables
self.tmpfolder = tmpfolder
self.dpkg = dpkg
self.output_path = output_path
self.in_package = in_package
self.debug = debug

def package_with_constrictor(self, source, control, postinst, prerm) -> Path:
@property
def absolute_output_path(self):
name = self.bundle['name'].replace(' ', '')
version = self.bundle['version']

return self.output_path / f"{name}_{version}.deb"

def package_with_constrictor(self, source, control, postinst, prerm) -> None:
dirs = [{
'source': source,
'destination': '/Applications',
'executable': self.bundle["executable"]
}]

scripts = {
'postinst': postinst,
'prerm': prerm
}

builder = DPKGBuilder(self.output_path, self.bundle, dirs, control, scripts)
return builder.build_package()

def package_with_dpkg(self, output_path: Path, tmp: Path, debug: bool) -> Path:
# Construct output name from app name and app version
# Then create a deb package with dpkg-deb
name = self.bundle["name"].replace(' ', '')
version = self.bundle["version"]
output_path = output_path / f"{name}_{version}.deb"
dpkg_cmd = f"dpkg-deb -Zxz --root-owner-group -b {tmp}/deb {output_path}"
logger.debug(f"Running command: {dpkg_cmd}", debug)
subprocess.run(dpkg_cmd.split(), stdout=subprocess.DEVNULL)
builder = DPKGBuilder(self.absolute_output_path, self.executables, dirs, control, scripts)
builder.build_package()

return output_path
def package_with_dpkg(self) -> None:
""" Package application with dpkg-deb"""

dpkg_cmd = f"dpkg-deb -Zxz --root-owner-group -b {self.tmpfolder}/deb {self.absolute_output_path}"
logger.debug(f"Running command: {dpkg_cmd}", self.debug)
subprocess.run(dpkg_cmd.split(), stdout=subprocess.DEVNULL)

def package(self) -> Path:
# If dpkg is in PATH
# then, package with dpkg-deb
# otherwise, package with constrictor
if self.dpkg:
return self.package_with_dpkg(self.output_path, self.tmpfolder, self.debug)
self.package_with_dpkg()
else:
return self.package_with_constrictor(
self.package_with_constrictor(
self.tmpfolder / "deb/Applications",
self.tmpfolder / "deb/DEBIAN/control",
self.tmpfolder / "deb/DEBIAN/postinst",
self.tmpfolder / "deb/DEBIAN/prerm"
)

return self.absolute_output_path


class Deb:
def __init__(self, src: Path, dest: Path, debug: bool) -> None:
Expand Down
2 changes: 1 addition & 1 deletion permasigner/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@

def log(message, color=None):
if color is None:
print(colors["bold"] + f"{message}" + colors["reset"])
print(f"{message}")
else:
print("\n" + color + colors["bold"] + "[*] " + colors["reset"] + color + f"{message}" + colors["reset"])

Expand Down
7 changes: 6 additions & 1 deletion permasigner/permasigner.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,11 @@ def permasign(self) -> Path:
print("Changing app executable permissions...")
utils.make_executable(full_app_path / bundle["executable"])

executables = utils.set_plugin_permissions(full_app_path, self.args.debug)

if utils.is_windows():
executables.add(f'./{full_app_path.parent.name}/{full_app_path.name}/{bundle["executable"]}')

# Get path to certificate file
cert = utils.get_certificate_path(self.in_package)

Expand All @@ -183,7 +188,7 @@ def permasign(self) -> Path:

# Package the deb file
logger.log("Packaging the deb file...", color=colors["yellow"])
dpkg = Dpkg(bundle, self.tmp, self.output_dir, self.dpkg, self.in_package, self.args.debug)
dpkg = Dpkg(bundle, executables, self.tmp, self.output_dir, self.dpkg, self.in_package, self.args.debug)
return dpkg.package()

def check_path_arguments(self) -> None:
Expand Down
19 changes: 19 additions & 0 deletions permasigner/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@ def make_executable(path: Path) -> None:
file.chmod(mode)


def set_plugin_permissions(payload: Path, debug: bool) -> set:

plugins = set()

for folder in Path(payload / "PlugIns").glob("*.appex"):
plist_path = folder / "Info.plist"
if plist_path.exists():
with open(plist_path, 'rb') as plist:
info = plistlib.load(plist)
executable = info["CFBundleExecutable"]
logger.debug(f"Settings chmod +x on {folder / executable}", debug)
make_executable(folder / executable)

if is_windows():
plugins.add(f"./{payload.parent.name}/{payload.name}/{folder.parent.name}/{folder.name}/{executable}")

return plugins


def cmd_in_path(cmd: str) -> Union[None, str]:
"""Check if command is in PATH"""
path = shutil.which(cmd)
Expand Down

0 comments on commit 445bbbc

Please sign in to comment.