Skip to content

Commit

Permalink
Add *Reader methods to allow processing of in-memory-only files
Browse files Browse the repository at this point in the history
Closes #4
  • Loading branch information
Tasssadar committed Oct 31, 2019
1 parent 5ea4c23 commit fa1742f
Show file tree
Hide file tree
Showing 3 changed files with 91 additions and 37 deletions.
18 changes: 15 additions & 3 deletions apkparser.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package apkparser

import (
"fmt"
"io"
"os"
"runtime/debug"
)
Expand All @@ -15,13 +16,23 @@ type ApkParser struct {
resources *ResourceTable
}

// Calls ParseApkReader
func ParseApk(path string, encoder ManifestEncoder) (zipErr, resourcesErr, manifestErr error) {
f, zipErr := os.Open(path)
if zipErr != nil {
return
}
defer f.Close()
return ParseApkReader(f, encoder)
}

// Parse APK's Manifest, including resolving refences to resource values.
// encoder expects an XML encoder instance, like Encoder from encoding/xml package.
//
// zipErr != nil means the APK couldn't be opened. The manifest will be parsed
// even when resourcesErr != nil, just without reference resolving.
func ParseApk(path string, encoder ManifestEncoder) (zipErr, resourcesErr, manifestErr error) {
zip, zipErr := OpenZip(path)
func ParseApkReader(r io.ReadSeeker, encoder ManifestEncoder) (zipErr, resourcesErr, manifestErr error) {
zip, zipErr := OpenZipReader(r)
if zipErr != nil {
return
}
Expand All @@ -34,7 +45,8 @@ func ParseApk(path string, encoder ManifestEncoder) (zipErr, resourcesErr, manif
// Parse APK's Manifest, including resolving refences to resource values.
// encoder expects an XML encoder instance, like Encoder from encoding/xml package.
//
// Use this if you already opened the zip with OpenZip before. This method will not Close() the zip.
// Use this if you already opened the zip with OpenZip or OpenZipReader before.
// This method will not Close() the zip.
//
// The manifest will be parsed even when resourcesErr != nil, just without reference resolving.
func ParseApkWithZip(zip *ZipReader, encoder ManifestEncoder) (resourcesErr, manifestErr error) {
Expand Down
2 changes: 1 addition & 1 deletion axml2xml/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ func processInput(input string, opts *optsType) bool {
enc := xml.NewEncoder(os.Stdout)
enc.Indent("", " ")

err = apkparser.ParseManifest(r, enc, nil)
err = apkparser.ParseXml(r, enc, nil)
} else {
_, err = apkparser.ParseResourceTable(r)
}
Expand Down
108 changes: 75 additions & 33 deletions zipreader.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ type ZipReader struct {
// multiple times in case of broken/crafted ZIPs
FilesOrdered []*ZipReaderFile

zipFile *os.File
zipFileReader io.ReadSeeker
ownedZipFile *os.File
}

// This struct mimics of File from archive/zip. The main difference is it can represent
Expand All @@ -34,8 +35,9 @@ type ZipReaderFile struct {
Name string
IsDir bool

zipFile *os.File
internalReader io.ReadCloser
zipFile io.ReadSeeker
internalReader io.Reader
internalCloser io.Closer

zipEntry *zip.File

Expand All @@ -53,8 +55,12 @@ func (zr *ZipReaderFile) Open() error {
if zr.zipEntry != nil {
var err error
zr.curEntry = 0
zr.internalReader, err = zr.zipEntry.Open()
return err
rc, err := zr.zipEntry.Open()
if err != nil {
return err
}
zr.internalReader = rc
zr.internalCloser = rc
} else {
zr.curEntry = -1
}
Expand Down Expand Up @@ -83,7 +89,9 @@ func (zr *ZipReaderFile) Read(p []byte) (int, error) {
case zip.Store:
zr.internalReader = zr.zipFile
default: // case zip.Deflate: // Android treats everything but 0 as deflate
zr.internalReader = flate.NewReader(zr.zipFile)
rc := flate.NewReader(zr.zipFile)
zr.internalReader = rc
zr.internalCloser = rc
}
}
return zr.internalReader.Read(p)
Expand All @@ -96,12 +104,7 @@ func (zr *ZipReaderFile) Next() bool {
return zr.curEntry == 1
}

if zr.internalReader != nil {
if zr.internalReader != zr.zipFile {
zr.internalReader.Close()
}
zr.internalReader = nil
}
zr.Close()

if zr.curEntry+1 >= len(zr.entries) {
return false
Expand All @@ -113,8 +116,9 @@ func (zr *ZipReaderFile) Next() bool {
// Closes this reader and all opened files.
func (zr *ZipReaderFile) Close() error {
if zr.internalReader != nil {
if zr.internalReader != zr.zipFile {
zr.internalReader.Close()
if zr.internalCloser != nil {
zr.internalCloser.Close()
zr.internalCloser = nil
}
zr.internalReader = nil
}
Expand All @@ -131,38 +135,76 @@ func (zr *ZipReaderFile) ZipHeader() *zip.FileHeader {

// Closes this ZIP archive and all it's ZipReaderFile entries.
func (zr *ZipReader) Close() error {
if zr.zipFile == nil {
if zr.zipFileReader == nil {
return nil
}

for _, zf := range zr.File {
zf.Close()
}

err := zr.zipFile.Close()
zr.zipFile = nil
var err error
if zr.ownedZipFile != nil {
err = zr.ownedZipFile.Close()
zr.ownedZipFile = nil
}

zr.zipFileReader = nil
return err
}

// Attempts to open ZIP for reading.
func OpenZip(zippath string) (zr *ZipReader, err error) {
f, err := os.Open(zippath)
type readAtWrapper struct {
io.ReadSeeker
}

func (wr *readAtWrapper) ReadAt(b []byte, off int64) (n int, err error) {
if readerAt, ok := wr.ReadSeeker.(io.ReaderAt); ok {
return readerAt.ReadAt(b, off)
}

oldpos, err := wr.Seek(off, io.SeekCurrent)
if err != nil {
return
}

defer func() {
if err != nil {
zr = nil
f.Close()
}
}()
if _, err = wr.Seek(off, io.SeekStart); err != nil {
return
}

if n, err = wr.Read(b); err != nil {
return
}

_, err = wr.Seek(oldpos, io.SeekStart)
return
}

// Attempts to open ZIP for reading.
func OpenZip(path string) (zr *ZipReader, err error) {
f, err := os.Open(path)
if err != nil {
return nil, err
}

zr, err = OpenZipReader(f)
if err != nil {
f.Close()
} else {
zr.ownedZipFile = f
}
return
}

// Attempts to open ZIP for reading. Might Seek the reader to arbitrary
// positions.
func OpenZipReader(zipReader io.ReadSeeker) (zr *ZipReader, err error) {
zr = &ZipReader{
File: make(map[string]*ZipReaderFile),
zipFile: f,
File: make(map[string]*ZipReaderFile),
zipFileReader: zipReader,
}

f := &readAtWrapper{zipReader}

var zipinfo *zip.Reader
zipinfo, err = tryReadZip(f)
if err == nil {
Expand All @@ -187,7 +229,7 @@ func OpenZip(zippath string) (zr *ZipReader, err error) {
return
}

if _, err = f.Seek(0, 0); err != nil {
if _, err = f.Seek(0, io.SeekStart); err != nil {
return
}

Expand Down Expand Up @@ -249,24 +291,24 @@ func OpenZip(zippath string) (zr *ZipReader, err error) {
}
}

func tryReadZip(f *os.File) (r *zip.Reader, err error) {
func tryReadZip(f *readAtWrapper) (r *zip.Reader, err error) {
defer func() {
if pn := recover(); pn != nil {
err = fmt.Errorf("%v", pn)
r = nil
}
}()

fi, err := f.Stat()
size, err := f.Seek(0, io.SeekEnd)
if err != nil {
return
}

r, err = zip.NewReader(f, fi.Size())
r, err = zip.NewReader(f, size)
return
}

func findNextFileHeader(f *os.File) (offset int64, err error) {
func findNextFileHeader(f io.ReadSeeker) (offset int64, err error) {
start, err := f.Seek(0, 1)
if err != nil {
return -1, err
Expand Down

0 comments on commit fa1742f

Please sign in to comment.