Files
Matheus Sampaio Queiroga fb6024ec5d big update and move dpkg module
Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
2025-03-13 14:40:14 -03:00

236 lines
6.4 KiB
Go

package dpkg
import (
"archive/tar"
"bytes"
"compress/gzip"
"encoding"
"errors"
"fmt"
"io"
"strings"
"sirherobrine23.com.br/sirherobrine23/go-dpkg/ar"
"sirherobrine23.com.br/sirherobrine23/go-dpkg/deb822"
)
var ErrInvalidDebian = errors.New("invalid debian file")
type Maintainer struct {
Name string `json:"name,omitempty"`
Contact string `json:"contact,omitempty"`
}
func (main Maintainer) String() string {
if main.Contact == "" {
return main.Name
}
return fmt.Sprintf("%s <%s>", main.Name, main.Contact)
}
type Package struct {
Name string `json:"name"`
Version string `json:"version"`
Architecture string `json:"arch"`
Maintainer Maintainer `json:"maintainer"`
Description [2]string `json:"description"`
OriginalMaintainer *Maintainer `json:"original_maintainer,omitempty"`
Extra deb822.Deb822 `json:"extra"`
}
func ParsePackage(data []byte) (*Package, error) {
pkg := &Package{}
if err := pkg.UnmarshalBinary(data); err != nil {
return nil, err
}
return pkg, nil
}
func (pkg Package) MarshalBinary() ([]byte, error) {
lines := []string{
fmt.Sprintf("Package: %s", pkg.Name),
fmt.Sprintf("Version: %s", pkg.Version),
fmt.Sprintf("Architecture: %s", pkg.Architecture),
fmt.Sprintf("Maintainer: %s", pkg.Maintainer.String()),
}
if pkg.OriginalMaintainer != nil {
lines = append(lines, fmt.Sprintf("Original-Maintainer: %s", pkg.OriginalMaintainer.String()))
}
for key, value := range pkg.Extra {
switch v := value.(type) {
case fmt.Stringer:
lines = append(lines, fmt.Sprintf("%s: %s", key, v.String()))
case encoding.TextMarshaler:
data, err := v.MarshalText()
if err != nil {
return nil, err
}
lines = append(lines, fmt.Sprintf("%s: %s", key, string(data)))
default:
if v.IsMultiline() {
lines = append(lines, fmt.Sprintf("%s: %s", key, strings.Join(v.ToSlice(), "\n")))
continue
}
lines = append(lines, fmt.Sprintf("%s: %s", key, v))
}
}
lines = append(lines, fmt.Sprintf("Description: %s", pkg.Description[0]))
if pkg.Description[1] != "" {
descLines := strings.Split(strings.TrimSpace(pkg.Description[1]), "\n")
for index := range descLines {
if strings.TrimSpace(descLines[index]) == "" {
descLines[index] = "."
continue
}
}
lines = append(lines, fmt.Sprintf(" %s", strings.Join(descLines, "\n ")))
}
return []byte(strings.Join(append(lines, ""), "\n")), nil
}
func (pkg *Package) UnmarshalBinary(body []byte) error {
*pkg = Package{Extra: deb822.Deb822{}} // Reset package struct
mapped, err := deb822.Parse(body)
if err != nil {
return err
}
pkg.Name = mapped["Package"].String()
pkg.Version = mapped["Version"].String()
pkg.Architecture = mapped["Architecture"].String()
pkg.Maintainer = Maintainer{}
if value, ok := mapped["Maintainer"]; ok {
maintainer := strings.SplitN(value.String(), "<", 2)
pkg.Maintainer.Name = strings.TrimSpace(maintainer[0])
if len(maintainer) > 1 {
pkg.Maintainer.Contact = strings.TrimRight(maintainer[1], ">")
}
}
if value, ok := mapped["Original-Maintainer"]; ok {
pkg.OriginalMaintainer = &Maintainer{}
maintainer := strings.SplitN(value.String(), "<", 2)
pkg.OriginalMaintainer.Name = strings.TrimSpace(maintainer[0])
if len(maintainer) > 1 {
pkg.OriginalMaintainer.Contact = strings.TrimRight(maintainer[1], ">")
}
}
if value, ok := mapped["Description"]; ok {
pkg.Description[0] = value.Line(0)
if value.IsMultiline() {
pkg.Description[1] = strings.TrimSpace(strings.Join(value.ToSlice()[1:], "\n"))
}
}
// Remove used keys
delete(mapped, "Package")
delete(mapped, "Version")
delete(mapped, "Architecture")
delete(mapped, "Maintainer")
delete(mapped, "Original-Maintainer")
delete(mapped, "Description")
pkg.Extra = mapped
return nil
}
type Scripts struct {
PreInstall []byte
PreRemove []byte
PostInstall []byte
PostRemove []byte
}
func (scr *Scripts) WriteToTar(prefix string, t *tar.Writer) error {
if prefix != "" && prefix[len(prefix)-1] != '-' {
prefix += "-"
}
// Install
if len(scr.PreInstall) > 0 {
if err := t.WriteHeader(&tar.Header{Name: fmt.Sprintf("%spreinst", prefix), Size: int64(len(scr.PreInstall)), Mode: 0755}); err != nil {
return err
} else if _, err = t.Write(scr.PreInstall); err != nil {
return err
}
}
if len(scr.PostInstall) > 0 {
if err := t.WriteHeader(&tar.Header{Name: fmt.Sprintf("%spostinst", prefix), Size: int64(len(scr.PostInstall)), Mode: 0755}); err != nil {
return err
} else if _, err = t.Write(scr.PostInstall); err != nil {
return err
}
}
// Remove
if len(scr.PreRemove) > 0 {
if err := t.WriteHeader(&tar.Header{Name: fmt.Sprintf("%sprerm", prefix), Size: int64(len(scr.PreRemove)), Mode: 0755}); err != nil {
return err
} else if _, err = t.Write(scr.PreRemove); err != nil {
return err
}
}
if len(scr.PostRemove) > 0 {
if err := t.WriteHeader(&tar.Header{Name: fmt.Sprintf("%spostrm", prefix), Size: int64(len(scr.PostRemove)), Mode: 0755}); err != nil {
return err
} else if _, err = t.Write(scr.PostRemove); err != nil {
return err
}
}
return nil
}
type Dpkg struct {
Size int64 // data.tar size
Ext string // data.tar extension
Pkg *Package // Package info
Scripts, OldScripts, NewScripts *Scripts // Scripts to add to control.tar
}
func (dpkg *Dpkg) WriteTo(w *ar.Writer) error {
controlFile, err := dpkg.Pkg.MarshalBinary()
if err != nil {
return err
}
controlBuffer := &bytes.Buffer{}
control := tar.NewWriter(controlBuffer)
// Control file
control.WriteHeader(&tar.Header{Name: "control", Size: int64(len(controlFile)), Mode: 0644})
control.Write(controlFile)
// Scripts
if dpkg.Scripts != nil {
if err = dpkg.Scripts.WriteToTar("", control); err != nil {
return err
}
}
if dpkg.NewScripts != nil {
if err = dpkg.NewScripts.WriteToTar("new-", control); err != nil {
return err
}
}
if dpkg.OldScripts != nil {
if err = dpkg.OldScripts.WriteToTar("old-", control); err != nil {
return err
}
}
control.Close()
tarControlBuggerGZ := &bytes.Buffer{}
gz := gzip.NewWriter(tarControlBuggerGZ)
if _, err = io.Copy(gz, controlBuffer); err != nil {
return err
}
gz.Close()
if _, err := w.WriteFile(tarControlBuggerGZ.Bytes(), &ar.Header{Filename: "control.tar.gz", Size: int64(len(tarControlBuggerGZ.Bytes()))}); err != nil {
return err
}
return nil
}