package main import ( "regexp" "fmt" "strings" "os" "bytes" "bufio" "compress/gzip" "encoding/hex" "io" "io/ioutil" "crypto/md5" "crypto/sha1" "crypto/sha256" "github.com/blang/semver" "encoding/json" "gitea.meow.tf/deb-simple/deb/archive" "path" ) var ( variableRegexp = regexp.MustCompile("(.*?):\\s*(.*)") ) type PackageFile struct { Path string `json:"path"` Name string `json:"name"` Size int64 `json:"size"` MD5Hash string `json:"md5"` SHA1Hash string `json:"sha1"` SHA256Hash string `json:"sha256"` ControlData string `json:"control"` Info *Package `json:"info"` } type Package struct { Package string `json:"package"` Version string `json:"version"` Architecture string `json:"architecture"` } func parsePackageData(ctlData string) *Package { res := &Package{} for _, match := range variableRegexp.FindAllStringSubmatch(ctlData, -1) { switch match[1] { case "Package": res.Package = match[2] case "Architecture": res.Architecture = match[2] case "Version": res.Version = match[2] } } return res } type Distro struct { Name string `json:"name"` Architectures map[string]map[string]*PackageFile `json:"architectures"` } var ( distros map[string]*Distro = make(map[string]*Distro) ) func loadCache(dist string) error { f, err := os.Open(path.Join(conf.DistPath(dist), "dist.json")) if err != nil { return err } defer f.Close() var distro Distro if err := json.NewDecoder(f).Decode(&distro); err != nil { return err } distros[dist] = &distro return nil } func saveCache(dist *Distro) error { f, err := os.Create(path.Join(conf.DistPath(dist.Name), "dist.json")) if err != nil { return err } defer f.Close() if err := json.NewEncoder(f).Encode(dist); err != nil { return err } return nil } func buildPackageList(config Conf, distro, arch string) (map[string]*PackageFile, error) { m := make(map[string]*PackageFile) if err := scanRecursive(config.PoolPath(distro, arch), m); err != nil { return nil, err } return m, nil } func scanRecursive(base string, m map[string]*PackageFile) error { dirList, err := ioutil.ReadDir(base) if err != nil { return err } for _, file := range dirList { if file.IsDir() { if err := scanRecursive(path.Join(base, file.Name()), m); err != nil { return err } continue } if !strings.HasSuffix(file.Name(), "deb") { continue } p, err := newPackageFile(path.Join(base, file.Name())) if err != nil { return err } if old, exists := m[p.Info.Package]; exists { v1, err := semver.Parse(old.Info.Version) v2, err2 := semver.Parse(p.Info.Version) if err == nil && err2 == nil && v1.Compare(v2) > 0 { // Use old version continue } } m[p.Info.Package] = p } return nil } func newPackageFile(filePath string) (*PackageFile, error) { p := &PackageFile{ Name: path.Base(filePath), Path: stripPrefix(filePath, conf.Repo.Root), } var err error p.ControlData, err = archive.InspectPackage(filePath) if err != nil { return nil, err } p.Info = parsePackageData(p.ControlData) if stat, err := os.Stat(filePath); err == nil { p.Size = stat.Size() } f, err := os.Open(filePath) if err != nil { return nil, err } defer f.Close() var ( md5hash = md5.New() sha1hash = sha1.New() sha256hash = sha256.New() ) w := io.MultiWriter(md5hash, sha1hash, sha256hash) if _, err := io.Copy(w, f); err != nil { return nil, err } p.MD5Hash = hex.EncodeToString(md5hash.Sum(nil)) p.SHA1Hash = hex.EncodeToString(sha1hash.Sum(nil)) p.SHA256Hash = hex.EncodeToString(sha256hash.Sum(nil)) return p, nil } func createPackagesCached(config Conf, distro, arch string, packages map[string]*PackageFile) error { stdFile, err := os.Create(path.Join(config.ArchPath(distro, arch), "Packages")) if err != nil { return fmt.Errorf("failed to create packages: %s", err) } defer stdFile.Close() gzipFile, err := os.Create(path.Join(config.ArchPath(distro, arch), "Packages.gz")) if err != nil { return fmt.Errorf("failed to create packages.gz: %s", err) } defer gzipFile.Close() gzWriter := gzip.NewWriter(gzipFile) defer gzWriter.Close() stdOut := bufio.NewWriter(stdFile) // loop through each directory // run inspectPackage for _, p := range packages { var packBuf bytes.Buffer packBuf.WriteString(p.ControlData) dir := path.Join("pool/main", distro, arch, p.Info.Package[0:1], p.Info.Package, p.Name) fmt.Fprintf(&packBuf, "Filename: %s\n", dir) fmt.Fprintf(&packBuf, "Size: %d\n", p.Size) fmt.Fprintf(&packBuf, "MD5sum: %s\n", p.MD5Hash) fmt.Fprintf(&packBuf, "SHA1: %s\n", p.SHA1Hash) fmt.Fprintf(&packBuf, "SHA256: %s\n", p.SHA256Hash) packBuf.WriteString("\n\n") stdOut.Write(packBuf.Bytes()) gzWriter.Write(packBuf.Bytes()) } stdOut.Flush() gzWriter.Flush() return nil }