package main
import (
"bufio"
"crypto/md5"
"crypto/sha256"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strings"
"go.wit.com/lib/cobol"
"go.wit.com/lib/gui/shell"
"go.wit.com/lib/protobuf/zoopb"
"go.wit.com/log"
"google.golang.org/protobuf/types/known/timestamppb"
)
func populateDebInfo(p *zoopb.Package) error {
// SIMPLE SANITY CHECKS
if p.DebInfo != nil {
// already added p.DebInfo
return nil
}
fullname := filepath.Join(me.pb.BaseDir, p.Filename)
cmd := []string{"dpkg", "-I", fullname}
r := shell.Run(cmd)
if r.Error != nil {
return r.Error
}
if r.Exit != 0 {
return errors.New("dpkg returned -1")
}
filedata, err := os.Open(fullname)
if err != nil {
return err
}
defer filedata.Close()
// SIMPLE SANITY CHECKS END
// SHA256 HASH
p.DebInfo = new(zoopb.DebInfo)
hSHA256 := sha256.New()
hMD5 := md5.New() // probably deprecate, but love md5sum too much
// hSHA1 := sha1.New() // deprecated
// TeeReader allows writing to multiple hashers at once
// multiWriter := io.MultiWriter(hMD5, hSHA1, hSHA256)
multiWriter := io.MultiWriter(hSHA256, hMD5)
if _, err := io.Copy(multiWriter, filedata); err != nil {
return err
}
p.DebInfo.SHA256 = fmt.Sprintf("%x", hSHA256.Sum(nil)) // should be the standard now
p.DebInfo.MD5SUM = fmt.Sprintf("%x", hMD5.Sum(nil)) // probably deprecate
// p.DebInfo.SHA1 = fmt.Sprintf("%x", hSHA1.Sum(nil)) // deprecated
// SHA256 HASH END
// PARSE "dpkg -I
", then exit as we are done
starting := true
all := strings.Join(r.Stdout, "\n")
scanner := bufio.NewScanner(strings.NewReader(all))
for scanner.Scan() {
line := scanner.Text()
parts := strings.Fields(line)
if starting {
if parts[0] == "new" {
if argv.Verbose {
log.Printf("new: %v\n", parts)
}
// skip the first dpkg -I line
continue
}
if parts[0] == "size" {
if argv.Verbose {
log.Printf("size: %v\n", parts)
}
p.DebInfo.InstalledSize = parts[1]
// scan all the entries from size
for scanner.Scan() {
line = scanner.Text()
parts = strings.Fields(line)
if strings.HasPrefix(line, " ") {
if argv.Verbose {
log.Printf("sizeline: %v\n", parts)
}
continue
}
starting = false
break
}
}
if starting {
if argv.Verbose {
log.Printf("parts: %v\n", parts)
}
continue
}
}
varname := strings.TrimSuffix(parts[0], ":")
varval := strings.Join(parts[1:], " ")
// log.Printf("varname:%s varval:%s\n", varname, varval)
switch varname {
case "Package":
p.Package = varval
case "Filename":
log.Printf("Filename: varname:%s varval:%s\n", varname, varval)
// p.Package = varval
case "Version":
p.Version = varval
case "Architecture":
p.Architecture = varval
case "GoPath":
p.Namespace = varval
case "Maintainer":
p.DebInfo.Maintainer = varval
case "Packager":
p.DebInfo.Packager = varval
case "Depends":
p.DebInfo.Depends = varval
case "Source":
p.DebInfo.Source = varval
case "URL":
p.DebInfo.URL = varval
case "Build-Depends":
p.DebInfo.BuildDepends = varval
case "Installed-Size:":
case "Installed-Size":
p.DebInfo.InstalledSize = varval
case "Homepage":
p.DebInfo.URL = varval
case "Conflicts":
p.DebInfo.Conflicts = varval
case "Source-Date":
t, err := cobol.GetTime(varval)
if err == nil {
p.GitDate = timestamppb.New(t)
} else {
if argv.Verbose {
log.Info("FIXME: Package-Build-Date", varval, err)
}
}
case "Build-Date":
case "Package-Build-Date":
t, err := cobol.GetTime(varval)
if err == nil {
p.BuildDate = timestamppb.New(t)
} else {
if argv.Verbose {
log.Info("FIXME: Package-Build-Date", varval, err)
}
}
case "Git-Tag-Date":
if argv.Verbose {
log.Info("FIXME: Git-Tag-Date", varval)
}
case "Description":
description := varval
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, " ") {
line = strings.TrimSpace(line)
description += line + "\n"
continue
}
break
}
p.DebInfo.Description = description
default:
// This forces me(it could be you!) to fix this parser
varname2 := strings.TrimSuffix(varname, ":")
log.Printf("UNKNOWN: varname:%s varval:%s varname2=%s\n", varname, varval, varname2)
panic("fix mirrors populateDebInfo()")
}
}
// pb.DebInfo should be correctly populated. You can verify these with "mirrors --verify"
return nil
}
/*
UNKNOWN: varname:Depends varval:protobuf-compiler, protoc-gen-go | protoc-gen-go-wit
UNKNOWN: varname:Build-Depends varval:golang
UNKNOWN: varname:URL varval:https://go.wit.com/
UNKNOWN: varname:Package-Build-Date varval:2025/10/05 07:40:30 UTC
UNKNOWN: varname:Git-Tag-Date varval:todo: get from repo
UNKNOWN: varname:Description varval:autogen protobuf marshal and sort files
UNKNOWN: varname:this varval:is needed for go.wit.com/lib/protobuf/ GO packages
*/
func getDebianControlFile(p *zoopb.Package, varname string) (string, string) {
switch varname {
case "Package":
return "Package", p.Package
case "Filename":
return "Filename", p.Filename
case "Version":
return "Version", p.Version
case "Architecture":
return "Architecture", p.Architecture
case "Maintainer":
return "Maintainer", p.DebInfo.Maintainer
// return "Maintainer", p.Author
case "Source":
if argv.Verbose {
log.Info("skipping from controlfile", varname)
// return "Source", p.DebInfo.Source
}
return "Source", ""
case "Conflicts":
return "Conflicts", p.DebInfo.Conflicts
case "Packager":
return "Packager", p.DebInfo.Packager
// return "Packager", p.Packager
case "Depends":
return "Depends", p.DebInfo.Depends
case "BuildDepends":
return "Build-Depends", p.DebInfo.BuildDepends
case "InstalledSize":
return "Installed-Size", "222222222"
case "URL":
return "Homepage", p.DebInfo.URL
case "Size":
return "Size", "22222222"
case "BuildDate":
return "Build-Date", cobol.Time(p.BuildDate)
// case "SHA1":
// return "SHA1", p.DebInfo.SHA1
case "MD5SUM":
return "MD5Sum", p.DebInfo.MD5SUM
case "SHA256":
return "SHA256", p.DebInfo.SHA256
case "SHA512":
return "SHA512", "" // todo: implement or totally rediculously overkill ?
}
log.Info("DebInfo sent a field we didn't have. fix the code above", varname)
return "", ""
}
// make a list of the newest .deb files
func doMakePackagesFile(all *zoopb.Packages) string {
var pfile string
for p := range all.IterAll() {
var controlfile string
parts, err := zoopb.GetDebInfoFields(p)
if err != nil {
log.Info(err)
}
for _, varname := range parts {
varname, varval := getDebianControlFile(p, varname)
varval = strings.TrimSpace(varval)
if varval == "" {
continue
}
controlfile += log.Sprintf("%s: %s\n", varname, varval)
}
controlfile += log.Sprintf("\n")
pfile += controlfile
}
return pfile
}
/*
log.Printf("Scanning for .deb files in %s\n", poolDir)
debInfos, err := scanDebs(poolDir, 200)
if err != nil {
log.Printf("Failed to scan .deb files: %v\n", err)
me.sh.BadExit("scan pool scan dir", err)
}
var counter int
for _, deb := range debInfos {
newdeb := new(zoopb.Package)
newdeb.DebInfo = new(zoopb.DebInfo)
newdeb.Filename = deb.Filename
newdeb.DebInfo.Filename = deb.Filename
newdeb.DebInfo.MD5SUM = deb.MD5Sum
// newdeb.DebInfo.SHA1 = deb.SHA1Sum
newdeb.DebInfo.SHA256 = deb.SHA256Sum
// log.Info("len(CONTROLDATA)", len(deb.ControlData))
// log.Sprintf("VAR='%s' VAL='%s'\n", varname, varvalue)
// log.Info("%v", deb.ControlData)
for varname, varvalue := range deb.ControlData {
switch varname {
case "Package":
newdeb.Package = varvalue
case "Version":
newdeb.Version = varvalue
case "Architecture":
newdeb.Architecture = varvalue
case "Git-Tag-Date":
if argv.Verbose {
log.Info("CONVERT THIS TO TIME", varname, varvalue)
}
case "Depends":
newdeb.DebInfo.Depends = varvalue
case "Build-Depends":
newdeb.DebInfo.BuildDepends = varvalue
case "Packager":
newdeb.DebInfo.Packager = varvalue
case "Package-Build-Date":
varname = "PackageBuildDate"
const layout = "2006/01/02 15:04:05 MST"
parsedTime, err := time.Parse(layout, varvalue)
if err != nil {
log.Info("CONVERT TO TIME failed", varname, varvalue, err)
}
newdeb.BuildDate = timestamppb.New(parsedTime)
case "URL":
newdeb.DebInfo.Homepage = varvalue
default:
if err := debian.SetDebInfoString(newdeb, varname, varvalue); err == nil {
if argv.Verbose {
log.Printf("Searching for Sugarman WORKED: VAR='%-30s' VAL='%s'\n", varname, varvalue)
}
} else {
log.Printf("Searching for Sugarman (unknwon var): VAR='%-30s' VAL='%s' err=%v\n", varname, varvalue, err)
}
// todo: add to protomap
}
}
*/