Merge pull request #89 from Retengart/test-features
Some checks failed
build-linux / build (push) Has been cancelled
build-macos / build (push) Has been cancelled
build-windows / build (push) Has been cancelled
CodeQL / Analyze (push) Has been cancelled

App version in the window title and zip unpacking
This commit is contained in:
Evan Su 2025-01-29 11:02:44 -05:00 committed by GitHub
commit 670d284603
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 151 additions and 7 deletions

View file

@ -3,6 +3,12 @@
<li>Migrate golang.org/x/crypto to standard library imports (https://github.com/golang/go/issues/65269)</li> <li>Migrate golang.org/x/crypto to standard library imports (https://github.com/golang/go/issues/65269)</li>
</ul> </ul>
# v1.46 (Released 01/29/2025)
<ul>
<li>✓ Added Picocrypt version to the window title</li>
<li>✓ Added ability to automatically unzip archives upon decryption</li>
</ul>
# v1.45 (Released 12/05/2024) # v1.45 (Released 12/05/2024)
<ul> <ul>
<li>✓ Bumped GitHub Actions Ubuntu 22 -> 24 and macOS 14 -> 15</li> <li>✓ Bumped GitHub Actions Ubuntu 22 -> 24 and macOS 14 -> 15</li>

View file

@ -1 +1 @@
1.45 1.46

View file

@ -44,11 +44,17 @@
</screenshot> </screenshot>
</screenshots> </screenshots>
<releases> <releases>
<release version="1.45" date="2024-12-05"> <release version="1.46" date="2025-01-29">
<url type="details">https://github.com/Picocrypt/Picocrypt/blob/main/Changelog.md#v145-released-12052024</url> <url type="details">https://github.com/Picocrypt/Picocrypt/blob/main/Changelog.md#v146-released-01292025</url>
<description> <description>
<p>No code changes; just updated dependencies.</p> <ul>
<li>Added Picocrypt version to the window title.</li>
<li>Added ability to automatically unpack zip archives during decryption.</li>
</ul>
</description> </description>
</release> </release>
<release version="1.45" date="2024-12-05">
<description></description>
</release>
</releases> </releases>
</component> </component>

View file

@ -2,7 +2,7 @@ package main
/* /*
Picocrypt v1.45 Picocrypt v1.46
Copyright (c) Evan Su Copyright (c) Evan Su
Released under a GNU GPL v3 License Released under a GNU GPL v3 License
https://github.com/Picocrypt/Picocrypt https://github.com/Picocrypt/Picocrypt
@ -60,7 +60,7 @@ var TRANSPARENT = color.RGBA{0x00, 0x00, 0x00, 0x00}
// Generic variables // Generic variables
var window *giu.MasterWindow var window *giu.MasterWindow
var version = "v1.45" var version = "v1.46"
var dpi float32 var dpi float32
var mode string var mode string
var working bool var working bool
@ -120,6 +120,8 @@ var splitSelected int32 = 1
var recombine bool var recombine bool
var compress bool var compress bool
var delete bool var delete bool
var autoUnzip bool
var sameLevel bool
var keep bool var keep bool
var kept bool var kept bool
@ -552,6 +554,22 @@ func draw() {
giu.Checkbox("Delete volume", &delete), giu.Checkbox("Delete volume", &delete),
giu.Tooltip("Delete the volume after a successful decryption"), giu.Tooltip("Delete the volume after a successful decryption"),
).Build() ).Build()
giu.Row(
giu.Style().SetDisabled(!strings.HasSuffix(inputFile, ".zip.pcv")).To(
giu.Checkbox("Auto unzip", &autoUnzip).OnChange(func() {
if !autoUnzip {
sameLevel = false
}
}),
giu.Tooltip("Extract .zip upon decryption (may overwrite)"),
),
giu.Dummy(-170, 0),
giu.Style().SetDisabled(!autoUnzip).To(
giu.Checkbox("Same level", &sameLevel),
giu.Tooltip("Extract .zip contents to same folder as volume"),
),
).Build()
} }
}), }),
@ -2112,6 +2130,21 @@ func work() {
os.Remove(inputFile) os.Remove(inputFile)
} }
if mode == "decrypt" && !kept && autoUnzip {
showProgress = true
popupStatus = "Unzipping..."
giu.Update()
if err := unpackArchive(outputFile); err != nil {
mainStatus = "Auto unzipping failed!"
mainStatusColor = RED
giu.Update()
return
}
os.Remove(outputFile)
}
// All done, reset the UI // All done, reset the UI
oldKept := kept oldKept := kept
resetUI() resetUI()
@ -2209,6 +2242,8 @@ func resetUI() {
recombine = false recombine = false
compress = false compress = false
delete = false delete = false
autoUnzip = false
sameLevel = false
keep = false keep = false
kept = false kept = false
@ -2330,9 +2365,106 @@ func sizeify(size int64) string {
} }
} }
func unpackArchive(zipPath string) error {
reader, err := zip.OpenReader(zipPath)
if err != nil {
return err
}
defer reader.Close()
var totalSize int64
for _, f := range reader.File {
totalSize += int64(f.UncompressedSize64)
}
var extractDir string
if sameLevel {
extractDir = filepath.Dir(zipPath)
} else {
extractDir = filepath.Join(filepath.Dir(zipPath), strings.TrimSuffix(filepath.Base(zipPath), ".zip"))
}
var done int64
startTime := time.Now()
for _, f := range reader.File {
if strings.Contains(f.Name, "..") {
return errors.New("potentially malicious zip item path")
}
outPath := filepath.Join(extractDir, f.Name)
// Make directory if current entry is a folder
if f.FileInfo().IsDir() {
if err := os.MkdirAll(outPath, f.Mode()); err != nil {
return err
}
}
}
for i, f := range reader.File {
if strings.Contains(f.Name, "..") {
return errors.New("potentially malicious zip item path")
}
// Already handled above
if f.FileInfo().IsDir() {
continue
}
outPath := filepath.Join(extractDir, f.Name)
// Otherwise create necessary parent directories
if err := os.MkdirAll(filepath.Dir(outPath), 0755); err != nil {
return err
}
// Open the file inside the archive
fileInArchive, err := f.Open()
if err != nil {
return err
}
defer fileInArchive.Close()
dstFile, err := os.Create(outPath)
if err != nil {
return err
}
// Read from zip in chunks to update progress
buffer := make([]byte, MiB)
for {
n, readErr := fileInArchive.Read(buffer)
if n > 0 {
_, writeErr := dstFile.Write(buffer[:n])
if writeErr != nil {
dstFile.Close()
os.Remove(dstFile.Name())
return writeErr
}
done += int64(n)
progress, speed, eta = statify(done, totalSize, startTime)
progressInfo = fmt.Sprintf("%d/%d", i+1, len(reader.File))
popupStatus = fmt.Sprintf("Unpacking at %.2f MiB/s (ETA: %s)", speed, eta)
giu.Update()
}
if readErr != nil {
if readErr == io.EOF {
break
}
dstFile.Close()
return readErr
}
}
dstFile.Close()
}
return nil
}
func main() { func main() {
// Create the main window // Create the main window
window = giu.NewMasterWindow("Picocrypt", 318, 507, giu.MasterWindowFlagsNotResizable) window = giu.NewMasterWindow("Picocrypt "+version[1:], 318, 507, giu.MasterWindowFlagsNotResizable)
// Start the dialog module // Start the dialog module
dialog.Init() dialog.Init()