Signed-off-by: Matheus Sampaio Queiroga <srherobrine20@gmail.com>
236 lines
6.4 KiB
Go
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
|
|
}
|