227 lines
5.2 KiB
Go
227 lines
5.2 KiB
Go
/**
|
|
* The MIT License (MIT)
|
|
*
|
|
* Copyright (c) 2014 Yani Iliev <yani@iliev.me>
|
|
*
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
* of this software and associated documentation files (the "Software"), to deal
|
|
* in the Software without restriction, including without limitation the rights
|
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
* copies of the Software, and to permit persons to whom the Software is
|
|
* furnished to do so, subject to the following conditions:
|
|
*
|
|
* The above copyright notice and this permission notice shall be included in
|
|
* all copies or substantial portions of the Software.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
* SOFTWARE.
|
|
*/
|
|
|
|
package wpress
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"os"
|
|
"path"
|
|
"runtime"
|
|
"strings"
|
|
)
|
|
|
|
const PATH_SEPARATOR_WIN = '\\'
|
|
const PATH_SEPARATOR_UNIX = '/'
|
|
|
|
// Reader structure
|
|
type Reader struct {
|
|
Filename string
|
|
File *os.File
|
|
NumberOfFiles int
|
|
}
|
|
|
|
// NewReader creates a new Reader instance and calls its constructor
|
|
func NewReader(filename string) (*Reader, error) {
|
|
// create a new instance of Reader
|
|
r := &Reader{filename, nil, 0}
|
|
|
|
// call the constructor
|
|
err := r.Init()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// return Reader instance
|
|
return r, nil
|
|
}
|
|
|
|
// Init is the constructor of Reader struct
|
|
func (r *Reader) Init() error {
|
|
// try to open the file
|
|
file, err := os.Open(r.Filename)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
// file was openned, assign the handle to the holding variable
|
|
r.File = file
|
|
|
|
return nil
|
|
}
|
|
|
|
// ExtractFile extracts file that matches tha filename and path from archive
|
|
func (r Reader) ExtractFile(filename string, path string) ([]byte, error) {
|
|
// TODO: implement
|
|
return nil, nil
|
|
}
|
|
|
|
// Extract all files from archive
|
|
func (r Reader) Extract() (int, error) {
|
|
// put pointer at the beginning of the file
|
|
r.File.Seek(0, 0)
|
|
|
|
// loop until end of file was reached
|
|
iteration := 0;
|
|
for {
|
|
iteration++;
|
|
// read header block
|
|
block, err := r.GetHeaderBlock()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// initialize new header
|
|
h := &Header{}
|
|
|
|
// check if block equals EOF sequence
|
|
if bytes.Compare(block, h.GetEOFBlock()) == 0 {
|
|
// EOF reached, stop the loop
|
|
break
|
|
}
|
|
|
|
// populate header from our block bytes
|
|
h.PopulateFromBytes(block)
|
|
|
|
pathToFile := path.Clean("." + string(os.PathSeparator) + string(bytes.Trim(h.Prefix, "\x00")) + string(os.PathSeparator) + string(bytes.Trim(h.Name, "\x00")))
|
|
if runtime.GOOS == "windows" {
|
|
sep := fmt.Sprintf("%c", PATH_SEPARATOR_UNIX)
|
|
pathToFile = strings.Replace(pathToFile,"\\",sep,-1)
|
|
fmt.Println(pathToFile)
|
|
}
|
|
|
|
err = os.MkdirAll(path.Dir(pathToFile), 0777)
|
|
if err != nil {
|
|
fmt.Println(err)
|
|
return r.NumberOfFiles, err
|
|
}
|
|
|
|
// try to open the file
|
|
|
|
|
|
|
|
file, err := os.Create(pathToFile)
|
|
if err != nil {
|
|
return r.NumberOfFiles, err
|
|
}
|
|
|
|
totalBytesToRead, _ := h.GetSize()
|
|
for {
|
|
bytesToRead := 512
|
|
if bytesToRead > totalBytesToRead {
|
|
bytesToRead = totalBytesToRead
|
|
}
|
|
|
|
if bytesToRead == 0 {
|
|
break
|
|
}
|
|
|
|
content := make([]byte, bytesToRead)
|
|
bytesRead, err := r.File.Read(content)
|
|
if err != nil {
|
|
return r.NumberOfFiles, err
|
|
}
|
|
|
|
totalBytesToRead -= bytesRead
|
|
contentRead := content[0:bytesRead]
|
|
|
|
_, err = file.Write(contentRead)
|
|
if err != nil {
|
|
return r.NumberOfFiles, err
|
|
}
|
|
}
|
|
|
|
file.Close()
|
|
|
|
// increment file counter
|
|
r.NumberOfFiles++
|
|
}
|
|
|
|
return r.NumberOfFiles, nil
|
|
}
|
|
|
|
// GetHeaderBlock reads and returns header block from archive
|
|
func (r Reader) GetHeaderBlock() ([]byte, error) {
|
|
// create buffer to keep the header block
|
|
block := make([]byte, headerSize)
|
|
|
|
// read the header block
|
|
bytesRead, err := r.File.Read(block)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
if bytesRead != headerSize {
|
|
return nil, errors.New("unable to read header block size")
|
|
}
|
|
|
|
return block, nil
|
|
}
|
|
|
|
// GetFilesCount returns the number of files in archive
|
|
func (r Reader) GetFilesCount() (int, error) {
|
|
// test if we have enumerated the archive already
|
|
if r.NumberOfFiles != 0 {
|
|
return r.NumberOfFiles, nil
|
|
}
|
|
|
|
// put pointer at the beginning of the file
|
|
r.File.Seek(0, 0)
|
|
|
|
// loop until end of file was reached
|
|
for {
|
|
// read header block
|
|
block, err := r.GetHeaderBlock()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
// initialize new header
|
|
h := &Header{}
|
|
|
|
// check if block equals EOF sequence
|
|
if bytes.Compare(block, h.GetEOFBlock()) == 0 {
|
|
// EOF reached, stop the loop
|
|
break
|
|
}
|
|
|
|
// populate header from our block bytes
|
|
h.PopulateFromBytes(block)
|
|
|
|
// set pointer after file content, to the next header block
|
|
size, err := h.GetSize()
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
r.File.Seek(int64(size), 1)
|
|
|
|
// increment file counter
|
|
r.NumberOfFiles++
|
|
}
|
|
|
|
return r.NumberOfFiles, nil
|
|
}
|