mirror of
https://github.com/GopeedLab/gopeed.git
synced 2026-11-07 00:50:27 +00:00
Move path sanitization before directory handling in extractFile so directory entries with "../" prefixes are rejected the same way file entries already are. Add the same sanitization to extractSevenZipMultiPart and both zip extraction loops in the Windows updater, which had no path validation at all.
97 lines
2.5 KiB
Go
97 lines
2.5 KiB
Go
package download
|
|
|
|
import (
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/bodgit/sevenzip"
|
|
)
|
|
|
|
// extractSevenZipMultiPart extracts a multi-part 7z archive using bodgit/sevenzip directly
|
|
// The mholt/archives wrapper doesn't properly handle multi-part 7z files because it uses
|
|
// io.SectionReader which can only see the first part. The bodgit/sevenzip library's
|
|
// OpenReaderWithPassword function handles multi-part files automatically when given the .001 file path.
|
|
func extractSevenZipMultiPart(firstPartPath string, destDir string, password string, progressCallback ExtractProgressCallback) error {
|
|
// Use bodgit/sevenzip directly - it automatically handles .001, .002, etc. files
|
|
var reader *sevenzip.ReadCloser
|
|
var err error
|
|
|
|
if password != "" {
|
|
reader, err = sevenzip.OpenReaderWithPassword(firstPartPath, password)
|
|
} else {
|
|
reader, err = sevenzip.OpenReader(firstPartPath)
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer reader.Close()
|
|
|
|
// Create destination directory
|
|
if err := os.MkdirAll(destDir, 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Count total files for progress
|
|
totalFiles := 0
|
|
for _, f := range reader.File {
|
|
if !f.FileInfo().IsDir() {
|
|
totalFiles++
|
|
}
|
|
}
|
|
|
|
// Extract files with progress tracking
|
|
extractedFiles := 0
|
|
for _, f := range reader.File {
|
|
cleanName := filepath.Clean(f.Name)
|
|
if strings.HasPrefix(cleanName, "..") || filepath.IsAbs(cleanName) {
|
|
continue
|
|
}
|
|
destPath := filepath.Join(destDir, cleanName)
|
|
|
|
if f.FileInfo().IsDir() {
|
|
if err := os.MkdirAll(destPath, f.Mode()); err != nil {
|
|
return err
|
|
}
|
|
continue
|
|
}
|
|
|
|
if err := os.MkdirAll(filepath.Dir(destPath), 0755); err != nil {
|
|
return err
|
|
}
|
|
|
|
// Extract file - open, copy, close immediately (as recommended by bodgit/sevenzip)
|
|
if err := extractSevenZipFile(f, destPath); err != nil {
|
|
return err
|
|
}
|
|
|
|
extractedFiles++
|
|
if progressCallback != nil && totalFiles > 0 {
|
|
progress := int(float64(extractedFiles) / float64(totalFiles) * 100)
|
|
progressCallback(extractedFiles, totalFiles, progress)
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// extractSevenZipFile extracts a single file from a 7z archive
|
|
// This follows the bodgit/sevenzip recommended pattern of closing rc before processing the next file
|
|
func extractSevenZipFile(f *sevenzip.File, destPath string) error {
|
|
rc, err := f.Open()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer rc.Close()
|
|
|
|
outFile, err := os.OpenFile(destPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer outFile.Close()
|
|
|
|
_, err = io.Copy(outFile, rc)
|
|
return err
|
|
}
|