--- /dev/null
+package getter
+
+import (
+ "archive/zip"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+)
+
+// ZipDecompressor is an implementation of Decompressor that can
+// decompress tar.gzip files.
+type ZipDecompressor struct{}
+
+func (d *ZipDecompressor) Decompress(dst, src string, dir bool) error {
+ // If we're going into a directory we should make that first
+ mkdir := dst
+ if !dir {
+ mkdir = filepath.Dir(dst)
+ }
+ if err := os.MkdirAll(mkdir, 0755); err != nil {
+ return err
+ }
+
+ // Open the zip
+ zipR, err := zip.OpenReader(src)
+ if err != nil {
+ return err
+ }
+ defer zipR.Close()
+
+ // Check the zip integrity
+ if len(zipR.File) == 0 {
+ // Empty archive
+ return fmt.Errorf("empty archive: %s", src)
+ }
+ if !dir && len(zipR.File) > 1 {
+ return fmt.Errorf("expected a single file: %s", src)
+ }
+
+ // Go through and unarchive
+ for _, f := range zipR.File {
+ path := dst
+ if dir {
+ path = filepath.Join(path, f.Name)
+ }
+
+ if f.FileInfo().IsDir() {
+ if !dir {
+ return fmt.Errorf("expected a single file: %s", src)
+ }
+
+ // A directory, just make the directory and continue unarchiving...
+ if err := os.MkdirAll(path, 0755); err != nil {
+ return err
+ }
+
+ continue
+ }
+
+ // Create the enclosing directories if we must. ZIP files aren't
+ // required to contain entries for just the directories so this
+ // can happen.
+ if dir {
+ if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
+ return err
+ }
+ }
+
+ // Open the file for reading
+ srcF, err := f.Open()
+ if err != nil {
+ return err
+ }
+
+ // Open the file for writing
+ dstF, err := os.Create(path)
+ if err != nil {
+ srcF.Close()
+ return err
+ }
+ _, err = io.Copy(dstF, srcF)
+ srcF.Close()
+ dstF.Close()
+ if err != nil {
+ return err
+ }
+
+ // Chmod the file
+ if err := os.Chmod(path, f.Mode()); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}