diff --git a/deb/changes.go b/deb/changes.go index 913dee9c..1df9bdda 100644 --- a/deb/changes.go +++ b/deb/changes.go @@ -3,39 +3,66 @@ package deb import ( "fmt" "github.com/smira/aptly/utils" + "io/ioutil" "os" + "path/filepath" ) // Changes is a result of .changes file parsing type Changes struct { - Changes string - Distribution string - Files PackageFiles + Changes string + Distribution string + Files PackageFiles + BasePath, ChangesName string + TempDir string + Stanza Stanza } -// ParseChangesFile does optional signature verification and parses changes files -func ParseChangesFile(path string, acceptUnsigned, ignoreSignature bool, verifier utils.Verifier) (*Changes, error) { - input, err := os.Open(path) +func NewChanges(path string) (*Changes, error) { + var err error + + c := &Changes{ + BasePath: filepath.Dir(path), + ChangesName: filepath.Base(path), + } + + c.TempDir, err = ioutil.TempDir(os.TempDir(), "aptly") if err != nil { return nil, err } + + // copy .changes file into temporary directory + err = utils.CopyFile(filepath.Join(c.BasePath, c.ChangesName), filepath.Join(c.TempDir, c.ChangesName)) + if err != nil { + return nil, err + } + + return c, nil +} + +// VerifyAndParse does optional signature verification and parses changes files +func (c *Changes) VerifyAndParse(acceptUnsigned, ignoreSignature bool, verifier utils.Verifier) error { + input, err := os.Open(filepath.Join(c.TempDir, c.ChangesName)) + if err != nil { + return err + } defer input.Close() isClearSigned, err := verifier.IsClearSigned(input) if err != nil { - return nil, err + return err } input.Seek(0, 0) if !isClearSigned && !acceptUnsigned { - return nil, fmt.Errorf(".changes file is not signed and unsigned processing hasn't been enabled") + return fmt.Errorf(".changes file is not signed and unsigned processing hasn't been enabled") } if isClearSigned && !ignoreSignature { err = verifier.VerifyClearsigned(input) if err != nil { - return nil, err + return err } input.Seek(0, 0) } @@ -45,7 +72,7 @@ func ParseChangesFile(path string, acceptUnsigned, ignoreSignature bool, verifie if isClearSigned { text, err = verifier.ExtractClearsigned(input) if err != nil { - return nil, err + return err } defer text.Close() } else { @@ -53,20 +80,68 @@ func ParseChangesFile(path string, acceptUnsigned, ignoreSignature bool, verifie } reader := NewControlFileReader(text) - stanza, err := reader.ReadStanza() + c.Stanza, err = reader.ReadStanza() if err != nil { - return nil, err + return err } - result := &Changes{ - Distribution: stanza["Distribution"], - Changes: stanza["Changes"], - } + c.Distribution = c.Stanza["Distribution"] + c.Changes = c.Stanza["Changes"] - result.Files, err = result.Files.ParseSumFields(stanza) + c.Files, err = c.Files.ParseSumFields(c.Stanza) if err != nil { - return nil, err + return err } - return result, nil + return nil +} + +// Prepare creates temporary directory, copies file there and verifies checksums +func (c *Changes) Prepare() error { + var err error + + for _, file := range c.Files { + if filepath.Dir(file.Filename) != "." { + return fmt.Errorf("file is not in the same folder as .changes file: %s", file.Filename) + } + + file.Filename = filepath.Base(file.Filename) + + err = utils.CopyFile(filepath.Join(c.BasePath, file.Filename), filepath.Join(c.TempDir, file.Filename)) + if err != nil { + return err + } + } + + for _, file := range c.Files { + var info utils.ChecksumInfo + + info, err = utils.ChecksumsForFile(filepath.Join(c.TempDir, file.Filename)) + if err != nil { + return err + } + + if info.MD5 != file.Checksums.MD5 { + return fmt.Errorf("checksum mismatch MD5: expected %v != obtained %v", file.Checksums.MD5, info.MD5) + } + + if info.SHA1 != file.Checksums.SHA1 { + return fmt.Errorf("checksum mismatch SHA1: expected %v != obtained %v", file.Checksums.SHA1, info.SHA1) + } + + if info.SHA256 != file.Checksums.SHA256 { + return fmt.Errorf("checksum mismatch SHA256 expected %v != obtained %v", file.Checksums.SHA256, info.SHA256) + } + } + + return nil +} + +// Cleanup removes all temporary files +func (c *Changes) Cleanup() error { + if c.TempDir == "" { + return nil + } + + return os.RemoveAll(c.TempDir) } diff --git a/deb/changes_test.go b/deb/changes_test.go index eae5ee18..058e7fb8 100644 --- a/deb/changes_test.go +++ b/deb/changes_test.go @@ -11,7 +11,7 @@ type ChangesSuite struct { var _ = Suite(&ChangesSuite{}) -func (s *ChangesSuite) TestParseChanges(c *C) { +func (s *ChangesSuite) TestParseAndVerify(c *C) { dir := c.MkDir() path := filepath.Join(dir, "calamares.changes") @@ -21,7 +21,10 @@ func (s *ChangesSuite) TestParseChanges(c *C) { f.WriteString(changesFile) f.Close() - changes, err := ParseChangesFile(path, true, true, &NullVerifier{}) + changes, err := NewChanges(path) + c.Check(err, IsNil) + + err = changes.VerifyAndParse(true, true, &NullVerifier{}) c.Check(err, IsNil) c.Check(changes.Distribution, Equals, "sid") @@ -31,6 +34,7 @@ func (s *ChangesSuite) TestParseChanges(c *C) { c.Check(changes.Files[0].Checksums.MD5, Equals, "05fd8f3ffe8f362c5ef9bad2f936a56e") c.Check(changes.Files[0].Checksums.SHA1, Equals, "79f10e955dab6eb25b7f7bae18213f367a3a0396") c.Check(changes.Files[0].Checksums.SHA256, Equals, "35b3280a7b1ffe159a276128cb5c408d687318f60ecbb8ab6dedb2e49c4e82dc") + c.Check(changes.BasePath, Equals, dir) } var changesFile = `Format: 1.8