release: restore ZUPT and harden source-only 5.2.2
This commit is contained in:
parent
74e393ba3e
commit
ff99770bd0
205 changed files with 19627 additions and 13215 deletions
|
|
@ -3,14 +3,13 @@
|
|||
|
||||
Copyright (C) 2026 Cristian Cezar Moisés <zupt@riseup.net>
|
||||
|
||||
VaptVupt GUI (formerly Zupt GUI; parent application renamed in v3.0.0
|
||||
due to a prior INPI Brasil trademark registration of "Zupt") is free
|
||||
ZUPT GUI is free
|
||||
software: you can redistribute it and/or modify it under the terms of
|
||||
the GNU Affero General Public License as published by the Free
|
||||
Software Foundation, either version 3 of the License, or (at your
|
||||
option) any later version.
|
||||
|
||||
VaptVupt GUI is distributed in the hope that it will be useful, but
|
||||
ZUPT GUI is distributed in the hope that it will be useful, but
|
||||
WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Affero General Public License for more details.
|
||||
|
|
@ -23,21 +22,26 @@
|
|||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
|
||||
PRIOR LICENSE NOTE
|
||||
HISTORICAL LICENSE NOTE
|
||||
|
||||
Earlier copies of this file may have stated "MIT License" — that was
|
||||
a packaging mistake inherited from a template. The GUI source code's
|
||||
SPDX-License-Identifier header has always been AGPL-3.0-or-later;
|
||||
the file-level license here is corrected to match. There is no
|
||||
historical MIT-licensed release of VaptVupt GUI; do not assume MIT
|
||||
grant from any prior tarball that contained this file.
|
||||
Published repository history includes earlier copies of this file
|
||||
under the MIT License. In particular, commit d4660e6539c8b6eeba81751c018217d978fdd618
|
||||
and the v2.2.2 source tag contain an MIT-form gui/LICENSE-GUI. Those
|
||||
permissions remain applicable to the exact material distributed under
|
||||
them; this file does not revoke or reinterpret a historical grant.
|
||||
|
||||
The current GUI source carries AGPL-3.0-or-later SPDX notices. Apply
|
||||
the notices shipped with the exact source revision being used. See the
|
||||
5.2.2 licensing erratum in CHANGELOG.md for the repository record.
|
||||
|
||||
─────────────────────────────────────────────────────────────────────
|
||||
|
||||
COMMERCIAL LICENSING
|
||||
|
||||
The VaptVupt GUI may be commercially relicensed by the author. If
|
||||
you require relief from copyleft terms (proprietary derivatives,
|
||||
closed-source bundling, etc.), contact:
|
||||
The applicable copyright holder may offer controlled first-party
|
||||
rights under a separately executed commercial agreement. This notice
|
||||
is not a commercial license grant, does not revoke a public license,
|
||||
and cannot relicense rights that the licensor does not control. For
|
||||
inquiries, contact:
|
||||
|
||||
sac@securityops.co
|
||||
|
|
|
|||
228
gui/README.md
228
gui/README.md
|
|
@ -1,123 +1,149 @@
|
|||
# VaptVupt GUI
|
||||
# ZUPT GUI
|
||||
|
||||
Desktop application for [vaptvupt](https://git.securityops.co/cristiancmoises/vaptvupt) backup compression with ML-KEM-768 + X25519 post-quantum hybrid encryption.
|
||||
The ZUPT GUI is a Python/Qt front end for the ZUPT 5.2.2 command-line
|
||||
program. It starts the CLI as a subprocess; compression, archive parsing, and
|
||||
cryptography remain in the C program.
|
||||
|
||||
Works on GNU/Linux, BSD, macOS, and Windows.
|
||||
The canonical project repository is
|
||||
`https://github.com/cristiancmoises/zupt`.
|
||||
|
||||
## Install
|
||||
## Requirements
|
||||
|
||||
### Linux (recommended)
|
||||
- a working `zupt` CLI from the same release, available on `PATH` or through
|
||||
the `ZUPT_BIN` environment variable;
|
||||
- Python 3.9 or newer;
|
||||
- PySide6 or PyQt6;
|
||||
- a graphical session for normal use.
|
||||
|
||||
```bash
|
||||
tar xzf vaptvupt-gui.tar.gz && cd vaptvupt-gui
|
||||
./vaptvupt-gui # auto-creates venv, installs PySide6
|
||||
./install.sh --user # adds right-click menu integration
|
||||
Install and test the source-only CLI first:
|
||||
|
||||
```sh
|
||||
make clean
|
||||
make -j"$(getconf _NPROCESSORS_ONLN 2>/dev/null || printf 1)" \
|
||||
WITH_SDK=0 WITH_PQBOX=0
|
||||
make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
./zupt --version
|
||||
```
|
||||
|
||||
### Windows
|
||||
The GUI detects the capabilities reported by that binary. Native `--pq` and
|
||||
`--pq-only` are available in the default build. SDK and PQBOX controls are
|
||||
usable only when the CLI was built explicitly against the corresponding system
|
||||
development libraries; no precompiled optional library is shipped in Git.
|
||||
|
||||
**Option A — Installer (recommended):**
|
||||
## Run from the source tree
|
||||
|
||||
Download `VaptVuptGUI-1.3.0-Setup.exe` and run it. Installs to Program Files, adds Start Menu shortcut, desktop shortcut, right-click context menus, and .zupt file association. Includes uninstaller.
|
||||
Using a virtual environment keeps Python packages outside the repository:
|
||||
|
||||
**Option B — Build from source:**
|
||||
|
||||
```cmd
|
||||
cd packaging\windows
|
||||
build-windows.bat
|
||||
```sh
|
||||
python3 -m venv ~/.local/share/zupt-gui-venv
|
||||
~/.local/share/zupt-gui-venv/bin/pip install PySide6
|
||||
ZUPT_BIN="$PWD/zupt" \
|
||||
~/.local/share/zupt-gui-venv/bin/python gui/src/zupt_gui.py
|
||||
```
|
||||
|
||||
Requires Python 3.9+, NSIS 3.x, and a compiled `vaptvupt.exe`.
|
||||
Installing PySide6 can access the Python package index. Do that as an explicit
|
||||
setup step; upstream CLI builds, package builds, and checks do not download
|
||||
dependencies.
|
||||
|
||||
**Option C — Run directly:**
|
||||
For noninteractive checks:
|
||||
|
||||
```cmd
|
||||
pip install PySide6
|
||||
python src\zupt_gui.py
|
||||
```sh
|
||||
python3 gui/src/zupt_gui.py --version
|
||||
python3 gui/src/zupt_gui.py --selftest
|
||||
```
|
||||
|
||||
### macOS / BSD
|
||||
The first command does not prove that a full desktop session works. Test the
|
||||
actual windows and archive operations on every platform for which a GUI package
|
||||
is published.
|
||||
|
||||
```bash
|
||||
pip3 install PySide6
|
||||
python3 src/zupt_gui.py
|
||||
## Functions
|
||||
|
||||
| Area | Function |
|
||||
|---|---|
|
||||
| Keys | Generate keys supported by the selected CLI build |
|
||||
| Compress | Select input, compression settings, and an available encryption mode |
|
||||
| Extract | Detect archive encryption, request the needed credential, and extract |
|
||||
| Verify | Run archive integrity validation without extraction |
|
||||
| Info | Display metadata reported by the CLI |
|
||||
| Disk | Front end for the CLI disk backup and restore commands |
|
||||
|
||||
Disk operations can require additional operating-system privileges. Run only
|
||||
the specific CLI operation that needs them; do not run the whole desktop session
|
||||
as root.
|
||||
|
||||
## Desktop integration
|
||||
|
||||
`gui/install.sh --user` installs the integration supported by that script for
|
||||
the current user. Review the script and its destination paths before running
|
||||
it. File-manager menus and file associations differ across desktops and
|
||||
operating systems and must be tested on the target system.
|
||||
|
||||
## Packaged GUI builds
|
||||
|
||||
Release pages provide only these GUI artifacts after their separate package and
|
||||
installed off-screen GUI/CLI integration gates pass:
|
||||
|
||||
- `zupt-gui_5.2.2_all.deb`;
|
||||
- `zupt-gui-5.2.2-1.noarch.rpm`;
|
||||
- `zupt-gui-5.2.2-1.src.rpm`;
|
||||
- `zupt-gui-5.2.2-portable.zip`.
|
||||
|
||||
The DEB/RPM packages install the Python/Qt source and depend on the matching
|
||||
`zupt` CLI package. The portable ZIP contains source, launchers, icons, licenses,
|
||||
and provenance only; it bundles no Python, Qt, CLI, or compiled runtime. Its
|
||||
gate scans the assembled and extracted trees, enforces an exact safe-member
|
||||
allowlist, and runs the extracted launcher off-screen against the tested CLI.
|
||||
An absent artifact did not pass its gate and must not be inferred from another
|
||||
format's result.
|
||||
|
||||
GUI AppImage, AppDir and Flatpak bundles, and Windows/macOS GUI installers are
|
||||
not promoted by the upstream 5.2.2 release gates.
|
||||
`packaging/build-gui-appimage.sh` is a downstream-only helper and fails unless
|
||||
its operator supplies the exact verified runtime plus a complete
|
||||
license/source-relink notice through `APPIMAGE_RUNTIME_COMPLIANCE_FILE`; that
|
||||
material is included in the resulting AppDir.
|
||||
|
||||
The downstream Windows GUI helper similarly requires
|
||||
`ZUPT_WINDOWS_RUNTIME_NOTICES_DIR` with a `MANIFEST.txt` that identifies
|
||||
the exact Python, PyInstaller, Qt and PySide/PyQt runtime inputs and their
|
||||
notices. It fails unless the directory also has non-empty
|
||||
`PYTHON-NOTICE.txt`, `PYINSTALLER-NOTICE.txt`, `QT-NOTICE.txt`, and either
|
||||
`PYSIDE6-NOTICE.txt` or `PYQT6-NOTICE.txt`. The installer includes that
|
||||
directory together with every ZUPT license and notice. This requirement does
|
||||
not make the untested GUI installer a 5.2.2 release asset. The promoted Windows
|
||||
ZIP and macOS DMG are CLI-only.
|
||||
|
||||
Packaging recipes and scripts under `gui/packaging/` and `packaging/` are build
|
||||
inputs, not evidence that a package has been accepted by a distribution. They
|
||||
must build the CLI from the immutable source tag with
|
||||
`WITH_SDK=0 WITH_PQBOX=0` unless source-built system dependencies are declared.
|
||||
Generated packages, application bundles, and executables must remain outside
|
||||
Git and outside upstream source archives.
|
||||
|
||||
The former standalone `gui/setup.py` sdist/wheel route is intentionally absent:
|
||||
its outputs did not carry the complete project license payload. Use
|
||||
`gui/install.sh` or the reviewed distribution helpers so the AGPL text and
|
||||
artwork provenance are installed with the GUI.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
Verify the exact interpreter and CLI used by the GUI:
|
||||
|
||||
```sh
|
||||
python3 -c 'import PySide6.QtWidgets'
|
||||
zupt --version
|
||||
ZUPT_BIN=/absolute/path/to/zupt \
|
||||
python3 gui/src/zupt_gui.py --selftest
|
||||
```
|
||||
|
||||
### AppImage (universal Linux)
|
||||
|
||||
```bash
|
||||
chmod +x VaptVupt-GUI-1.3.0-x86_64.AppImage
|
||||
./VaptVupt-GUI-1.3.0-x86_64.AppImage
|
||||
```
|
||||
|
||||
### Flatpak
|
||||
|
||||
```bash
|
||||
flatpak-builder --install build packaging/flatpak/dev.zupt.gui.yml
|
||||
flatpak run dev.zupt.gui
|
||||
```
|
||||
|
||||
## Features
|
||||
|
||||
| Tab | Function |
|
||||
|-----|----------|
|
||||
| Keys | Generate ML-KEM-768 + X25519 hybrid keypairs |
|
||||
| Compress | All codecs, levels 1-9, dedup, solid, password, PQ keys |
|
||||
| Extract | Decrypt and extract .zupt archives |
|
||||
| Verify | Check block checksums, view archive metadata |
|
||||
| Disk | Full-disk backup and restore |
|
||||
| About | Version, cryptographic stack, credits |
|
||||
|
||||
All tabs support drag-and-drop. Drop a .zupt file anywhere on the window to extract it. Drop any other file to compress it.
|
||||
|
||||
## System Integration
|
||||
|
||||
### Linux (Nemo / Cinnamon)
|
||||
|
||||
After `./install.sh --user`:
|
||||
- Right-click any file: **Compress with VaptVupt**
|
||||
- Right-click .zupt file: **Extract with VaptVupt**
|
||||
- Double-click .zupt: opens in VaptVupt GUI
|
||||
|
||||
### Windows (after installer)
|
||||
|
||||
- Right-click any file: **Compress with VaptVupt**
|
||||
- Right-click any folder: **Compress with VaptVupt**
|
||||
- Double-click .zupt: opens in VaptVupt GUI
|
||||
- Right-click .zupt: **Verify Integrity**
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
VaptVupt GUI (PySide6, Python)
|
||||
|
|
||||
|-- subprocess.Popen() with streaming stderr
|
||||
|
|
||||
v
|
||||
vaptvupt CLI (Pure C11 binary)
|
||||
ML-KEM-768 + X25519 + AES-256-CTR
|
||||
VaptVupt / LZHP / Store codecs
|
||||
Block deduplication, full-disk backup
|
||||
```
|
||||
|
||||
The GUI calls the vaptvupt CLI binary — all cryptography runs in native C, not Python.
|
||||
|
||||
## Packaging
|
||||
|
||||
| Platform | Format | Tool |
|
||||
|----------|--------|------|
|
||||
| Any | pip | `pip install .` |
|
||||
| Debian/Ubuntu/Mint | .deb | `packaging/deb/control` |
|
||||
| Fedora/RHEL | .rpm | `rpmbuild -ba packaging/rpm/vaptvupt.spec` |
|
||||
| Universal Linux | .AppImage | `packaging/appimage/build-appimage.sh` |
|
||||
| Sandboxed Linux | .flatpak | `packaging/flatpak/dev.zupt.gui.yml` |
|
||||
| Windows | .exe installer | `packaging/windows/build-windows.bat` |
|
||||
| Windows | NSIS .exe | `packaging/windows/zupt-installer.nsi` |
|
||||
|
||||
## Credits
|
||||
|
||||
- **vaptvupt** v5.2.1 — Cristian Cezar Moisés ([github](https://git.securityops.co/cristiancmoises/vaptvupt))
|
||||
If no window appears, run the GUI from a terminal and check the display/Wayland
|
||||
or X11 error. When reporting a problem, include OS and desktop versions, Python
|
||||
and Qt binding versions, `zupt --version`, and the non-sensitive error
|
||||
message. Never attach passwords, private keys, tokens, or confidential archives.
|
||||
|
||||
## License
|
||||
|
||||
AGPL-3.0-or-later
|
||||
The current GUI source is AGPL-3.0-or-later. Published historical revisions
|
||||
include MIT notices whose grants remain applicable to the exact material
|
||||
distributed under them. See `gui/LICENSE-GUI`, the 5.2.2 licensing erratum in
|
||||
`CHANGELOG.md`, and the repository-level license notices.
|
||||
|
|
|
|||
23
gui/assets/README.md
Normal file
23
gui/assets/README.md
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# GUI image assets
|
||||
|
||||
These files are runtime data used by the graphical interface and its packaging;
|
||||
they are not executable code or compiler output.
|
||||
|
||||
| File | Purpose |
|
||||
| --- | --- |
|
||||
| `zupt-icon.png` | Main 48 px application icon used by the GUI and packaging. |
|
||||
| `zupt-128.png` | 128 px application icon. |
|
||||
| `zupt.png` | 256 px application/AppDir icon. |
|
||||
| `zupt.ico` | Windows application icon container. |
|
||||
|
||||
Git provenance: all four files were first added by Cristian Cezar Moisés in
|
||||
ZUPT repository commit `d4660e6539c8b6eeba81751c018217d978fdd618`; the repository
|
||||
records no earlier or external origin. Their current Git blobs are byte-for-byte
|
||||
the same blobs present in that commit. That revision distributed them with MIT
|
||||
license notices, whose permissions remain available for those exact files.
|
||||
|
||||
The current GUI tree also carries AGPL-3.0-or-later notices; apply the license
|
||||
option appropriate to the exact material and revision being redistributed and
|
||||
preserve `gui/LICENSE-GUI` and the repository notices. Their historical
|
||||
filenames are retained because build and desktop-integration files refer to
|
||||
them.
|
||||
200
gui/install.sh
200
gui/install.sh
|
|
@ -1,90 +1,130 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Install Zupt GUI + desktop integration
|
||||
# Run: sudo ./install.sh (or ./install.sh --user for per-user install)
|
||||
set -e
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
USER_INSTALL=0
|
||||
[ "$1" = "--user" ] && USER_INSTALL=1
|
||||
# Install the integrated ZUPT GUI from the checked-out source tree.
|
||||
# This script never downloads Python modules or operating-system packages.
|
||||
|
||||
if [ "$USER_INSTALL" -eq 1 ]; then
|
||||
BIN="$HOME/.local/bin"
|
||||
APPS="$HOME/.local/share/applications"
|
||||
NEMO="$HOME/.local/share/nemo/actions"
|
||||
MIME="$HOME/.local/share/mime"
|
||||
NAUTILUS="$HOME/.local/share/nautilus/scripts"
|
||||
else
|
||||
BIN="/usr/local/bin"
|
||||
APPS="/usr/share/applications"
|
||||
NEMO="/usr/share/nemo/actions"
|
||||
MIME="/usr/share/mime"
|
||||
NAUTILUS=""
|
||||
set -Eeuo pipefail
|
||||
|
||||
die() {
|
||||
printf 'zupt-gui install: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Usage: gui/install.sh [OPTIONS]
|
||||
|
||||
--user install below $HOME/.local
|
||||
--prefix DIR installation prefix (default: /usr/local)
|
||||
--destdir DIR staging root for package builds
|
||||
--legacy-alias install opt-in vaptvupt-gui compatibility symlink
|
||||
-h, --help show this help
|
||||
|
||||
Python 3.9+ and either PySide6 or PyQt6 must already be installed. The
|
||||
zupt CLI must also be installed or selected with ZUPT_BIN at runtime.
|
||||
VAPTVUPT_BIN remains a renamed-era compatibility fallback.
|
||||
EOF
|
||||
}
|
||||
|
||||
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
repo_root=$(cd -- "$script_dir/.." && pwd -P)
|
||||
prefix=/usr/local
|
||||
destdir=${DESTDIR:-}
|
||||
legacy_alias=0
|
||||
|
||||
while (($#)); do
|
||||
case $1 in
|
||||
--user)
|
||||
[[ -n ${HOME:-} ]] || die 'HOME is unset; cannot use --user'
|
||||
prefix=$HOME/.local
|
||||
;;
|
||||
--prefix)
|
||||
(($# >= 2)) || die '--prefix requires a directory'
|
||||
prefix=$2
|
||||
shift
|
||||
;;
|
||||
--destdir)
|
||||
(($# >= 2)) || die '--destdir requires a directory'
|
||||
destdir=$2
|
||||
shift
|
||||
;;
|
||||
--legacy-alias) legacy_alias=1 ;;
|
||||
-h|--help) usage; exit 0 ;;
|
||||
*) die "unknown option: $1" ;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
[[ $prefix == /* ]] || die '--prefix must be an absolute path'
|
||||
[[ -z $destdir || $destdir == /* ]] || die '--destdir must be an absolute path'
|
||||
|
||||
bindir=${BINDIR:-$prefix/bin}
|
||||
libexecdir=${LIBEXECDIR:-$prefix/lib/zupt-gui}
|
||||
datadir=${DATADIR:-$prefix/share}
|
||||
|
||||
for source_file in \
|
||||
"$script_dir/src/zupt_gui.py" \
|
||||
"$script_dir/assets/zupt-icon.png" \
|
||||
"$script_dir/packaging/zupt-gui.desktop" \
|
||||
"$repo_root/doc/zupt-gui.1" \
|
||||
"$repo_root/LICENSE" \
|
||||
"$repo_root/LICENSE-AGPL-3.0" \
|
||||
"$script_dir/LICENSE-GUI" \
|
||||
"$script_dir/assets/README.md"; do
|
||||
[[ -f $source_file ]] || die "required source file is missing: $source_file"
|
||||
done
|
||||
|
||||
stage_bindir=$destdir$bindir
|
||||
stage_libexecdir=$destdir$libexecdir
|
||||
stage_datadir=$destdir$datadir
|
||||
install -d -- "$stage_bindir" "$stage_libexecdir" \
|
||||
"$stage_datadir/applications" \
|
||||
"$stage_datadir/icons/hicolor/256x256/apps" \
|
||||
"$stage_datadir/man/man1" \
|
||||
"$stage_datadir/licenses/zupt-gui"
|
||||
|
||||
install -m 0644 -- "$script_dir/src/zupt_gui.py" "$stage_libexecdir/zupt_gui.py"
|
||||
install -m 0644 -- "$script_dir/packaging/zupt-gui.desktop" \
|
||||
"$stage_datadir/applications/zupt-gui.desktop"
|
||||
install -m 0644 -- "$script_dir/assets/zupt-icon.png" \
|
||||
"$stage_datadir/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
install -m 0644 -- "$repo_root/doc/zupt-gui.1" \
|
||||
"$stage_datadir/man/man1/zupt-gui.1"
|
||||
install -m 0644 -- "$repo_root/LICENSE" \
|
||||
"$stage_datadir/licenses/zupt-gui/LICENSE"
|
||||
install -m 0644 -- "$repo_root/LICENSE-AGPL-3.0" \
|
||||
"$stage_datadir/licenses/zupt-gui/LICENSE-AGPL-3.0"
|
||||
install -m 0644 -- "$script_dir/LICENSE-GUI" \
|
||||
"$stage_datadir/licenses/zupt-gui/LICENSE-GUI"
|
||||
install -m 0644 -- "$script_dir/assets/README.md" \
|
||||
"$stage_datadir/licenses/zupt-gui/ASSET-PROVENANCE.md"
|
||||
|
||||
# Quote the installed module path for a POSIX shell without embedding DESTDIR.
|
||||
quoted_libexec=${libexecdir//\'/\'\\\'\'}
|
||||
launcher_tmp=$(mktemp "${TMPDIR:-/tmp}/zupt-gui-launcher.XXXXXXXX")
|
||||
trap 'rm -f -- "$launcher_tmp"' EXIT HUP INT TERM
|
||||
cat >"$launcher_tmp" <<EOF
|
||||
#!/bin/sh
|
||||
exec python3 '$quoted_libexec/zupt_gui.py' "\$@"
|
||||
EOF
|
||||
install -m 0755 -- "$launcher_tmp" "$stage_bindir/zupt-gui"
|
||||
|
||||
if ((legacy_alias)); then
|
||||
ln -s -- zupt-gui "$stage_bindir/vaptvupt-gui"
|
||||
fi
|
||||
|
||||
mkdir -p "$BIN" "$APPS"
|
||||
|
||||
# ── Install launcher ──
|
||||
cat > "$BIN/zupt-gui" << LAUNCHER
|
||||
#!/bin/bash
|
||||
DIR="$DIR"
|
||||
VENV="\$DIR/.venv"
|
||||
PY="\$VENV/bin/python3"
|
||||
[ ! -x "\$PY" ] && python3 -m venv "\$VENV" && "\$VENV/bin/pip" install PySide6 -q
|
||||
exec "\$PY" "\$DIR/src/zupt_gui.py" "\$@"
|
||||
LAUNCHER
|
||||
chmod +x "$BIN/zupt-gui"
|
||||
echo "Installed: $BIN/zupt-gui"
|
||||
|
||||
# ── Desktop entry ──
|
||||
cp "$DIR/packaging/zupt-gui.desktop" "$APPS/"
|
||||
echo "Installed: $APPS/zupt-gui.desktop"
|
||||
|
||||
# ── Nemo actions (Linux Mint / Cinnamon) ──
|
||||
if [ -d "$(dirname "$NEMO")" ] || [ "$USER_INSTALL" -eq 1 ]; then
|
||||
mkdir -p "$NEMO"
|
||||
cp "$DIR/packaging/desktop-integration/nemo/"*.nemo_action "$NEMO/" 2>/dev/null && \
|
||||
echo "Installed: Nemo right-click actions" || true
|
||||
fi
|
||||
|
||||
# ── Nautilus scripts (GNOME) ──
|
||||
if [ -n "$NAUTILUS" ]; then
|
||||
mkdir -p "$NAUTILUS"
|
||||
cat > "$NAUTILUS/Compress with Zupt" << 'NSCRIPT'
|
||||
#!/bin/bash
|
||||
zupt-gui --compress $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
|
||||
NSCRIPT
|
||||
chmod +x "$NAUTILUS/Compress with Zupt"
|
||||
echo "Installed: Nautilus script"
|
||||
fi
|
||||
|
||||
# ── MIME type for .zupt files ──
|
||||
MIME_XML="$MIME/packages/zupt.xml"
|
||||
if [ ! -f "$MIME_XML" ]; then
|
||||
mkdir -p "$(dirname "$MIME_XML")"
|
||||
cat > "$MIME_XML" << 'MIMEXML'
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<mime-info xmlns="http://www.freedesktop.org/standards/shared-mime-info">
|
||||
<mime-type type="application/x-zupt">
|
||||
<comment>Zupt Archive</comment>
|
||||
<glob pattern="*.zupt"/>
|
||||
<icon name="package-x-generic"/>
|
||||
</mime-type>
|
||||
</mime-info>
|
||||
MIMEXML
|
||||
if command -v update-mime-database >/dev/null; then
|
||||
update-mime-database "$MIME" 2>/dev/null
|
||||
if [[ -z $destdir ]]; then
|
||||
if command -v update-desktop-database >/dev/null 2>&1; then
|
||||
update-desktop-database "$datadir/applications" >/dev/null 2>&1 || true
|
||||
fi
|
||||
if command -v gtk-update-icon-cache >/dev/null 2>&1; then
|
||||
gtk-update-icon-cache -q "$datadir/icons/hicolor" >/dev/null 2>&1 || true
|
||||
fi
|
||||
echo "Registered: .zupt MIME type"
|
||||
fi
|
||||
|
||||
# ── Associate .zupt files with zupt-gui ──
|
||||
if command -v xdg-mime >/dev/null; then
|
||||
xdg-mime default zupt-gui.desktop application/x-zupt 2>/dev/null
|
||||
echo "Associated: .zupt files open with Zupt GUI"
|
||||
printf 'Installed zupt-gui below %s%s\n' "$destdir" "$prefix"
|
||||
if ((!legacy_alias)); then
|
||||
printf 'Legacy vaptvupt-gui alias was not installed (use --legacy-alias to opt in).\n'
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "Done. Right-click any file in your file manager to see Zupt options."
|
||||
echo "Double-click any .zupt file to open it in Zupt GUI."
|
||||
|
|
|
|||
|
|
@ -1,51 +1,7 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Build Zupt GUI AppImage
|
||||
# Requires: appimagetool, python3, pip
|
||||
set -e
|
||||
|
||||
APP="zupt-gui"
|
||||
VERSION="1.0.0"
|
||||
APPDIR="${APP}.AppDir"
|
||||
|
||||
rm -rf "$APPDIR" "${APP}-${VERSION}-x86_64.AppImage"
|
||||
mkdir -p "$APPDIR/usr/bin" "$APPDIR/usr/share/zupt-gui" "$APPDIR/usr/share/applications" "$APPDIR/usr/share/icons/hicolor/256x256/apps"
|
||||
|
||||
# Install Python + deps into AppDir
|
||||
python3 -m venv "$APPDIR/usr/python"
|
||||
"$APPDIR/usr/python/bin/pip" install PySide6 --quiet
|
||||
|
||||
# Copy app
|
||||
cp ../../src/zupt_gui.py "$APPDIR/usr/share/zupt-gui/"
|
||||
cp ../../assets/zupt.png "$APPDIR/usr/share/icons/hicolor/256x256/apps/zupt-gui.png" 2>/dev/null || true
|
||||
|
||||
# Create launcher
|
||||
cat > "$APPDIR/AppRun" << 'APPRUN'
|
||||
#!/bin/bash
|
||||
HERE="$(dirname "$(readlink -f "$0")")"
|
||||
export PATH="$HERE/usr/bin:$HERE/usr/python/bin:$PATH"
|
||||
exec python3 "$HERE/usr/share/zupt-gui/zupt_gui.py" "$@"
|
||||
APPRUN
|
||||
chmod +x "$APPDIR/AppRun"
|
||||
|
||||
# Desktop file
|
||||
cat > "$APPDIR/${APP}.desktop" << DESKTOP
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Zupt GUI
|
||||
Comment=Post-Quantum Backup Utility
|
||||
Exec=zupt-gui
|
||||
Icon=zupt-gui
|
||||
Categories=Utility;Archiving;Security;
|
||||
Terminal=false
|
||||
DESKTOP
|
||||
|
||||
# Build AppImage
|
||||
if command -v appimagetool >/dev/null; then
|
||||
ARCH=x86_64 appimagetool "$APPDIR" "${APP}-${VERSION}-x86_64.AppImage"
|
||||
echo "Built: ${APP}-${VERSION}-x86_64.AppImage"
|
||||
else
|
||||
echo "appimagetool not found. Install from https://github.com/AppImage/AppImageKit"
|
||||
echo "AppDir ready at: $APPDIR/"
|
||||
fi
|
||||
# Compatibility entry point for the canonical source-only GUI builder.
|
||||
set -Eeuo pipefail
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../../.." && pwd -P)
|
||||
exec "$repo_root/packaging/build-gui-appimage.sh" "$@"
|
||||
|
|
|
|||
|
|
@ -1,94 +1,7 @@
|
|||
#!/bin/bash
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
# Build zupt-gui .deb package
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
VERSION="${VERSION:-2.2.1}"
|
||||
ARCH="all"
|
||||
PKG="zupt-gui_${VERSION}_${ARCH}"
|
||||
ROOT="/tmp/$PKG"
|
||||
|
||||
rm -rf "$ROOT"
|
||||
mkdir -p "$ROOT/DEBIAN" \
|
||||
"$ROOT/usr/bin" \
|
||||
"$ROOT/usr/lib/python3/dist-packages" \
|
||||
"$ROOT/usr/share/applications" \
|
||||
"$ROOT/usr/share/icons/hicolor/256x256/apps" \
|
||||
"$ROOT/usr/share/doc/zupt-gui"
|
||||
|
||||
# Python module
|
||||
install -m 644 src/zupt_gui.py "$ROOT/usr/lib/python3/dist-packages/zupt_gui.py"
|
||||
|
||||
# Launcher
|
||||
cat > "$ROOT/usr/bin/zupt-gui" <<'LAUNCH'
|
||||
#!/usr/bin/env python3
|
||||
import sys
|
||||
sys.path.insert(0, "/usr/lib/python3/dist-packages")
|
||||
from zupt_gui import main
|
||||
sys.exit(main())
|
||||
LAUNCH
|
||||
chmod +x "$ROOT/usr/bin/zupt-gui"
|
||||
|
||||
# Desktop file
|
||||
install -m 644 packaging/zupt-gui.desktop "$ROOT/usr/share/applications/" 2>/dev/null || cat > "$ROOT/usr/share/applications/zupt-gui.desktop" <<'DESK'
|
||||
[Desktop Entry]
|
||||
Name=Zupt GUI
|
||||
GenericName=Post-Quantum Backup Utility
|
||||
Comment=Compress and encrypt files with hybrid PQ crypto
|
||||
Exec=zupt-gui
|
||||
Terminal=false
|
||||
Type=Application
|
||||
Categories=Utility;Archiving;Security;
|
||||
Icon=zupt-gui
|
||||
DESK
|
||||
|
||||
# Icon (use a real one if assets exist)
|
||||
if [ -f assets/zupt-256.png ]; then
|
||||
install -m 644 assets/zupt-256.png "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
elif [ -d ../assets ] && [ -f ../assets/zupt-256.png ]; then
|
||||
install -m 644 ../assets/zupt-256.png "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
else
|
||||
# 1×1 placeholder
|
||||
printf '\x89PNG\r\n\x1a\n\x00\x00\x00\rIHDR\x00\x00\x00\x01\x00\x00\x00\x01\x08\x06\x00\x00\x00\x1f\x15\xc4\x89\x00\x00\x00\rIDATx\x9cc\xfc\xcf\xc0\x00\x00\x00\x05\x00\x01\xa5\xf6E@\x00\x00\x00\x00IEND\xaeB`\x82' > "$ROOT/usr/share/icons/hicolor/256x256/apps/zupt-gui.png"
|
||||
fi
|
||||
|
||||
# Docs
|
||||
install -m 644 README.md "$ROOT/usr/share/doc/zupt-gui/"
|
||||
gzip -9n -c ../CHANGELOG.md > "$ROOT/usr/share/doc/zupt-gui/changelog.gz" 2>/dev/null || true
|
||||
|
||||
cat > "$ROOT/usr/share/doc/zupt-gui/copyright" <<'COPYRIGHT'
|
||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: zupt-gui
|
||||
Upstream-Contact: Cristian Cezar Moisés <zupt@riseup.net>
|
||||
Source: https://git.securityops.co/cristiancmoises/zupt
|
||||
|
||||
Files: *
|
||||
Copyright: 2025-2026 Cristian Cezar Moisés
|
||||
License: AGPL-3.0+
|
||||
COPYRIGHT
|
||||
|
||||
INSTALLED_SIZE=$(du -sk "$ROOT" | cut -f1)
|
||||
cat > "$ROOT/DEBIAN/control" <<EOF
|
||||
Package: zupt-gui
|
||||
Version: $VERSION
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: $ARCH
|
||||
Depends: python3 (>= 3.9), python3-pyside6, zupt (>= 2.2.0)
|
||||
Maintainer: Cristian Cezar Moisés <zupt@riseup.net>
|
||||
Installed-Size: $INSTALLED_SIZE
|
||||
Homepage: https://git.securityops.co/cristiancmoises/zupt
|
||||
Description: Zupt GUI — Post-Quantum Backup Utility
|
||||
Cross-platform graphical interface for the zupt backup compression
|
||||
utility with post-quantum hybrid encryption (ML-KEM-768 + X25519).
|
||||
.
|
||||
v2.2+ uses libzuptsdk under the hood for HKDF-SHA3 hybrid combiner,
|
||||
32-byte key commitment, HPKE binding (RFC 9180), and anti-fault
|
||||
double-decapsulation. Supports legacy archives via auto-detection.
|
||||
EOF
|
||||
|
||||
dpkg-deb --build --root-owner-group "$ROOT" "/tmp/$PKG.deb"
|
||||
echo "Built: /tmp/$PKG.deb"
|
||||
dpkg-deb --info "/tmp/$PKG.deb" | head -12
|
||||
# Compatibility entry point for the canonical source-only GUI builder.
|
||||
set -Eeuo pipefail
|
||||
repo_root=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/../.." && pwd -P)
|
||||
exec "$repo_root/packaging/build-gui-deb.sh" "$@"
|
||||
|
|
|
|||
|
|
@ -1,13 +1,12 @@
|
|||
Package: zupt-gui
|
||||
Version: 1.0.0
|
||||
Version: 5.2.2
|
||||
Section: utils
|
||||
Priority: optional
|
||||
Architecture: all
|
||||
Depends: python3 (>= 3.9), python3-pyside6, zupt (>= 2.1.6)
|
||||
Maintainer: Cristian Cezar Moises <cristian@zupt.dev>
|
||||
Depends: python3 (>= 3.9), python3-pyqt6 | python3-pyside6.qtwidgets, zupt (= 5.2.2)
|
||||
Maintainer: Cristian Cezar Moisés <sac@securityops.co>
|
||||
Homepage: https://github.com/cristiancmoises/zupt
|
||||
Description: Zupt GUI — Post-Quantum Backup Utility
|
||||
Cross-platform graphical interface for the zupt backup compression
|
||||
utility with post-quantum hybrid encryption (ML-KEM-768 + X25519),
|
||||
hardware-adaptive codecs, block-level deduplication, and full-disk
|
||||
backup/restore support.
|
||||
Description: Qt graphical interface for the ZUPT backup utility
|
||||
The GUI creates, inspects, verifies, and extracts .zupt archives through the
|
||||
separately packaged ZUPT command. Native post-quantum modes are available
|
||||
in the baseline build; SDK and PQ-box controls follow CLI capability detection.
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
[Nemo Action]
|
||||
Name=Compress with Zupt
|
||||
Comment=Create encrypted .zupt archive
|
||||
Name=Compress with ZUPT
|
||||
Comment=Create a .zupt archive with ZUPT GUI
|
||||
Exec=zupt-gui --compress %F
|
||||
Icon-Name=package-x-generic
|
||||
Icon-Name=zupt-gui
|
||||
Selection=Any
|
||||
Extensions=any;
|
||||
|
|
|
|||
|
|
@ -1,7 +1,8 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
[Nemo Action]
|
||||
Name=Extract with Zupt
|
||||
Comment=Decrypt and extract .zupt archive
|
||||
Name=Extract with ZUPT
|
||||
Comment=Extract a .zupt archive with ZUPT GUI
|
||||
Exec=zupt-gui --extract %F
|
||||
Icon-Name=package-x-generic
|
||||
Icon-Name=zupt-gui
|
||||
Selection=S
|
||||
Extensions=zupt;
|
||||
|
|
|
|||
|
|
@ -1,39 +1,42 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2026 Cristian Cezar Moisés
|
||||
|
||||
# Offline build manifest for the integrated source tree. The Qt/Python base and
|
||||
# SDK are resolved by Flatpak before the build; no command below uses pip or
|
||||
# downloads project dependencies.
|
||||
app-id: dev.zupt.gui
|
||||
runtime: org.freedesktop.Platform
|
||||
runtime-version: '24.08'
|
||||
sdk: org.freedesktop.Sdk
|
||||
runtime: org.kde.Platform
|
||||
runtime-version: '6.8'
|
||||
sdk: org.kde.Sdk
|
||||
base: io.qt.PySide.BaseApp
|
||||
base-version: '6.8'
|
||||
command: zupt-gui
|
||||
|
||||
finish-args:
|
||||
- --share=ipc
|
||||
- --socket=x11
|
||||
- --socket=fallback-x11
|
||||
- --socket=wayland
|
||||
- --filesystem=home
|
||||
- --device=all # For disk backup (block devices)
|
||||
|
||||
modules:
|
||||
- name: python3-pyside6
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- pip3 install --prefix=/app PySide6
|
||||
|
||||
- name: zupt
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- make
|
||||
- install -Dm755 zupt /app/bin/zupt
|
||||
sources:
|
||||
- type: git
|
||||
url: https://git.securityops.co/cristiancmoises/zupt
|
||||
tag: v2.1.6
|
||||
|
||||
- name: zupt-gui
|
||||
buildsystem: simple
|
||||
build-commands:
|
||||
- install -Dm755 src/zupt_gui.py /app/bin/zupt-gui
|
||||
- install -Dm644 packaging/zupt-gui.desktop /app/share/applications/dev.zupt.gui.desktop
|
||||
- make -j${FLATPAK_BUILDER_N_JOBS} WITH_SDK=0 WITH_PQBOX=0
|
||||
- make WITH_SDK=0 WITH_PQBOX=0 check
|
||||
- make PREFIX=/app WITH_SDK=0 WITH_PQBOX=0 INSTALL_LEGACY_ALIAS=0 install
|
||||
- install -Dm644 gui/src/zupt_gui.py /app/bin/zupt_gui.py
|
||||
- install -Dm755 packaging/portable/zupt-gui.sh /app/bin/zupt-gui
|
||||
- install -Dm644 gui/packaging/zupt-gui.desktop /app/share/applications/dev.zupt.gui.desktop
|
||||
- sed -i 's/^Icon=.*/Icon=dev.zupt.gui/' /app/share/applications/dev.zupt.gui.desktop
|
||||
- install -Dm644 gui/assets/zupt-icon.png /app/share/icons/hicolor/256x256/apps/dev.zupt.gui.png
|
||||
- install -Dm644 doc/zupt-gui.1 /app/share/man/man1/zupt-gui.1
|
||||
- install -d /app/share/licenses/zupt
|
||||
- install -m 0644 LICENSE LICENSE-AGPL-3.0 LICENSE-GPL-3.0 LICENSE-BSD-2-Clause LICENSE-BSD-3-Clause LICENSE-CC0-1.0 NOTICE THIRD-PARTY-NOTICES.md /app/share/licenses/zupt/
|
||||
- install -d /app/share/licenses/zupt-gui
|
||||
- install -m 0644 LICENSE-AGPL-3.0 /app/share/licenses/zupt-gui/LICENSE-AGPL-3.0
|
||||
- install -m 0644 gui/LICENSE-GUI /app/share/licenses/zupt-gui/LICENSE-GUI
|
||||
- install -m 0644 gui/assets/README.md /app/share/licenses/zupt-gui/ASSET-PROVENANCE.md
|
||||
sources:
|
||||
- type: dir
|
||||
path: .
|
||||
path: ../../..
|
||||
|
|
|
|||
|
|
@ -1,92 +1,102 @@
|
|||
@echo off
|
||||
REM ══════════════════════════════════════════════════
|
||||
REM Zupt GUI — Windows Build Script
|
||||
REM Creates: ZuptGUI-2.1.6-Setup.exe
|
||||
REM
|
||||
REM Prerequisites:
|
||||
REM 1. Python 3.9+ (python.org)
|
||||
REM 2. NSIS 3.x (nsis.sourceforge.io)
|
||||
REM 3. zupt.exe (compiled zupt CLI binary for Windows)
|
||||
REM
|
||||
REM Usage:
|
||||
REM cd packaging\windows
|
||||
REM build-windows.bat
|
||||
REM ══════════════════════════════════════════════════
|
||||
setlocal
|
||||
rem SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
rem Build the ZUPT GUI installer without downloading dependencies.
|
||||
rem
|
||||
rem Prerequisites must already be installed: Python 3.9+, PySide6, PyInstaller,
|
||||
rem Inno Setup 6, and a source-built/tested zupt.exe. The CLI path can be
|
||||
rem selected with ZUPT_CLI_EXE. Final output goes to ZUPT_DIST_DIR,
|
||||
rem which defaults to a directory below %%TEMP%% (outside the Git checkout).
|
||||
rem ZUPT_WINDOWS_RUNTIME_NOTICES_DIR is mandatory and must contain the
|
||||
rem license/notices for the exact Python, PyInstaller, Qt and PySide/PyQt
|
||||
rem runtime files embedded by this local build.
|
||||
|
||||
echo.
|
||||
echo Zupt GUI — Windows Build
|
||||
echo ════════════════════════
|
||||
echo.
|
||||
setlocal EnableExtensions
|
||||
for %%I in ("%~dp0\..\..\..") do set "REPO_ROOT=%%~fI"
|
||||
set "VERSION=%~1"
|
||||
if not defined VERSION set "VERSION=5.2.2"
|
||||
if not defined ZUPT_DIST_DIR set "ZUPT_DIST_DIR=%TEMP%\zupt-release"
|
||||
if not defined ZUPT_CLI_EXE set "ZUPT_CLI_EXE=%REPO_ROOT%\zupt.exe"
|
||||
set "WORK=%TEMP%\zupt-gui-build-%RANDOM%-%RANDOM%"
|
||||
set "RC=1"
|
||||
|
||||
REM ── Step 1: Install Python deps ──
|
||||
echo [1/4] Installing dependencies...
|
||||
pip install PySide6 pyinstaller --quiet --upgrade
|
||||
if errorlevel 1 (
|
||||
echo ERROR: pip install failed. Is Python in PATH?
|
||||
pause
|
||||
exit /b 1
|
||||
where pyinstaller >nul 2>nul || (
|
||||
echo ERROR: PyInstaller is required and is not downloaded by this script.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
where ISCC.exe >nul 2>nul || (
|
||||
echo ERROR: Inno Setup 6 ISCC.exe is required.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
if not exist "%ZUPT_CLI_EXE%" (
|
||||
echo ERROR: source-built CLI not found: %ZUPT_CLI_EXE%>&2
|
||||
goto :cleanup
|
||||
)
|
||||
if not defined ZUPT_WINDOWS_RUNTIME_NOTICES_DIR (
|
||||
echo ERROR: set ZUPT_WINDOWS_RUNTIME_NOTICES_DIR for the exact bundled runtime.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
if not exist "%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\MANIFEST.txt" (
|
||||
echo ERROR: runtime notice directory must contain MANIFEST.txt.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
for %%I in ("%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\MANIFEST.txt") do if %%~zI LEQ 0 (
|
||||
echo ERROR: runtime notice MANIFEST.txt must not be empty.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
for %%N in (PYTHON-NOTICE.txt PYINSTALLER-NOTICE.txt QT-NOTICE.txt) do (
|
||||
if not exist "%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N" (
|
||||
echo ERROR: runtime notice directory is missing %%N.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
for %%I in ("%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N") do if %%~zI LEQ 0 (
|
||||
echo ERROR: runtime notice %%N must not be empty.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
)
|
||||
set "QT_BINDING_NOTICE_FOUND="
|
||||
for %%N in (PYSIDE6-NOTICE.txt PYQT6-NOTICE.txt) do if exist "%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N" (
|
||||
for %%I in ("%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%\%%N") do if %%~zI GTR 0 set "QT_BINDING_NOTICE_FOUND=1"
|
||||
)
|
||||
if not defined QT_BINDING_NOTICE_FOUND (
|
||||
echo ERROR: runtime notices need non-empty PYSIDE6-NOTICE.txt or PYQT6-NOTICE.txt.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
|
||||
REM ── Step 2: Build .exe with PyInstaller ──
|
||||
echo [2/4] Building ZuptGUI.exe...
|
||||
if exist dist rmdir /s /q dist
|
||||
if exist build rmdir /s /q build
|
||||
mkdir "%WORK%" || goto :cleanup
|
||||
if not exist "%ZUPT_DIST_DIR%" mkdir "%ZUPT_DIST_DIR%" || goto :cleanup
|
||||
|
||||
pyinstaller --onefile --windowed ^
|
||||
--name "ZuptGUI" ^
|
||||
--icon "..\..\assets\zupt.ico" ^
|
||||
--add-data "..\..\assets\zupt.ico;assets" ^
|
||||
--add-data "..\..\assets\zupt.png;assets" ^
|
||||
"..\..\src\zupt_gui.py"
|
||||
|
||||
if not exist "dist\ZuptGUI.exe" (
|
||||
echo ERROR: PyInstaller build failed.
|
||||
pause
|
||||
exit /b 1
|
||||
"%ZUPT_CLI_EXE%" version >"%WORK%\cli-version.txt" 2>&1 || goto :cleanup
|
||||
findstr /b /c:"zupt %VERSION%" "%WORK%\cli-version.txt" >nul || (
|
||||
echo ERROR: CLI version does not match %VERSION%.>&2
|
||||
goto :cleanup
|
||||
)
|
||||
echo Built: dist\ZuptGUI.exe
|
||||
"%ZUPT_CLI_EXE%" help >nul 2>&1 || goto :cleanup
|
||||
|
||||
REM ── Step 3: Check for zupt.exe ──
|
||||
echo [3/4] Checking for zupt.exe...
|
||||
if not exist "zupt.exe" (
|
||||
echo.
|
||||
echo WARNING: zupt.exe not found in this directory.
|
||||
echo The installer needs zupt.exe to bundle the CLI tool.
|
||||
echo Options:
|
||||
echo a) Copy zupt.exe here and re-run this script
|
||||
echo b) Build zupt from source with MSYS2/MinGW:
|
||||
echo pacman -S mingw-w64-x86_64-gcc make
|
||||
echo cd zupt-2.1.6 ^&^& make
|
||||
echo cp zupt.exe packaging/windows/
|
||||
echo.
|
||||
)
|
||||
pyinstaller --noconfirm --clean --onefile --windowed ^
|
||||
--name zupt-gui ^
|
||||
--icon "%REPO_ROOT%\gui\assets\zupt.ico" ^
|
||||
--add-data "%REPO_ROOT%\gui\assets\zupt.ico;assets" ^
|
||||
--add-data "%REPO_ROOT%\gui\assets\zupt-icon.png;assets" ^
|
||||
--distpath "%WORK%\dist" ^
|
||||
--workpath "%WORK%\build" ^
|
||||
--specpath "%WORK%" ^
|
||||
"%REPO_ROOT%\gui\src\zupt_gui.py" || goto :cleanup
|
||||
|
||||
REM ── Step 4: Build NSIS installer ──
|
||||
echo [4/4] Building installer...
|
||||
where makensis >nul 2>&1
|
||||
if errorlevel 1 (
|
||||
echo.
|
||||
echo NSIS not found. Install from: https://nsis.sourceforge.io
|
||||
echo Then run: makensis zupt-installer.nsi
|
||||
echo.
|
||||
echo Standalone exe ready at: dist\ZuptGUI.exe
|
||||
pause
|
||||
exit /b 0
|
||||
)
|
||||
set "GUI_EXE=%WORK%\dist\zupt-gui.exe"
|
||||
if not exist "%GUI_EXE%" goto :cleanup
|
||||
set "ZUPT_BIN=%ZUPT_CLI_EXE%"
|
||||
"%GUI_EXE%" --version >"%WORK%\gui-version.txt" 2>&1 || goto :cleanup
|
||||
findstr /b /c:"zupt-gui %VERSION%" "%WORK%\gui-version.txt" >nul || goto :cleanup
|
||||
|
||||
makensis zupt-installer.nsi
|
||||
if errorlevel 1 (
|
||||
echo ERROR: NSIS build failed.
|
||||
pause
|
||||
exit /b 1
|
||||
)
|
||||
ISCC.exe "/DAppVersion=%VERSION%" "/DGuiExecutable=%GUI_EXE%" ^
|
||||
"/DCliExecutable=%ZUPT_CLI_EXE%" "/DBuildOutputDir=%ZUPT_DIST_DIR%" ^
|
||||
"/DRuntimeNoticesDir=%ZUPT_WINDOWS_RUNTIME_NOTICES_DIR%" ^
|
||||
"%REPO_ROOT%\packaging\windows\zupt-gui.iss" || goto :cleanup
|
||||
|
||||
echo.
|
||||
echo ════════════════════════════════════════════
|
||||
echo Build complete!
|
||||
echo Standalone: dist\ZuptGUI.exe
|
||||
echo Installer: ZuptGUI-2.1.6-Setup.exe
|
||||
echo ════════════════════════════════════════════
|
||||
echo.
|
||||
pause
|
||||
if not exist "%ZUPT_DIST_DIR%\ZUPT-Setup-%VERSION%.exe" goto :cleanup
|
||||
echo PASS: built %ZUPT_DIST_DIR%\ZUPT-Setup-%VERSION%.exe
|
||||
set "RC=0"
|
||||
|
||||
:cleanup
|
||||
if exist "%WORK%" rmdir /s /q "%WORK%"
|
||||
endlocal & exit /b %RC%
|
||||
|
|
|
|||
|
|
@ -1,132 +0,0 @@
|
|||
; Zupt GUI — NSIS Installer Script
|
||||
; Builds: ZuptGUI-Setup.exe
|
||||
;
|
||||
; Prerequisites on the build machine:
|
||||
; 1. NSIS 3.x installed (https://nsis.sourceforge.io)
|
||||
; 2. Run build-windows.bat first to create dist/ZuptGUI.exe
|
||||
; 3. Place zupt.exe in this directory
|
||||
; 4. Then: makensis zupt-installer.nsi
|
||||
|
||||
!include "MUI2.nsh"
|
||||
!include "FileFunc.nsh"
|
||||
|
||||
; ── Config ──
|
||||
!define APPNAME "Zupt"
|
||||
!define APPVERSION "2.1.6"
|
||||
!define GUIVERSION "1.0.0"
|
||||
!define PUBLISHER "Cristian Cezar Moises"
|
||||
!define HELPURL "https://github.com/cristiancmoises/zupt"
|
||||
!define EXE "ZuptGUI.exe"
|
||||
!define CLI "zupt.exe"
|
||||
|
||||
Name "${APPNAME} ${APPVERSION}"
|
||||
OutFile "ZuptGUI-${APPVERSION}-Setup.exe"
|
||||
InstallDir "$PROGRAMFILES\${APPNAME}"
|
||||
InstallDirRegKey HKLM "Software\${APPNAME}" "InstallDir"
|
||||
RequestExecutionLevel admin
|
||||
|
||||
; ── UI ──
|
||||
!define MUI_ICON "..\..\assets\zupt.ico"
|
||||
!define MUI_UNICON "..\..\assets\zupt.ico"
|
||||
!define MUI_ABORTWARNING
|
||||
!define MUI_WELCOMEPAGE_TITLE "Install ${APPNAME} ${APPVERSION}"
|
||||
!define MUI_WELCOMEPAGE_TEXT "Post-quantum backup compression with ML-KEM-768 + X25519 hybrid encryption.$\r$\n$\r$\nThis will install the Zupt GUI and CLI tools."
|
||||
|
||||
!insertmacro MUI_PAGE_WELCOME
|
||||
!insertmacro MUI_PAGE_LICENSE "..\..\LICENSE"
|
||||
!insertmacro MUI_PAGE_DIRECTORY
|
||||
!insertmacro MUI_PAGE_INSTFILES
|
||||
!insertmacro MUI_PAGE_FINISH
|
||||
|
||||
!insertmacro MUI_UNPAGE_CONFIRM
|
||||
!insertmacro MUI_UNPAGE_INSTFILES
|
||||
|
||||
!insertmacro MUI_LANGUAGE "English"
|
||||
|
||||
; ── Install ──
|
||||
Section "Install"
|
||||
SetOutPath $INSTDIR
|
||||
|
||||
; Copy files
|
||||
File "dist\${EXE}"
|
||||
File "${CLI}"
|
||||
File "..\..\assets\zupt.ico"
|
||||
File "..\..\LICENSE"
|
||||
File "..\..\README.md"
|
||||
|
||||
; Write uninstaller
|
||||
WriteUninstaller "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Start Menu
|
||||
CreateDirectory "$SMPROGRAMS\${APPNAME}"
|
||||
CreateShortcut "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk" "$INSTDIR\${EXE}" "" "$INSTDIR\zupt.ico"
|
||||
CreateShortcut "$SMPROGRAMS\${APPNAME}\Uninstall.lnk" "$INSTDIR\Uninstall.exe"
|
||||
|
||||
; Desktop shortcut
|
||||
CreateShortcut "$DESKTOP\${APPNAME}.lnk" "$INSTDIR\${EXE}" "" "$INSTDIR\zupt.ico"
|
||||
|
||||
; Add to PATH (so zupt.exe is available system-wide)
|
||||
EnVar::AddValue "PATH" "$INSTDIR"
|
||||
|
||||
; Register .zupt file association
|
||||
WriteRegStr HKCR ".zupt" "" "ZuptArchive"
|
||||
WriteRegStr HKCR "ZuptArchive" "" "Zupt Archive"
|
||||
WriteRegStr HKCR "ZuptArchive\DefaultIcon" "" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKCR "ZuptArchive\shell\open\command" "" '"$INSTDIR\${EXE}" --extract "%1"'
|
||||
WriteRegStr HKCR "ZuptArchive\shell\verify\command" "" '"$INSTDIR\${CLI}" test "%1"'
|
||||
WriteRegStr HKCR "ZuptArchive\shell\verify" "" "Verify Integrity"
|
||||
|
||||
; Right-click "Compress with Zupt" on any file
|
||||
WriteRegStr HKCR "*\shell\ZuptCompress" "" "Compress with Zupt"
|
||||
WriteRegStr HKCR "*\shell\ZuptCompress\Icon" "" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKCR "*\shell\ZuptCompress\command" "" '"$INSTDIR\${EXE}" --compress "%1"'
|
||||
|
||||
; Right-click on directories
|
||||
WriteRegStr HKCR "Directory\shell\ZuptCompress" "" "Compress with Zupt"
|
||||
WriteRegStr HKCR "Directory\shell\ZuptCompress\Icon" "" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKCR "Directory\shell\ZuptCompress\command" "" '"$INSTDIR\${EXE}" --compress "%1"'
|
||||
|
||||
; Add/Remove Programs entry
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayName" "${APPNAME} — Post-Quantum Backup"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "UninstallString" "$INSTDIR\Uninstall.exe"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayIcon" "$INSTDIR\zupt.ico"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "Publisher" "${PUBLISHER}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "DisplayVersion" "${APPVERSION}"
|
||||
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "URLInfoAbout" "${HELPURL}"
|
||||
|
||||
; Calculate installed size
|
||||
${GetSize} "$INSTDIR" "/S=0K" $0 $1 $2
|
||||
IntFmt $0 "0x%08X" $0
|
||||
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}" "EstimatedSize" "$0"
|
||||
SectionEnd
|
||||
|
||||
; ── Uninstall ──
|
||||
Section "Uninstall"
|
||||
; Remove files
|
||||
Delete "$INSTDIR\${EXE}"
|
||||
Delete "$INSTDIR\${CLI}"
|
||||
Delete "$INSTDIR\zupt.ico"
|
||||
Delete "$INSTDIR\LICENSE"
|
||||
Delete "$INSTDIR\README.md"
|
||||
Delete "$INSTDIR\Uninstall.exe"
|
||||
RMDir "$INSTDIR"
|
||||
|
||||
; Remove shortcuts
|
||||
Delete "$SMPROGRAMS\${APPNAME}\${APPNAME}.lnk"
|
||||
Delete "$SMPROGRAMS\${APPNAME}\Uninstall.lnk"
|
||||
RMDir "$SMPROGRAMS\${APPNAME}"
|
||||
Delete "$DESKTOP\${APPNAME}.lnk"
|
||||
|
||||
; Remove from PATH
|
||||
EnVar::DeleteValue "PATH" "$INSTDIR"
|
||||
|
||||
; Remove file associations
|
||||
DeleteRegKey HKCR ".zupt"
|
||||
DeleteRegKey HKCR "ZuptArchive"
|
||||
DeleteRegKey HKCR "*\shell\ZuptCompress"
|
||||
DeleteRegKey HKCR "Directory\shell\ZuptCompress"
|
||||
|
||||
; Remove Add/Remove Programs entry
|
||||
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
|
||||
DeleteRegKey HKLM "Software\${APPNAME}"
|
||||
SectionEnd
|
||||
|
|
@ -1,12 +1,13 @@
|
|||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
[Desktop Entry]
|
||||
Type=Application
|
||||
Name=Zupt GUI
|
||||
GenericName=Post-Quantum Backup
|
||||
Comment=Compress, encrypt, and backup with quantum-resistant cryptography
|
||||
Exec=zupt-gui
|
||||
Name=ZUPT GUI
|
||||
GenericName=Backup and Compression Utility
|
||||
Comment=Create, inspect, verify, and extract ZUPT archives
|
||||
Exec=zupt-gui %f
|
||||
Icon=zupt-gui
|
||||
Categories=Utility;Archiving;Security;
|
||||
Keywords=backup;compress;encrypt;quantum;zupt;
|
||||
Categories=Utility;Archiving;Compression;
|
||||
Keywords=backup;archive;compression;encryption;post-quantum;zupt;
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
MimeType=application/x-zupt;
|
||||
|
|
|
|||
34
gui/setup.py
34
gui/setup.py
|
|
@ -1,34 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
from setuptools import setup, find_packages
|
||||
|
||||
setup(
|
||||
name="zupt-gui",
|
||||
version="1.1.1",
|
||||
description="Zupt GUI — Cross-Platform Post-Quantum Backup Utility",
|
||||
long_description=open("README.md").read(),
|
||||
long_description_content_type="text/markdown",
|
||||
author="Cristian Cezar Moisés",
|
||||
url="https://git.securityops.co/cristiancmoises/zupt",
|
||||
license="AGPL-3.0-or-later",
|
||||
packages=find_packages(where="src"),
|
||||
package_dir={"": "src"},
|
||||
py_modules=["zupt_gui"],
|
||||
python_requires=">=3.9",
|
||||
install_requires=["PySide6>=6.5"],
|
||||
entry_points={
|
||||
"console_scripts": ["zupt-gui=zupt_gui:main"],
|
||||
"gui_scripts": ["zupt-gui=zupt_gui:main"],
|
||||
},
|
||||
classifiers=[
|
||||
"Development Status :: 4 - Beta",
|
||||
"Environment :: X11 Applications :: Qt",
|
||||
"Intended Audience :: End Users/Desktop",
|
||||
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python :: 3",
|
||||
"Topic :: Security :: Cryptography",
|
||||
"Topic :: System :: Archiving :: Compression",
|
||||
],
|
||||
)
|
||||
|
|
@ -1,10 +1,10 @@
|
|||
#!/usr/bin/env python3
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
# Copyright (c) 2025-2026 Cristian Cezar Moisés
|
||||
"""VaptVupt GUI — Cross-Platform Post-Quantum Backup.
|
||||
"""ZUPT GUI — Cross-platform post-quantum backup.
|
||||
|
||||
Renamed from "Zupt" in v3.0.0 due to INPI Brasil trademark.
|
||||
The .zupt file extension is preserved.
|
||||
The original ZUPT product name was restored in 5.2.2. The .zupt archive
|
||||
extension, format, codec identifiers, and compatibility remain unchanged.
|
||||
|
||||
Tries PySide6 first (preferred), falls back to PyQt6 if PySide6 is
|
||||
not installed. PyQt6 is the default available package on Debian/Ubuntu
|
||||
|
|
@ -40,32 +40,32 @@ except ImportError:
|
|||
except ImportError:
|
||||
if sys.stderr is not None: # None under PyInstaller --windowed
|
||||
sys.stderr.write(
|
||||
"ERROR: vaptvupt-gui requires PySide6 or PyQt6. Install one of:\n"
|
||||
"ERROR: zupt-gui requires PySide6 or PyQt6. Install one of:\n"
|
||||
" Debian/Ubuntu: sudo apt install python3-pyqt6\n"
|
||||
" Fedora/RHEL: sudo dnf install python3-pyqt6\n"
|
||||
" pip (any OS): pip install PySide6\n"
|
||||
)
|
||||
sys.exit(1)
|
||||
|
||||
# ── Find vaptvupt binary ──
|
||||
# ── Find the ZUPT binary ──
|
||||
#
|
||||
# v3.0.0 rename: the binary is now `vaptvupt`; older installations
|
||||
# (1.x/2.x) ship `zupt`. We try the new name first, fall back to the
|
||||
# old name, and on every candidate verify it's actually executable
|
||||
# ZUPT 5.2.2 restores `zupt` as the primary command. Renamed-era
|
||||
# installations may still provide `vaptvupt`, so discovery accepts it as a
|
||||
# compatibility fallback. Every candidate is verified as actually executable
|
||||
# (not just present). After picking a candidate, we run a quick
|
||||
# `version` liveness check — this catches the case where the binary
|
||||
# exists but can't load its shared library (the original bug report:
|
||||
# "GUI doesn't find zupt; copying to /usr/local/bin fixes it").
|
||||
#
|
||||
# Diagnostic output goes to stderr so users can `vaptvupt-gui 2>log`
|
||||
# Diagnostic output goes to stderr so users can `zupt-gui 2>log`
|
||||
# to see exactly which path was tried and why each failed.
|
||||
|
||||
_DISCOVERY_LOG = []
|
||||
|
||||
def _discovery_log(msg):
|
||||
_DISCOVERY_LOG.append(msg)
|
||||
# Echo to stderr if VAPTVUPT_DEBUG or ZUPT_DEBUG is set
|
||||
if ((os.environ.get("VAPTVUPT_DEBUG") or os.environ.get("ZUPT_DEBUG"))
|
||||
# Echo to stderr if ZUPT_DEBUG or its renamed-era alias is set.
|
||||
if ((os.environ.get("ZUPT_DEBUG") or os.environ.get("VAPTVUPT_DEBUG"))
|
||||
and sys.stderr is not None): # None under PyInstaller --windowed
|
||||
sys.stderr.write(f" [discovery] {msg}\n")
|
||||
|
||||
|
|
@ -91,9 +91,9 @@ def _is_runnable(path):
|
|||
except OSError as e:
|
||||
return False, f"OSError: {e}"
|
||||
|
||||
def _find_vaptvupt():
|
||||
def _find_zupt():
|
||||
# 1. Explicit env override
|
||||
for env in ("VAPTVUPT_BIN", "ZUPT_BIN"):
|
||||
for env in ("ZUPT_BIN", "VAPTVUPT_BIN"):
|
||||
p = os.environ.get(env)
|
||||
if p:
|
||||
ok, reason = _is_runnable(p)
|
||||
|
|
@ -102,18 +102,18 @@ def _find_vaptvupt():
|
|||
return p
|
||||
|
||||
# 2. Local project tree (running from a source checkout)
|
||||
# Try BOTH names (vaptvupt is v3.0.0+, zupt is legacy).
|
||||
# Prefer the canonical name, then the renamed-era compatibility name.
|
||||
here = Path(getattr(sys, "_MEIPASS", Path(__file__).parent))
|
||||
for parent in (here.parent.parent, here.parent, here):
|
||||
for name in ("vaptvupt", "zupt", "vaptvupt.exe", "zupt.exe"):
|
||||
for name in ("zupt", "vaptvupt", "zupt.exe", "vaptvupt.exe"):
|
||||
c = parent / name
|
||||
ok, reason = _is_runnable(c)
|
||||
_discovery_log(f"local {c}: {reason}")
|
||||
if ok:
|
||||
return str(c.resolve())
|
||||
|
||||
# 3. System PATH — try new name first, then legacy
|
||||
for name in ("vaptvupt", "zupt"):
|
||||
# 3. System PATH — try the canonical name first, then compatibility.
|
||||
for name in ("zupt", "vaptvupt"):
|
||||
found = shutil.which(name)
|
||||
if found:
|
||||
ok, reason = _is_runnable(found)
|
||||
|
|
@ -127,17 +127,17 @@ def _find_vaptvupt():
|
|||
# from a desktop session with a minimal PATH that omits /usr/bin"
|
||||
# scenario reported against v2.4.8.
|
||||
common = [
|
||||
# New name (v3.0.0+)
|
||||
"/usr/local/bin/vaptvupt", "/usr/bin/vaptvupt",
|
||||
"/opt/vaptvupt/bin/vaptvupt", "/opt/homebrew/bin/vaptvupt",
|
||||
# Legacy name (1.x/2.x)
|
||||
# Canonical name
|
||||
"/usr/local/bin/zupt", "/usr/bin/zupt",
|
||||
"/opt/zupt/bin/zupt", "/opt/homebrew/bin/zupt",
|
||||
# Renamed-era compatibility name
|
||||
"/usr/local/bin/vaptvupt", "/usr/bin/vaptvupt",
|
||||
"/opt/vaptvupt/bin/vaptvupt", "/opt/homebrew/bin/vaptvupt",
|
||||
# Termux (Android) install path
|
||||
"/data/data/com.termux/files/usr/bin/vaptvupt",
|
||||
"/data/data/com.termux/files/usr/bin/zupt",
|
||||
"/data/data/com.termux/files/usr/bin/vaptvupt",
|
||||
# Flatpak sandbox runtime path
|
||||
"/app/bin/vaptvupt", "/app/bin/zupt",
|
||||
"/app/bin/zupt", "/app/bin/vaptvupt",
|
||||
]
|
||||
for path in common:
|
||||
ok, reason = _is_runnable(path)
|
||||
|
|
@ -145,15 +145,13 @@ def _find_vaptvupt():
|
|||
if ok:
|
||||
return path
|
||||
|
||||
# 5. Last resort — return "vaptvupt" and let exec fail loudly later.
|
||||
# 5. Last resort — return "zupt" and let exec fail loudly later.
|
||||
# A caller-visible error is better than silently returning a path
|
||||
# that doesn't work.
|
||||
_discovery_log("FAILED: no runnable vaptvupt/zupt binary found")
|
||||
return "vaptvupt"
|
||||
_discovery_log("FAILED: no runnable zupt/vaptvupt binary found")
|
||||
return "zupt"
|
||||
|
||||
# Backward-compat: code elsewhere in this file still uses `ZUPT`.
|
||||
VAPTVUPT = _find_vaptvupt()
|
||||
ZUPT = VAPTVUPT # legacy alias used throughout the rest of zupt_gui.py
|
||||
ZUPT_CLI = _find_zupt()
|
||||
|
||||
# ── Query version ONCE at import (cached) ──
|
||||
#
|
||||
|
|
@ -174,11 +172,11 @@ ZUPT = VAPTVUPT # legacy alias used throughout the rest of zupt_gui.py
|
|||
_VERSION_RE = re.compile(r'^(?:vaptvupt|zupt)\s+(\d+\.\d+\.\d+(?:[._A-Za-z0-9-]*)?)')
|
||||
|
||||
def _get_version():
|
||||
short = "vaptvupt (not found)"
|
||||
short = "zupt (not found)"
|
||||
number = "?"
|
||||
full = ""
|
||||
try:
|
||||
r = subprocess.run([VAPTVUPT, "version"], capture_output=True, stdin=subprocess.DEVNULL, text=True, timeout=5)
|
||||
r = subprocess.run([ZUPT_CLI, "version"], capture_output=True, stdin=subprocess.DEVNULL, text=True, timeout=5)
|
||||
if r.returncode == 0:
|
||||
full = r.stdout.strip()
|
||||
lines = full.split("\n")
|
||||
|
|
@ -194,24 +192,30 @@ ZUPT_VER_SHORT, ZUPT_VER_NUMBER, ZUPT_VER_FULL = _get_version()
|
|||
|
||||
# ── Detect build capabilities from `version` (and `help` as fallback) ──
|
||||
#
|
||||
# The default build is SOURCE-ONLY: the libvuptsdk-backed modes (Argon2id
|
||||
# KDF, --pq-sdk, --pq-box) are absent and fail with exit 1. Offering them in
|
||||
# The default build is SOURCE-ONLY: the system-lib-backed modes (Argon2id,
|
||||
# --pq-sdk and --pq-box) are absent and fail with exit 1. Offering them in
|
||||
# the UI is the #1 reason "functions don't work". We detect what THIS binary
|
||||
# actually supports and build the encryption UI around it:
|
||||
# - SDK_AVAILABLE : --pq-sdk / --pq-box / Argon2id compiled in (WITH_SDK=1)
|
||||
# - SDK_AVAILABLE : --pq-sdk / Argon2id compiled in (WITH_SDK=1)
|
||||
# - PQBOX_AVAILABLE: --pq-box compiled in (WITH_PQBOX=1)
|
||||
# - PQONLY_AVAILABLE: native --pq-only (full post-quantum, v4.2.0+)
|
||||
# - DEFAULT_KDF : the password KDF this build actually uses
|
||||
# The `version` banner carries a machine-readable "Build:" line (v4.2.1+);
|
||||
# The `version` banner carries a machine-readable "Build integrations:" line;
|
||||
# for older binaries we fall back to `help` text and default SDK off (safe:
|
||||
# the native --pq / --pq-only / password modes work on every build).
|
||||
def _get_caps():
|
||||
sdk = False
|
||||
pqbox = False
|
||||
pqonly = False
|
||||
default_kdf = "PBKDF2-SHA256"
|
||||
blob = ZUPT_VER_FULL or ""
|
||||
for line in blob.splitlines():
|
||||
low = line.lower()
|
||||
if low.startswith("build:"):
|
||||
if low.startswith("build integrations:"):
|
||||
sdk = "libvuptsdk=enabled" in low
|
||||
pqbox = "libpqvaptvupt=enabled" in low
|
||||
elif low.startswith("build:"):
|
||||
# Compatibility with the pre-5.2.2 combined build banner.
|
||||
sdk = ("full" in low) and ("vuptsdk" in low)
|
||||
elif low.startswith("kdf:"):
|
||||
default_kdf = "Argon2id" if "argon2id (default)" in low else "PBKDF2-SHA256"
|
||||
|
|
@ -219,15 +223,15 @@ def _get_caps():
|
|||
pqonly = True
|
||||
if not pqonly:
|
||||
try:
|
||||
h = subprocess.run([VAPTVUPT, "help"], capture_output=True, stdin=subprocess.DEVNULL, text=True, timeout=5)
|
||||
h = subprocess.run([ZUPT_CLI, "help"], capture_output=True, stdin=subprocess.DEVNULL, text=True, timeout=5)
|
||||
txt = (h.stdout or "") + (h.stderr or "")
|
||||
if "--pq-only" in txt:
|
||||
pqonly = True
|
||||
except Exception:
|
||||
pass
|
||||
return sdk, pqonly, default_kdf
|
||||
return sdk, pqbox, pqonly, default_kdf
|
||||
|
||||
SDK_AVAILABLE, PQONLY_AVAILABLE, DEFAULT_KDF = _get_caps()
|
||||
SDK_AVAILABLE, PQBOX_AVAILABLE, PQONLY_AVAILABLE, DEFAULT_KDF = _get_caps()
|
||||
|
||||
# Post-quantum recipient modes offered in the UI, keyed to CLI flags.
|
||||
# token -> (label, keygen-flag-list, compress/extract-flag)
|
||||
|
|
@ -242,6 +246,8 @@ def pq_mode_options(include_auto=False):
|
|||
opts.append(("Full PQ — ML-KEM-768 only", "pqonly"))
|
||||
if SDK_AVAILABLE:
|
||||
opts.append(("SDK v2 — HKDF + commitment + HPKE", "sdk"))
|
||||
if PQBOX_AVAILABLE:
|
||||
opts.append(("PQ sealed box — system libpqvaptvupt", "box"))
|
||||
return opts
|
||||
|
||||
# token -> (extra keygen flags, encrypt/decrypt flag)
|
||||
|
|
@ -249,12 +255,13 @@ _PQ_FLAG = {
|
|||
"pq": ([], "--pq"),
|
||||
"pqonly": (["--pq-only"], "--pq-only"),
|
||||
"sdk": (["--sdk"], "--pq-sdk"),
|
||||
"box": (["--box"], "--pq-box"),
|
||||
}
|
||||
|
||||
def _archive_info_text(archive):
|
||||
"""Return the `info` output for an archive (no password/key needed), or ""."""
|
||||
try:
|
||||
r = subprocess.run([VAPTVUPT, "info", archive], capture_output=True,
|
||||
r = subprocess.run([ZUPT_CLI, "info", archive], capture_output=True,
|
||||
stdin=subprocess.DEVNULL, text=True, timeout=15)
|
||||
return (r.stdout or "") + (r.stderr or "")
|
||||
except Exception:
|
||||
|
|
@ -263,6 +270,8 @@ def _archive_info_text(archive):
|
|||
def _detect_archive_pq(archive):
|
||||
"""Inspect an archive's `info` and return the matching PQ token, or None."""
|
||||
low = _archive_info_text(archive).lower()
|
||||
if "pq box" in low or "pq-box" in low or "sealed box" in low or "sealed-box" in low:
|
||||
return "box"
|
||||
if "ml-kem-768 only" in low or "no classical" in low:
|
||||
return "pqonly"
|
||||
if "sdk v2" in low or "hpke" in low:
|
||||
|
|
@ -274,7 +283,7 @@ def _detect_archive_pq(archive):
|
|||
def _detect_archive_enc(archive):
|
||||
"""Detect how an archive is protected, reading only its header (`info`, no
|
||||
credential). Returns (kind, human_label):
|
||||
kind: "none" | "password" | "pq" | "pqonly" | "sdk" | "unknown"
|
||||
kind: "none" | "password" | "pq" | "pqonly" | "sdk" | "box" | "unknown"
|
||||
Used to guide the user (which credential to supply) and to pick the right
|
||||
decrypt flag automatically instead of relying on a mode dropdown."""
|
||||
txt = _archive_info_text(archive)
|
||||
|
|
@ -289,6 +298,8 @@ def _detect_archive_enc(archive):
|
|||
break
|
||||
if encrypted is False:
|
||||
return "none", "not encrypted"
|
||||
if "pq box" in low or "pq-box" in low or "sealed box" in low or "sealed-box" in low:
|
||||
return "box", "PQ sealed box (system libpqvaptvupt)"
|
||||
if "ml-kem-768 only" in low or "no classical" in low:
|
||||
return "pqonly", "full post-quantum (ML-KEM-768)"
|
||||
if "sdk v2" in low or "hpke" in low:
|
||||
|
|
@ -362,10 +373,10 @@ QFrame#sep { background: #1a2a30; max-height: 1px; }
|
|||
|
||||
def run_zupt(args, timeout=30):
|
||||
try:
|
||||
r = subprocess.run([ZUPT]+list(args), capture_output=True, text=True,
|
||||
r = subprocess.run([ZUPT_CLI]+list(args), capture_output=True, text=True,
|
||||
stdin=subprocess.DEVNULL, timeout=timeout)
|
||||
return r.returncode, r.stdout, r.stderr
|
||||
except FileNotFoundError: return -1, "", f"vaptvupt not found: {VAPTVUPT}\n\nDiscovery log:\n" + "\n".join(_DISCOVERY_LOG[-10:])
|
||||
except FileNotFoundError: return -1, "", f"zupt not found: {ZUPT_CLI}\n\nDiscovery log:\n" + "\n".join(_DISCOVERY_LOG[-10:])
|
||||
except subprocess.TimeoutExpired: return -1, "", "Timed out"
|
||||
|
||||
class Worker(QObject):
|
||||
|
|
@ -382,7 +393,7 @@ class Worker(QObject):
|
|||
def __init__(self, args):
|
||||
super().__init__(); self.args = args; self.proc = None; self._cancelled = False
|
||||
def run(self):
|
||||
self.log.emit(f"$ {Path(VAPTVUPT).name} {' '.join(self.args)}")
|
||||
self.log.emit(f"$ {Path(ZUPT_CLI).name} {' '.join(self.args)}")
|
||||
try:
|
||||
# stdin=DEVNULL: the CLI prompts on a terminal for some inputs
|
||||
# (e.g. bare -p); a child that reads stdin inherited from the GUI's
|
||||
|
|
@ -391,7 +402,7 @@ class Worker(QObject):
|
|||
# (the CLI's human output is on stderr; stdout is empty) — no
|
||||
# second pipe that could fill while we drain the first.
|
||||
self.proc = proc = subprocess.Popen(
|
||||
[ZUPT]+self.args, stdout=subprocess.PIPE,
|
||||
[ZUPT_CLI]+self.args, stdout=subprocess.PIPE,
|
||||
stderr=subprocess.STDOUT, stdin=subprocess.DEVNULL)
|
||||
if self._cancelled: # cancel() ran before Popen finished (see below)
|
||||
proc.kill()
|
||||
|
|
@ -424,7 +435,7 @@ class Worker(QObject):
|
|||
if buf.strip() and not self._PCT_RE.search(buf):
|
||||
lines.append(buf); self.log.emit(buf)
|
||||
self.done.emit(proc.returncode, "", "\n".join(lines))
|
||||
except FileNotFoundError: self.done.emit(-1, "", f"vaptvupt not found: {VAPTVUPT}")
|
||||
except FileNotFoundError: self.done.emit(-1, "", f"zupt not found: {ZUPT_CLI}")
|
||||
except subprocess.TimeoutExpired: proc.kill(); self.done.emit(-1, "", "Timed out")
|
||||
except Exception as exc:
|
||||
# Any escape from this slot would strand the job forever (done never
|
||||
|
|
@ -611,10 +622,10 @@ class KeysTab(QWidget):
|
|||
v.addWidget(QLabel("Writes a private key and its matching public key."))
|
||||
|
||||
v.addWidget(H("Private key output"))
|
||||
self.gen_priv = PathField("e.g. ~/vaptvupt_private.key", "save", "Key (*.key);;All (*)")
|
||||
self.gen_priv = PathField("e.g. ~/zupt_private.key", "save", "Key (*.key);;All (*)")
|
||||
v.addWidget(self.gen_priv)
|
||||
v.addWidget(H("Public key output"))
|
||||
self.gen_pub = PathField("e.g. ~/vaptvupt_public.key", "save", "Key (*.key);;All (*)")
|
||||
self.gen_pub = PathField("e.g. ~/zupt_public.key", "save", "Key (*.key);;All (*)")
|
||||
v.addWidget(self.gen_pub)
|
||||
|
||||
self.gen_btn = QPushButton("Generate Keypair")
|
||||
|
|
@ -634,7 +645,7 @@ class KeysTab(QWidget):
|
|||
v.addWidget(self.exp_priv)
|
||||
|
||||
v.addWidget(H("Public key output"))
|
||||
self.exp_pub = PathField("e.g. ~/vaptvupt_public.key", "save", "Key (*.key);;All (*)")
|
||||
self.exp_pub = PathField("e.g. ~/zupt_public.key", "save", "Key (*.key);;All (*)")
|
||||
v.addWidget(self.exp_pub)
|
||||
|
||||
self.exp_btn = QPushButton("Export Public Key")
|
||||
|
|
@ -653,7 +664,7 @@ class KeysTab(QWidget):
|
|||
return (priv.rsplit(".", 1)[0] + "_public.key") if "." in priv else priv + ".pub"
|
||||
|
||||
def _generate(self):
|
||||
p = self.gen_priv.path() or str(Path.home() / "vaptvupt_private.key")
|
||||
p = self.gen_priv.path() or str(Path.home() / "zupt_private.key")
|
||||
self.gen_priv.edit.setText(p)
|
||||
pub = self.gen_pub.path() or self._default_pub(p)
|
||||
self.gen_pub.edit.setText(pub)
|
||||
|
|
@ -679,7 +690,7 @@ class KeysTab(QWidget):
|
|||
def _export(self):
|
||||
priv = self.exp_priv.path()
|
||||
pub = self.exp_pub.path()
|
||||
if not priv: QMessageBox.warning(self, "VaptVupt", "Select the private key file."); return
|
||||
if not priv: QMessageBox.warning(self, "ZUPT", "Select the private key file."); return
|
||||
if not pub:
|
||||
pub = self._default_pub(priv); self.exp_pub.edit.setText(pub)
|
||||
tok = self._token()
|
||||
|
|
@ -701,7 +712,7 @@ class CompressTab(QWidget):
|
|||
v.addWidget(H("Source files / directory"))
|
||||
self.src = PathField("Drop files here or browse", "multi"); v.addWidget(self.src)
|
||||
v.addWidget(H("Output archive"))
|
||||
self.dst = PathField("e.g. backup.zupt", "save", "VaptVupt archive (*.zupt);;All (*)"); v.addWidget(self.dst)
|
||||
self.dst = PathField("e.g. backup.zupt", "save", "ZUPT archive (*.zupt);;All (*)"); v.addWidget(self.dst)
|
||||
row = QHBoxLayout(); row.setSpacing(16)
|
||||
for label, widget in [("Codec", self._mk_codec()), ("Level", self._mk_level())]:
|
||||
c = QVBoxLayout(); c.addWidget(H(label)); c.addWidget(widget); row.addLayout(c)
|
||||
|
|
@ -735,7 +746,7 @@ class CompressTab(QWidget):
|
|||
|
||||
def _run(self):
|
||||
srcs = self.src.paths()
|
||||
if not srcs or not srcs[0]: QMessageBox.warning(self, "VaptVupt", "Select files."); return
|
||||
if not srcs or not srcs[0]: QMessageBox.warning(self, "ZUPT", "Select files."); return
|
||||
dst = self.dst.path() or srcs[0] + ".zupt"; self.dst.edit.setText(dst)
|
||||
cmd = ["compress", "-l", str(self.level.value())]
|
||||
cm = {"AUTO": None, "VaptVupt": "--vv", "LZHP": "--lzhp", "Store": "-s"}
|
||||
|
|
@ -758,7 +769,7 @@ class ExtractTab(QWidget):
|
|||
v = QVBoxLayout(inner); v.setContentsMargins(24,24,24,24); v.setSpacing(10)
|
||||
v.addWidget(QLabel("Extract and decrypt a .zupt archive."))
|
||||
v.addWidget(Sep())
|
||||
v.addWidget(H("Archive")); self.arc = PathField("Drop .zupt here", filters="VaptVupt archive (*.zupt);;All (*)"); v.addWidget(self.arc)
|
||||
v.addWidget(H("Archive")); self.arc = PathField("Drop .zupt here", filters="ZUPT archive (*.zupt);;All (*)"); v.addWidget(self.arc)
|
||||
v.addWidget(H("Output directory")); self.out = PathField("Same as archive", "dir"); v.addWidget(self.out)
|
||||
enc = QHBoxLayout(); enc.setSpacing(16)
|
||||
pw = QVBoxLayout(); pw.addWidget(H("Password")); self.pw = PwField(); pw.addWidget(self.pw); enc.addLayout(pw)
|
||||
|
|
@ -768,7 +779,7 @@ class ExtractTab(QWidget):
|
|||
self._pqmodes = pq_mode_options(include_auto=True)
|
||||
for label, _tok in self._pqmodes:
|
||||
self.pqmode.addItem(label)
|
||||
self.pqmode.setToolTip("Auto-detect reads the archive header (vaptvupt info) to pick the\n"
|
||||
self.pqmode.setToolTip("Auto-detect reads the archive header (zupt info) to pick the\n"
|
||||
"right mode. Or choose it explicitly to match your private key.")
|
||||
mode_box.addWidget(self.pqmode); mode_box.addStretch(); enc.addLayout(mode_box)
|
||||
v.addLayout(enc)
|
||||
|
|
@ -780,7 +791,7 @@ class ExtractTab(QWidget):
|
|||
|
||||
def _run(self):
|
||||
arc = self.arc.path()
|
||||
if not arc: QMessageBox.warning(self, "VaptVupt", "Select an archive."); return
|
||||
if not arc: QMessageBox.warning(self, "ZUPT", "Select an archive."); return
|
||||
if not os.path.isfile(arc):
|
||||
self.log.clear(); self.log.append(f"No such file: {arc}"); return
|
||||
# Read the header (no credential) so we can guide the user instead of
|
||||
|
|
@ -791,7 +802,7 @@ class ExtractTab(QWidget):
|
|||
self.log.append("This archive is password-encrypted.\n"
|
||||
"Enter the password above, then click Extract again.")
|
||||
return
|
||||
if kind in ("pq", "pqonly", "sdk") and not self.pq.path():
|
||||
if kind in ("pq", "pqonly", "sdk", "box") and not self.pq.path():
|
||||
self.log.clear()
|
||||
self.log.append(f"This archive uses {label} encryption.\n"
|
||||
"Select the matching private key above, then click Extract again.")
|
||||
|
|
@ -803,7 +814,7 @@ class ExtractTab(QWidget):
|
|||
if self.pq.path():
|
||||
# Prefer the header-detected mode; fall back to the dropdown for an
|
||||
# unreadable header. Auto-detect can't pick the wrong flag this way.
|
||||
tok = kind if kind in ("pq", "pqonly", "sdk") else self._pqmodes[self.pqmode.currentIndex()][1]
|
||||
tok = kind if kind in ("pq", "pqonly", "sdk", "box") else self._pqmodes[self.pqmode.currentIndex()][1]
|
||||
if tok == "auto":
|
||||
tok = _detect_archive_pq(arc) or "pq"
|
||||
_, flag = _PQ_FLAG[tok]
|
||||
|
|
@ -822,7 +833,7 @@ class VerifyTab(QWidget):
|
|||
v.addWidget(QLabel("Verify checksums or inspect archive metadata."))
|
||||
v.addWidget(Sep())
|
||||
v.addWidget(H("Verify integrity"))
|
||||
self.varc = PathField("Archive to verify", filters="VaptVupt archive (*.zupt);;All (*)"); v.addWidget(self.varc)
|
||||
self.varc = PathField("Archive to verify", filters="ZUPT archive (*.zupt);;All (*)"); v.addWidget(self.varc)
|
||||
enc = QHBoxLayout(); enc.setSpacing(16)
|
||||
pw = QVBoxLayout(); pw.addWidget(H("Password (if encrypted)")); self.vpw = PwField("Leave empty if not encrypted"); pw.addWidget(self.vpw); enc.addLayout(pw)
|
||||
pq = QVBoxLayout(); pq.addWidget(H("PQ private key (if post-quantum)")); self.vpq = PathField("Auto-detected; needed for --pq / --pq-only archives", filters="Key (*.key);;All (*)"); pq.addWidget(self.vpq); enc.addLayout(pq)
|
||||
|
|
@ -836,7 +847,7 @@ class VerifyTab(QWidget):
|
|||
self.vlog = Log(120); v.addWidget(self.vlog)
|
||||
v.addWidget(Sep())
|
||||
v.addWidget(H("Archive info (no password needed)"))
|
||||
self.iarc = PathField("Archive to inspect", filters="VaptVupt archive (*.zupt);;All (*)"); v.addWidget(self.iarc)
|
||||
self.iarc = PathField("Archive to inspect", filters="ZUPT archive (*.zupt);;All (*)"); v.addWidget(self.iarc)
|
||||
self.ibtn = QPushButton("Show Info"); self.ibtn.clicked.connect(self._info); v.addWidget(self.ibtn)
|
||||
self.ilog = Log(140); v.addWidget(self.ilog); v.addStretch()
|
||||
lay = QVBoxLayout(self); lay.setContentsMargins(0,0,0,0); lay.addWidget(scrollable(inner))
|
||||
|
|
@ -844,7 +855,7 @@ class VerifyTab(QWidget):
|
|||
def _verify(self):
|
||||
arc = self.varc.path()
|
||||
if not arc:
|
||||
QMessageBox.warning(self, "VaptVupt", "Select an archive to verify."); return
|
||||
QMessageBox.warning(self, "ZUPT", "Select an archive to verify."); return
|
||||
if not os.path.isfile(arc):
|
||||
self.vlog.clear(); self.vlog.append(f"No such file: {arc}"); return
|
||||
self.vlog.clear()
|
||||
|
|
@ -860,7 +871,7 @@ class VerifyTab(QWidget):
|
|||
"Enter the password above, then click Verify again.")
|
||||
return
|
||||
cmd += ["-p", self.vpw.text()]
|
||||
elif kind in ("pq", "pqonly", "sdk"):
|
||||
elif kind in ("pq", "pqonly", "sdk", "box"):
|
||||
if not self.vpq.path():
|
||||
self.vlog.append(f"This archive uses {label} encryption.\n"
|
||||
"Select the matching private key above, then click Verify again.")
|
||||
|
|
@ -899,7 +910,7 @@ class DiskTab(QWidget):
|
|||
v.addWidget(H("Backup — source device or image"))
|
||||
self.bsrc = PathField("/dev/sdX or disk.img"); v.addWidget(self.bsrc)
|
||||
v.addWidget(H("Backup — output archive"))
|
||||
self.bout = PathField("backup.zupt", "save", "VaptVupt archive (*.zupt);;All (*)"); v.addWidget(self.bout)
|
||||
self.bout = PathField("backup.zupt", "save", "ZUPT archive (*.zupt);;All (*)"); v.addWidget(self.bout)
|
||||
bopt = QHBoxLayout(); bopt.setSpacing(16)
|
||||
oc = QVBoxLayout(); oc.addWidget(H("Options")); self.bdedup = QCheckBox("Block deduplication"); oc.addWidget(self.bdedup); bopt.addLayout(oc)
|
||||
pc = QVBoxLayout(); pc.addWidget(H("Password")); self.bpw = PwField("Optional — AES-256"); pc.addWidget(self.bpw); bopt.addLayout(pc)
|
||||
|
|
@ -908,7 +919,7 @@ class DiskTab(QWidget):
|
|||
self.blog = Log(100); v.addWidget(self.blog)
|
||||
v.addWidget(Sep())
|
||||
v.addWidget(H("Restore — archive"))
|
||||
self.rarc = PathField("backup.zupt", filters="VaptVupt archive (*.zupt);;All (*)"); v.addWidget(self.rarc)
|
||||
self.rarc = PathField("backup.zupt", filters="ZUPT archive (*.zupt);;All (*)"); v.addWidget(self.rarc)
|
||||
v.addWidget(H("Restore — target device or file"))
|
||||
self.rtgt = PathField("/dev/sdX or output.img", "save"); v.addWidget(self.rtgt)
|
||||
v.addWidget(H("Restore — password"))
|
||||
|
|
@ -919,7 +930,7 @@ class DiskTab(QWidget):
|
|||
|
||||
def _backup(self):
|
||||
s, o = self.bsrc.path(), self.bout.path()
|
||||
if not s or not o: QMessageBox.warning(self, "VaptVupt", "Set source and output."); return
|
||||
if not s or not o: QMessageBox.warning(self, "ZUPT", "Set source and output."); return
|
||||
cmd = ["disk", "backup"]
|
||||
if self.bdedup.isChecked(): cmd.append("--dedup")
|
||||
if self.bpw.text(): cmd += ["-p", self.bpw.text()]
|
||||
|
|
@ -927,7 +938,7 @@ class DiskTab(QWidget):
|
|||
|
||||
def _restore(self):
|
||||
a, t = self.rarc.path(), self.rtgt.path()
|
||||
if not a or not t: QMessageBox.warning(self, "VaptVupt", "Set archive and target."); return
|
||||
if not a or not t: QMessageBox.warning(self, "ZUPT", "Set archive and target."); return
|
||||
SB = QMessageBox.StandardButton
|
||||
if QMessageBox.warning(self, "Confirm", f"OVERWRITE {t}?", SB.Yes|SB.Cancel) != SB.Yes: return
|
||||
cmd = ["disk", "restore"]
|
||||
|
|
@ -941,14 +952,14 @@ class AboutTab(QWidget):
|
|||
inner = QWidget()
|
||||
v = QVBoxLayout(inner); v.setContentsMargins(24,24,24,24); v.setSpacing(4)
|
||||
for text, style in [
|
||||
("VAPTVUPT", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
|
||||
("ZUPT", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
|
||||
(ZUPT_VER_NUMBER, "color:white;font-size:28px;font-weight:800;font-family:monospace;"),
|
||||
("", ""),
|
||||
("Post-quantum backup compression with ML-KEM-768: --pq hybrid", "color:#6a8898;font-size:13px;"),
|
||||
(f"(+ X25519) or --pq-only (pure). {DEFAULT_KDF} password KDF,", "color:#6a8898;font-size:13px;"),
|
||||
("block deduplication, and full-disk backup.", "color:#6a8898;font-size:13px;"),
|
||||
("Renamed from Zupt in v3.0.0 (INPI Brasil trademark); .zupt", "color:#6a8898;font-size:13px;"),
|
||||
("archive extension and v1.6 wire format are unchanged.", "color:#6a8898;font-size:13px;"),
|
||||
("Original ZUPT name restored in 5.2.2; the .zupt extension", "color:#6a8898;font-size:13px;"),
|
||||
("and v1.6 version byte remain; 5.2.2 adds flag-gated records.", "color:#6a8898;font-size:13px;"),
|
||||
("", ""),
|
||||
("CRYPTOGRAPHIC STACK", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
|
||||
("ML-KEM-768 FIPS 203 Post-Quantum KEM", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
|
|
@ -963,22 +974,21 @@ class AboutTab(QWidget):
|
|||
("XXH64 (non-crypto) Per-block checksum (inside AEAD)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("", ""),
|
||||
("COMPRESSION CODEC", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
|
||||
("VaptVupt LZ + ANS 2.60.4 LZ77 + tabled ANS entropy", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("AVX2 / NEON SIMD acceleration; CBMC-verified BCJ filters", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("VaptVupt LZ + ANS 2.65.3 LZ77 + tabled ANS entropy", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("AVX2 / NEON SIMD acceleration; portable scalar fallbacks", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("", ""),
|
||||
("CREDITS", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
|
||||
("VaptVupt application Cristian Cezar Moisés", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
(" License: AGPL-3.0-or-later (commercial license available)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
(" git.securityops.co/cristiancmoises/vaptvupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
|
||||
("ZUPT application Cristian Cezar Moisés", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
(" License: AGPL-3.0-or-later (commercial terms may be available)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
(" github.com/cristiancmoises/zupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
|
||||
("", ""),
|
||||
("VaptVupt LZ + ANS codec Cristian Cezar Moisés", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
(" License: GPL-3.0-or-later (commercial license available)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
(" git.securityops.co/cristiancmoises/vaptvupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
|
||||
(" License: GPL-3.0-or-later (commercial terms may be available)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
(" github.com/cristiancmoises/zupt", "color:#3a5868;font-size:11px;font-family:monospace;"),
|
||||
("", ""),
|
||||
("WEBSITE & CONTACT", "color:#00dde0;font-size:10px;font-weight:700;letter-spacing:2px;font-family:monospace;"),
|
||||
("https://zupt.securityops.co", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("sac@securityops.co (commercial licensing)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("zupt@riseup.net (general / bugs)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("https://github.com/cristiancmoises/zupt", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("sac@securityops.co (commercial-terms inquiries)", "color:#5a7a88;font-size:12px;font-family:monospace;"),
|
||||
("", ""),
|
||||
(ZUPT_VER_SHORT, "color:#3a5868;font-size:11px;font-family:monospace;"),
|
||||
]:
|
||||
|
|
@ -995,7 +1005,7 @@ class AboutTab(QWidget):
|
|||
class ZuptWindow(QMainWindow):
|
||||
def __init__(self, compress_files=None, extract_file=None):
|
||||
super().__init__()
|
||||
self.setWindowTitle(f"VaptVupt {ZUPT_VER_NUMBER}")
|
||||
self.setWindowTitle(f"ZUPT {ZUPT_VER_NUMBER}")
|
||||
self.setMinimumSize(720, 500)
|
||||
self.resize(880, 640)
|
||||
self.setAcceptDrops(True)
|
||||
|
|
@ -1010,7 +1020,7 @@ class ZuptWindow(QMainWindow):
|
|||
# Header
|
||||
hdr = QFrame(); hdr.setStyleSheet("background:#050a0e;border-bottom:1px solid #1a2a30;")
|
||||
hl = QHBoxLayout(hdr); hl.setContentsMargins(20,10,20,10)
|
||||
title = QLabel("VAPTVUPT"); title.setStyleSheet("color:white;font-size:15px;font-weight:800;letter-spacing:3px;")
|
||||
title = QLabel("ZUPT"); title.setStyleSheet("color:white;font-size:15px;font-weight:800;letter-spacing:3px;")
|
||||
hl.addWidget(title)
|
||||
sub = QLabel("Post-Quantum Backup"); sub.setStyleSheet("color:#3a5868;font-size:10px;font-weight:600;letter-spacing:1px;margin-left:8px;")
|
||||
hl.addWidget(sub); hl.addStretch()
|
||||
|
|
@ -1033,7 +1043,7 @@ class ZuptWindow(QMainWindow):
|
|||
layout.addWidget(self.tabs)
|
||||
|
||||
sb = QStatusBar()
|
||||
sb.showMessage(f"VaptVupt {ZUPT_VER_NUMBER} | {VAPTVUPT}")
|
||||
sb.showMessage(f"ZUPT {ZUPT_VER_NUMBER} | {ZUPT_CLI}")
|
||||
self.setStatusBar(sb)
|
||||
|
||||
def dragEnterEvent(self, e):
|
||||
|
|
@ -1058,7 +1068,7 @@ class ZuptWindow(QMainWindow):
|
|||
# leaves the target half-written), so never do it silently.
|
||||
SB = QMessageBox.StandardButton
|
||||
if QMessageBox.warning(
|
||||
self, "VaptVupt",
|
||||
self, "ZUPT",
|
||||
"An operation is still running.\nQuit and abort it?",
|
||||
SB.Yes | SB.Cancel) != SB.Yes:
|
||||
e.ignore(); return
|
||||
|
|
@ -1081,19 +1091,19 @@ class ZuptWindow(QMainWindow):
|
|||
def main():
|
||||
args = sys.argv[1:]
|
||||
|
||||
# Lightweight non-GUI flags first, so `vaptvupt-gui --version|--help|--selftest`
|
||||
# Lightweight non-GUI flags first, so `zupt-gui --version|--help|--selftest`
|
||||
# work with no display and aren't mistaken for files to compress. `--selftest`
|
||||
# is a headless-friendly smoke test: it builds the whole UI and spins the event
|
||||
# loop once, then exits 0 — the reliable way to confirm the GUI stack launches
|
||||
# on a machine where the window itself is hard to see (tiling WM, remote, CI).
|
||||
if args and args[0] in ("--version", "-V", "version"):
|
||||
print(f"vaptvupt-gui {ZUPT_VER_NUMBER} ({QT_BINDING}) | CLI: {VAPTVUPT}")
|
||||
print(f"zupt-gui {ZUPT_VER_NUMBER} ({QT_BINDING}) | CLI: {ZUPT_CLI}")
|
||||
return 0
|
||||
if args and args[0] in ("--help", "-h", "help"):
|
||||
print("usage: vaptvupt-gui [ARCHIVE.zupt | --extract ARCHIVE.zupt |\n"
|
||||
print("usage: zupt-gui [ARCHIVE.zupt | --extract ARCHIVE.zupt |\n"
|
||||
" --compress FILE [FILE ...]]\n"
|
||||
" vaptvupt-gui --selftest # verify the GUI launches (no window kept)\n"
|
||||
" vaptvupt-gui --version")
|
||||
" zupt-gui --selftest # verify the GUI launches (no window kept)\n"
|
||||
" zupt-gui --version")
|
||||
return 0
|
||||
|
||||
compress_files = extract_file = None
|
||||
|
|
@ -1105,7 +1115,7 @@ def main():
|
|||
else: compress_files = args
|
||||
|
||||
app = QApplication(sys.argv)
|
||||
app.setApplicationName("VaptVupt")
|
||||
app.setApplicationName("ZUPT")
|
||||
if ICON_PATH: app.setWindowIcon(QIcon(ICON_PATH))
|
||||
app.setStyle("Fusion")
|
||||
app.setStyleSheet(STYLE)
|
||||
|
|
@ -1126,7 +1136,7 @@ def main():
|
|||
QTimer.singleShot(400, app.quit)
|
||||
rc = app.exec()
|
||||
print(f"selftest OK — {QT_BINDING}: window + {win.tabs.count()} tabs built, "
|
||||
f"event loop ran (rc={rc}); CLI={VAPTVUPT}")
|
||||
f"event loop ran (rc={rc}); CLI={ZUPT_CLI}")
|
||||
return rc
|
||||
|
||||
# Center + raise + focus ONLY on X11 (xcb), where a stacking WM may place
|
||||
|
|
@ -1162,7 +1172,7 @@ def main():
|
|||
# is unaffected. The sentinel env var prevents any relaunch loop (e.g.
|
||||
# "-platform wayland" in argv outranks the env override and would come up
|
||||
# wayland again). Nothing auto-starts jobs before the deadline, so the exec
|
||||
# cannot interrupt real work. Opt out with VAPTVUPT_NO_XCB_FALLBACK=1.
|
||||
# cannot interrupt real work. Opt out with ZUPT_NO_XCB_FALLBACK=1.
|
||||
if app.platformName().startswith("wayland"):
|
||||
class _ExposeLatch(QObject):
|
||||
exposed_once = False
|
||||
|
|
@ -1177,10 +1187,14 @@ def main():
|
|||
def _wayland_map_check():
|
||||
if latch.exposed_once or (handle is not None and handle.isExposed()):
|
||||
return
|
||||
no_fallback = (os.environ.get("ZUPT_NO_XCB_FALLBACK")
|
||||
or os.environ.get("VAPTVUPT_NO_XCB_FALLBACK"))
|
||||
fallback_done = (os.environ.get("ZUPT_XCB_FALLBACK_DONE")
|
||||
or os.environ.get("VAPTVUPT_XCB_FALLBACK_DONE"))
|
||||
can_fallback = (os.environ.get("DISPLAY")
|
||||
and sys.executable
|
||||
and os.environ.get("VAPTVUPT_NO_XCB_FALLBACK") != "1"
|
||||
and os.environ.get("VAPTVUPT_XCB_FALLBACK_DONE") != "1")
|
||||
and no_fallback != "1"
|
||||
and fallback_done != "1")
|
||||
if sys.stderr is not None:
|
||||
try:
|
||||
sys.stderr.write(
|
||||
|
|
@ -1195,7 +1209,7 @@ def main():
|
|||
pass
|
||||
if can_fallback:
|
||||
env = dict(os.environ, QT_QPA_PLATFORM="xcb",
|
||||
VAPTVUPT_XCB_FALLBACK_DONE="1")
|
||||
ZUPT_XCB_FALLBACK_DONE="1")
|
||||
argv = (list(sys.argv) if getattr(sys, "frozen", False)
|
||||
else [sys.executable] + sys.argv)
|
||||
try:
|
||||
|
|
@ -1217,7 +1231,7 @@ def main():
|
|||
# a courtesy notice must never take the GUI down.
|
||||
if sys.stderr is not None:
|
||||
try:
|
||||
sys.stderr.write(f"VaptVupt {ZUPT_VER_NUMBER} GUI started — "
|
||||
sys.stderr.write(f"ZUPT {ZUPT_VER_NUMBER} GUI started — "
|
||||
f"window open (close it to exit).\n")
|
||||
sys.stderr.flush()
|
||||
except OSError:
|
||||
|
|
|
|||
65
gui/zupt-gui
65
gui/zupt-gui
|
|
@ -1,46 +1,31 @@
|
|||
#!/bin/bash
|
||||
set -e
|
||||
#!/usr/bin/env bash
|
||||
# SPDX-License-Identifier: AGPL-3.0-or-later
|
||||
|
||||
DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||
VENV="$DIR/.venv"
|
||||
PY="$VENV/bin/python3"
|
||||
PIP="$VENV/bin/pip"
|
||||
GUI="$DIR/src/zupt_gui.py"
|
||||
# Source-tree launcher for ZUPT GUI.
|
||||
# It performs no package installation and never downloads dependencies.
|
||||
set -Eeuo pipefail
|
||||
|
||||
# ─── System deps (Qt xcb needs these on Debian/Mint/Ubuntu) ───
|
||||
NEED_APT=0
|
||||
for pkg in libxcb-cursor0 libxcb-xinerama0 libxkbcommon-x11-0 libegl1 python3-full; do
|
||||
dpkg -s "$pkg" >/dev/null 2>&1 || NEED_APT=1
|
||||
done
|
||||
if [ "$NEED_APT" -eq 1 ]; then
|
||||
echo "Installing system dependencies..."
|
||||
sudo apt-get update -qq
|
||||
sudo apt-get install -y python3-full python3-venv \
|
||||
libxcb-cursor0 libxcb-xinerama0 libxkbcommon-x11-0 libegl1 2>/dev/null
|
||||
fi
|
||||
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd -P)
|
||||
gui=$script_dir/src/zupt_gui.py
|
||||
[[ -f $gui ]] || {
|
||||
printf 'zupt-gui: GUI source is missing: %s\n' "$gui" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
# ─── Venv ───
|
||||
if [ ! -x "$PY" ]; then
|
||||
rm -rf "$VENV"
|
||||
python3 -m venv "$VENV"
|
||||
fi
|
||||
if ! "$PY" -c "import PySide6" 2>/dev/null; then
|
||||
echo "Installing PySide6..."
|
||||
"$PIP" install --upgrade pip -q 2>/dev/null
|
||||
"$PIP" install PySide6 -q
|
||||
fi
|
||||
|
||||
# ─── Find zupt — local build FIRST, then system ───
|
||||
if [ -z "$ZUPT_BIN" ]; then
|
||||
# Check project tree first (gui/ is inside zupt-2.1.6/)
|
||||
for p in "$DIR/../zupt" "$DIR/../../zupt" "$DIR/zupt"; do
|
||||
[ -x "$p" ] && export ZUPT_BIN="$(readlink -f "$p")" && break
|
||||
done
|
||||
# Then system PATH
|
||||
if [ -z "$ZUPT_BIN" ]; then
|
||||
p="$(command -v zupt 2>/dev/null)"
|
||||
[ -x "$p" ] && export ZUPT_BIN="$p"
|
||||
if [[ -z ${ZUPT_BIN:-} ]]; then
|
||||
if [[ -n ${VAPTVUPT_BIN:-} ]]; then
|
||||
export ZUPT_BIN=$VAPTVUPT_BIN
|
||||
elif [[ -x $script_dir/../zupt ]]; then
|
||||
export ZUPT_BIN=$script_dir/../zupt
|
||||
elif command -v zupt >/dev/null 2>&1; then
|
||||
ZUPT_BIN=$(command -v zupt)
|
||||
export ZUPT_BIN
|
||||
elif [[ -x $script_dir/../vaptvupt ]]; then
|
||||
export ZUPT_BIN=$script_dir/../vaptvupt
|
||||
elif command -v vaptvupt >/dev/null 2>&1; then
|
||||
ZUPT_BIN=$(command -v vaptvupt)
|
||||
export ZUPT_BIN
|
||||
fi
|
||||
fi
|
||||
|
||||
exec "$PY" "$GUI" "$@"
|
||||
exec python3 "$gui" "$@"
|
||||
|
|
|
|||
Loading…
Reference in a new issue