diff --git a/dangerzone/docker_installer.py b/dangerzone/docker_installer.py index 3619ef1..6def8ee 100644 --- a/dangerzone/docker_installer.py +++ b/dangerzone/docker_installer.py @@ -74,6 +74,7 @@ class DockerInstaller(QtWidgets.QDialog): self.task_label = QtWidgets.QLabel() self.task_label.setAlignment(QtCore.Qt.AlignCenter) self.task_label.setWordWrap(True) + self.task_label.setOpenExternalLinks(True) self.progress = QtWidgets.QProgressBar() self.progress.setMinimum(0) diff --git a/install/windows/build-wxs.py b/install/windows/build-wxs.py index 24d74a9..193de67 100644 --- a/install/windows/build-wxs.py +++ b/install/windows/build-wxs.py @@ -1,225 +1,263 @@ -#!/usr/bin/env python3 -import os -import uuid -import xml.etree.ElementTree as ET - - -def build_data(dirname, dir_prefix, id_, name): - data = { - "id": id_, - "name": name, - "files": [], - "dirs": [], - } - - for basename in os.listdir(dirname): - filename = os.path.join(dirname, basename) - if os.path.isfile(filename): - data["files"].append(os.path.join(dir_prefix, basename)) - elif os.path.isdir(filename): - if id_ == "INSTALLDIR": - id_prefix = "Folder" - else: - id_prefix = id_ - - data["dirs"].append( - build_data( - os.path.join(dirname, basename), - os.path.join(dir_prefix, basename), - f"{id_prefix}{basename.capitalize()}", - basename, - ) - ) - - if len(data["files"]) > 0: - if id_ == "INSTALLDIR": - data["component_id"] = "ApplicationFiles" - else: - data["component_id"] = "FolderComponent" + id_[len("Folder") :] - data["component_guid"] = str(uuid.uuid4()) - - return data - - -def build_dir_xml(root, data): - attrs = {} - if "id" in data: - attrs["Id"] = data["id"] - if "name" in data: - attrs["Name"] = data["name"] - el = ET.SubElement(root, "Directory", attrs) - for subdata in data["dirs"]: - build_dir_xml(el, subdata) - - -def build_components_xml(root, data): - component_ids = [] - if "component_id" in data: - component_ids.append(data["component_id"]) - - for subdata in data["dirs"]: - if "component_guid" in subdata: - dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=subdata["id"]) - component_el = ET.SubElement( - dir_ref_el, - "Component", - Id=subdata["component_id"], - Guid=subdata["component_guid"], - ) - for filename in subdata["files"]: - file_el = ET.SubElement( - component_el, "File", Source=filename, Id="file_" + uuid.uuid4().hex - ) - - component_ids += build_components_xml(root, subdata) - - return component_ids - - -def main(): - version_filename = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "dangerzone", - "__init__.py", - ) - with open(version_filename) as f: - for line in f.readlines(): - if line.startswith("dangerzone_version ="): - version = line.split('"')[1] - break - - dist_dir = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), - "dist", - "dangerzone", - ) - if not os.path.exists(dist_dir): - print( - "You must run step1-build-exe.bat to build dangerzone binary before running this" - ) - return - - data = { - "id": "TARGETDIR", - "name": "SourceDir", - "dirs": [ - { - "id": "ProgramFilesFolder", - "dirs": [], - } - ], - } - - data["dirs"][0]["dirs"].append( - build_data( - dist_dir, - os.path.join("..", "..", "dist", "dangerzone"), - "INSTALLDIR", - "Dangerzone", - ) - ) - - root_el = ET.Element("Wix", xmlns="http://schemas.microsoft.com/wix/2006/wi") - product_el = ET.SubElement( - root_el, - "Product", - Name="Dangerzone", - Manufacturer="First Look Media", - Id="f40ff0a9-ebf8-4e1e-9bce-6ab5c74fe119", - UpgradeCode="$(var.ProductUpgradeCode)", - Language="1033", - Codepage="1252", - Version="$(var.ProductVersion)", - ) - ET.SubElement( - product_el, - "Package", - Id="*", - Keywords="Installer", - Description="Dangerzone $(var.ProductVersion) Installer", - Manufacturer="First Look Media", - InstallerVersion="100", - Languages="1033", - Compressed="yes", - SummaryCodepage="1252", - ) - ET.SubElement(product_el, "Media", Id="1", Cabinet="product.cab", EmbedCab="yes") - ET.SubElement( - product_el, "Icon", Id="ProductIcon", SourceFile="..\\..\\share\\dangerzone.ico" - ) - ET.SubElement(product_el, "Property", Id="ARPPRODUCTICON", Value="ProductIcon") - ET.SubElement( - product_el, - "Property", - Id="ARPHELPLINK", - Value="https://github.com/firstlookmedia/dangerzone", - ) - ET.SubElement( - product_el, - "Property", - Id="ARPURLINFOABOUT", - Value="https://tech.firstlook.media", - ) - ET.SubElement(product_el, "UIRef", Id="WixUI_Minimal") - ET.SubElement(product_el, "UIRef", Id="WixUI_ErrorProgressText") - ET.SubElement( - product_el, - "WixVariable", - Id="WixUILicenseRtf", - Value="..\\..\\install\\windows\\license.rtf", - ) - ET.SubElement( - product_el, - "WixVariable", - Id="WixUIDialogBmp", - Value="..\\..\\install\\windows\\dialog.bmp", - ) - upgrade_el = ET.SubElement(product_el, "Upgrade", Id="$(var.ProductUpgradeCode)") - ET.SubElement( - upgrade_el, - "UpgradeVersion", - Minimum="$(var.ProductVersion)", - OnlyDetect="yes", - Property="NEWERVERSIONDETECTED", - ) - ET.SubElement( - upgrade_el, - "UpgradeVersion", - Minimum="0.0.0", - Maximum="$(var.ProductVersion)", - IncludeMinimum="yes", - IncludeMaximum="no", - Property="OLDERVERSIONBEINGUPGRADED", - ) - condition_el = ET.SubElement( - product_el, - "Condition", - Message="A newer version of this software is already installed.", - ) - condition_el.text = "NOT NEWERVERSIONDETECTED" - - build_dir_xml(product_el, data) - component_ids = build_components_xml(product_el, data) - - install_exec_seq_el = ET.SubElement( - product_el, - "InstallExecuteSequence", - ) - ET.SubElement( - install_exec_seq_el, "RemoveExistingProducts", After="InstallValidate" - ) - - feature_el = ET.SubElement(product_el, "Feature", Id="DefaultFeature", Level="1") - for component_id in component_ids: - ET.SubElement(feature_el, "ComponentRef", Id=component_id) - - print('') - print(f'') - print('') - - ET.indent(root_el) - ET.dump(root_el) - - -if __name__ == "__main__": +#!/usr/bin/env python3 +import os +import uuid +import xml.etree.ElementTree as ET + + +def build_data(dirname, dir_prefix, id_, name): + data = { + "id": id_, + "name": name, + "files": [], + "dirs": [], + } + + for basename in os.listdir(dirname): + filename = os.path.join(dirname, basename) + if os.path.isfile(filename): + data["files"].append(os.path.join(dir_prefix, basename)) + elif os.path.isdir(filename): + if id_ == "INSTALLDIR": + id_prefix = "Folder" + else: + id_prefix = id_ + + data["dirs"].append( + build_data( + os.path.join(dirname, basename), + os.path.join(dir_prefix, basename), + f"{id_prefix}{basename.capitalize()}", + basename, + ) + ) + + if len(data["files"]) > 0: + if id_ == "INSTALLDIR": + data["component_id"] = "ApplicationFiles" + else: + data["component_id"] = "FolderComponent" + id_[len("Folder") :] + data["component_guid"] = str(uuid.uuid4()) + + return data + + +def build_dir_xml(root, data): + attrs = {} + if "id" in data: + attrs["Id"] = data["id"] + if "name" in data: + attrs["Name"] = data["name"] + el = ET.SubElement(root, "Directory", attrs) + for subdata in data["dirs"]: + build_dir_xml(el, subdata) + + # If this is the ProgramMenuSubfolder, add the menu component + if "id" in data and data["id"] == "ProgramMenuSubfolder": + component_el = ET.SubElement( + el, + "Component", + Id="ApplicationShortcuts", + Guid="539e7de8-a124-4c09-aa55-0dd516aad7bc", + ) + ET.SubElement( + component_el, + "Shortcut", + Id="ApplicationShortcut1", + Name="Dangerzone", + Description="Dangerzone", + Target="[INSTALLDIR]dangerzone.exe", + WorkingDirectory="INSTALLDIR", + ) + ET.SubElement( + component_el, + "RegistryValue", + Root="HKCU", + Key="Software\First Look Media\Dangerzone", + Name="installed", + Type="integer", + Value="1", + KeyPath="yes", + ) + ET.SubElement( + component_el, "RemoveFolder", Id="ProgramMenuSubfolder", On="uninstall" + ) + + +def build_components_xml(root, data): + component_ids = [] + if "component_id" in data: + component_ids.append(data["component_id"]) + + for subdata in data["dirs"]: + if "component_guid" in subdata: + dir_ref_el = ET.SubElement(root, "DirectoryRef", Id=subdata["id"]) + component_el = ET.SubElement( + dir_ref_el, + "Component", + Id=subdata["component_id"], + Guid=subdata["component_guid"], + ) + for filename in subdata["files"]: + file_el = ET.SubElement( + component_el, "File", Source=filename, Id="file_" + uuid.uuid4().hex + ) + + component_ids += build_components_xml(root, subdata) + + return component_ids + + +def main(): + version_filename = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "dangerzone", + "__init__.py", + ) + with open(version_filename) as f: + for line in f.readlines(): + if line.startswith("dangerzone_version ="): + version = line.split('"')[1] + break + + dist_dir = os.path.join( + os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + "dist", + "dangerzone", + ) + if not os.path.exists(dist_dir): + print( + "You must run step1-build-exe.bat to build dangerzone binary before running this" + ) + return + + data = { + "id": "TARGETDIR", + "name": "SourceDir", + "dirs": [ + { + "id": "ProgramFilesFolder", + "dirs": [], + }, + { + "id": "ProgramMenuFolder", + "dirs": [ + {"id": "ProgramMenuSubfolder", "name": "Dangerzone", "dirs": []} + ], + }, + ], + } + + data["dirs"][0]["dirs"].append( + build_data( + dist_dir, + os.path.join("..", "..", "dist", "dangerzone"), + "INSTALLDIR", + "Dangerzone", + ) + ) + + root_el = ET.Element("Wix", xmlns="http://schemas.microsoft.com/wix/2006/wi") + product_el = ET.SubElement( + root_el, + "Product", + Name="Dangerzone", + Manufacturer="First Look Media", + Id="f40ff0a9-ebf8-4e1e-9bce-6ab5c74fe119", + UpgradeCode="$(var.ProductUpgradeCode)", + Language="1033", + Codepage="1252", + Version="$(var.ProductVersion)", + ) + ET.SubElement( + product_el, + "Package", + Id="*", + Keywords="Installer", + Description="Dangerzone $(var.ProductVersion) Installer", + Manufacturer="First Look Media", + InstallerVersion="100", + Languages="1033", + Compressed="yes", + SummaryCodepage="1252", + ) + ET.SubElement(product_el, "Media", Id="1", Cabinet="product.cab", EmbedCab="yes") + ET.SubElement( + product_el, "Icon", Id="ProductIcon", SourceFile="..\\..\\share\\dangerzone.ico" + ) + ET.SubElement(product_el, "Property", Id="ARPPRODUCTICON", Value="ProductIcon") + ET.SubElement( + product_el, + "Property", + Id="ARPHELPLINK", + Value="https://github.com/firstlookmedia/dangerzone", + ) + ET.SubElement( + product_el, + "Property", + Id="ARPURLINFOABOUT", + Value="https://tech.firstlook.media", + ) + ET.SubElement(product_el, "UIRef", Id="WixUI_Minimal") + ET.SubElement(product_el, "UIRef", Id="WixUI_ErrorProgressText") + ET.SubElement( + product_el, + "WixVariable", + Id="WixUILicenseRtf", + Value="..\\..\\install\\windows\\license.rtf", + ) + ET.SubElement( + product_el, + "WixVariable", + Id="WixUIDialogBmp", + Value="..\\..\\install\\windows\\dialog.bmp", + ) + upgrade_el = ET.SubElement(product_el, "Upgrade", Id="$(var.ProductUpgradeCode)") + ET.SubElement( + upgrade_el, + "UpgradeVersion", + Minimum="$(var.ProductVersion)", + OnlyDetect="yes", + Property="NEWERVERSIONDETECTED", + ) + ET.SubElement( + upgrade_el, + "UpgradeVersion", + Minimum="0.0.0", + Maximum="$(var.ProductVersion)", + IncludeMinimum="yes", + IncludeMaximum="no", + Property="OLDERVERSIONBEINGUPGRADED", + ) + condition_el = ET.SubElement( + product_el, + "Condition", + Message="A newer version of this software is already installed.", + ) + condition_el.text = "NOT NEWERVERSIONDETECTED" + + build_dir_xml(product_el, data) + component_ids = build_components_xml(product_el, data) + + install_exec_seq_el = ET.SubElement( + product_el, + "InstallExecuteSequence", + ) + ET.SubElement( + install_exec_seq_el, "RemoveExistingProducts", After="InstallValidate" + ) + + feature_el = ET.SubElement(product_el, "Feature", Id="DefaultFeature", Level="1") + for component_id in component_ids: + ET.SubElement(feature_el, "ComponentRef", Id=component_id) + ET.SubElement(feature_el, "ComponentRef", Id="ApplicationShortcuts") + + print('') + print(f'') + print('') + + ET.indent(root_el) + ET.dump(root_el) + + +if __name__ == "__main__": main() \ No newline at end of file