mirror of
https://github.com/OpenListTeam/OpenList.git
synced 2025-11-25 03:15:19 +08:00
* fix(archive/zip): user specific encoding for non-EFS zips * fix(stream): simplify head cache initialization and improve reader retrieval logic * fix: support multipart zips (.z01) * chore(deps): update github.com/KirCute/zip to v1.0.1 --------- Co-authored-by: j2rong4cn <j2rong@qq.com> Co-authored-by: Pikachu Ren <40362270+PIKACHUIM@users.noreply.github.com>
126 lines
2.6 KiB
Go
126 lines
2.6 KiB
Go
package zip
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"io/fs"
|
|
"strings"
|
|
|
|
"github.com/KirCute/zip"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/archive/tool"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/conf"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/errs"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/setting"
|
|
"github.com/OpenListTeam/OpenList/v4/internal/stream"
|
|
"golang.org/x/text/encoding/ianaindex"
|
|
"golang.org/x/text/transform"
|
|
)
|
|
|
|
type WrapReader struct {
|
|
Reader *zip.Reader
|
|
}
|
|
|
|
func (r *WrapReader) Files() []tool.SubFile {
|
|
ret := make([]tool.SubFile, 0, len(r.Reader.File))
|
|
for _, f := range r.Reader.File {
|
|
ret = append(ret, &WrapFile{f: f})
|
|
}
|
|
return ret
|
|
}
|
|
|
|
type WrapFileInfo struct {
|
|
fs.FileInfo
|
|
efs bool
|
|
}
|
|
|
|
func (f *WrapFileInfo) Name() string {
|
|
return decodeName(f.FileInfo.Name(), f.efs)
|
|
}
|
|
|
|
type WrapFile struct {
|
|
f *zip.File
|
|
}
|
|
|
|
func (f *WrapFile) Name() string {
|
|
return decodeName(f.f.Name, isEFS(f.f.Flags))
|
|
}
|
|
|
|
func (f *WrapFile) FileInfo() fs.FileInfo {
|
|
return &WrapFileInfo{FileInfo: f.f.FileInfo(), efs: isEFS(f.f.Flags)}
|
|
}
|
|
|
|
func (f *WrapFile) Open() (io.ReadCloser, error) {
|
|
return f.f.Open()
|
|
}
|
|
|
|
func (f *WrapFile) IsEncrypted() bool {
|
|
return f.f.IsEncrypted()
|
|
}
|
|
|
|
func (f *WrapFile) SetPassword(password string) {
|
|
f.f.SetPassword(password)
|
|
}
|
|
|
|
func makePart(ss *stream.SeekableStream) (zip.SizeReaderAt, error) {
|
|
ra, err := stream.NewReadAtSeeker(ss, 0)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return &inlineSizeReaderAt{ReaderAt: ra, size: ss.GetSize()}, nil
|
|
}
|
|
|
|
func (z *Zip) getReader(ss []*stream.SeekableStream) (*zip.Reader, error) {
|
|
if len(ss) > 1 && z.traditionalSecondPartRegExp.MatchString(ss[1].GetName()) {
|
|
ss = append(ss[1:], ss[0])
|
|
ras := make([]zip.SizeReaderAt, 0, len(ss))
|
|
for _, s := range ss {
|
|
ra, err := makePart(s)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
ras = append(ras, ra)
|
|
}
|
|
return zip.NewMultipartReader(ras)
|
|
} else {
|
|
reader, err := stream.NewMultiReaderAt(ss)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return zip.NewReader(reader, reader.Size())
|
|
}
|
|
}
|
|
|
|
func filterPassword(err error) error {
|
|
if err != nil && strings.Contains(err.Error(), "password") {
|
|
return errs.WrongArchivePassword
|
|
}
|
|
return err
|
|
}
|
|
|
|
func decodeName(name string, efs bool) string {
|
|
if efs {
|
|
return name
|
|
}
|
|
enc, err := ianaindex.IANA.Encoding(setting.GetStr(conf.NonEFSZipEncoding))
|
|
if err != nil {
|
|
return name
|
|
}
|
|
i := bytes.NewReader([]byte(name))
|
|
decoder := transform.NewReader(i, enc.NewDecoder())
|
|
content, _ := io.ReadAll(decoder)
|
|
return string(content)
|
|
}
|
|
|
|
func isEFS(flags uint16) bool {
|
|
return (flags & 0x800) > 0
|
|
}
|
|
|
|
type inlineSizeReaderAt struct {
|
|
io.ReaderAt
|
|
size int64
|
|
}
|
|
|
|
func (i *inlineSizeReaderAt) Size() int64 {
|
|
return i.size
|
|
}
|