303 lines
5.9 KiB
Go
303 lines
5.9 KiB
Go
package packets
|
|
|
|
import (
|
|
"archive/tar"
|
|
"bytes"
|
|
"crypto/ed25519"
|
|
"database/sql"
|
|
"fmt"
|
|
"io"
|
|
"log"
|
|
"net/http"
|
|
"os"
|
|
"packets/internal/build"
|
|
"packets/internal/consts"
|
|
errors_packets "packets/internal/errors"
|
|
"packets/internal/packet"
|
|
"packets/internal/utils"
|
|
"path"
|
|
|
|
utils_lua "packets/internal/utils/lua"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/klauspost/compress/zstd"
|
|
_ "modernc.org/sqlite"
|
|
)
|
|
|
|
type Package struct {
|
|
PackageF []byte
|
|
Version string
|
|
ImageUrl string
|
|
QueryName string
|
|
Description string
|
|
Author string
|
|
AuthorVerified bool
|
|
OS string
|
|
Arch string
|
|
Filename string
|
|
Size int64
|
|
Dependencies map[string]string
|
|
|
|
Signature []byte
|
|
PublicKey ed25519.PublicKey
|
|
|
|
Serial int
|
|
|
|
Manifest packet.PacketLua
|
|
}
|
|
|
|
// Install exctract and fully install from a package file ( tar.zst )
|
|
func InstallPackage(file []byte, destDir string) error {
|
|
manifest, err := packet.ReadPacketFromFile(bytes.NewReader(file))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
zstdReader, err := zstd.NewReader(bytes.NewReader(file))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer zstdReader.Close()
|
|
|
|
tarReader := tar.NewReader(zstdReader)
|
|
|
|
uid, err := utils.GetPacketsUID()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
for {
|
|
hdr, err := tarReader.Next()
|
|
if err == io.EOF {
|
|
break
|
|
}
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
rel := filepath.Clean(hdr.Name)
|
|
|
|
if rel == ".." || strings.HasPrefix(rel, ".."+string(os.PathSeparator)) {
|
|
continue
|
|
}
|
|
|
|
if err := os.MkdirAll(destDir, 0775); err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := os.Chown(destDir, uid, 0); err != nil {
|
|
return err
|
|
}
|
|
|
|
absPath := filepath.Join(destDir, rel)
|
|
|
|
switch hdr.Typeflag {
|
|
|
|
case tar.TypeDir:
|
|
err = os.MkdirAll(absPath, os.FileMode(hdr.Mode))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if err := os.Chown(absPath, uid, 0); err != nil {
|
|
return err
|
|
}
|
|
|
|
case tar.TypeReg:
|
|
|
|
err = os.MkdirAll(filepath.Dir(absPath), 0775)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
out, err := os.Create(absPath)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
_, err = io.Copy(out, tarReader)
|
|
out.Close()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
err = os.Chmod(absPath, os.FileMode(0775))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if filepath.Base(hdr.Name) == "Packet.lua" {
|
|
err = os.Chmod(absPath, os.FileMode(0755))
|
|
if err != nil {
|
|
return err
|
|
}
|
|
} else {
|
|
if err := os.Chown(absPath, uid, 0); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
L, err := utils_lua.GetSandBox()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
bootstrapcontainer, err := build.NewContainer(manifest)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if err := bootstrapcontainer.ExecutePrepare(manifest, &L); err != nil {
|
|
return fmt.Errorf("error executing prepare: %s", err)
|
|
}
|
|
|
|
if err := bootstrapcontainer.ExecuteBuild(manifest, &L); err != nil {
|
|
return fmt.Errorf("error executing build: %s", err)
|
|
}
|
|
|
|
if err := utils.ChangeToNoPermission(); err != nil {
|
|
return fmt.Errorf("error changing to packet user: %s", err)
|
|
}
|
|
if err := bootstrapcontainer.ExecuteInstall(manifest, &L); err != nil {
|
|
return fmt.Errorf("error executing build: %s", err)
|
|
}
|
|
|
|
if err := utils.ElevatePermission(); err != nil {
|
|
return fmt.Errorf("error changing to root: %s", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func GetPackage(id string) (Package, error) {
|
|
|
|
var this Package
|
|
this.Dependencies = make(map[string]string)
|
|
var peers []Peer
|
|
|
|
db, err := sql.Open("sqlite", consts.IndexDB)
|
|
if err != nil {
|
|
return this, err
|
|
}
|
|
defer db.Close()
|
|
|
|
var packageUrl string
|
|
err = db.QueryRow("SELECT query_name, version, package_url, image_url, description, author, author_verified, os, arch, signature, public_key, serial, size FROM packages WHERE id = ?", id).
|
|
Scan(
|
|
&this.QueryName,
|
|
&this.Version,
|
|
&packageUrl,
|
|
&this.ImageUrl,
|
|
&this.Description,
|
|
&this.Author,
|
|
&this.AuthorVerified,
|
|
&this.OS,
|
|
&this.Arch,
|
|
&this.Signature,
|
|
&this.PublicKey,
|
|
&this.Serial,
|
|
&this.Size,
|
|
)
|
|
if err != nil {
|
|
return Package{}, err
|
|
}
|
|
|
|
rows, err := db.Query("SELECT dependency_name, version_constraint FROM package_dependencies WHERE package_id = ?", id)
|
|
if err != nil {
|
|
return Package{}, err
|
|
}
|
|
defer rows.Close()
|
|
|
|
for rows.Next() {
|
|
var a, vConstraint string
|
|
if err := rows.Scan(&a, &vConstraint); err != nil {
|
|
return Package{}, err
|
|
}
|
|
|
|
this.Dependencies[a] = vConstraint
|
|
}
|
|
|
|
filename := path.Base(packageUrl)
|
|
this.Filename = filename
|
|
|
|
dirEntry, err := os.ReadDir(consts.DefaultCache_d)
|
|
if err != nil {
|
|
return Package{}, err
|
|
}
|
|
|
|
for _, v := range dirEntry {
|
|
if v.Name() == filename {
|
|
this.PackageF, err = os.ReadFile(filepath.Join(consts.DefaultCache_d, filename))
|
|
if err != nil {
|
|
break
|
|
}
|
|
goto skipping
|
|
|
|
}
|
|
}
|
|
|
|
peers, err = AskLAN(filename)
|
|
if err != nil {
|
|
return Package{}, err
|
|
}
|
|
|
|
if len(peers) == 0 {
|
|
fmt.Printf(":: Pulling from %s\n", packageUrl)
|
|
this.PackageF, err = getFileHTTP(packageUrl)
|
|
if err != nil {
|
|
return Package{}, err
|
|
}
|
|
} else {
|
|
var totalerrors int = 0
|
|
for _, peer := range peers {
|
|
fmt.Printf(":: Pulling from local network (%s)\n", peer.IP)
|
|
this.PackageF, err = getFileHTTP(fmt.Sprintf("http://%s:%d/%s", peer.IP, peer.Port, filename))
|
|
if err == nil {
|
|
break
|
|
} else {
|
|
totalerrors++
|
|
}
|
|
}
|
|
if totalerrors == len(peers) {
|
|
this.PackageF, err = getFileHTTP(packageUrl)
|
|
if err != nil {
|
|
return Package{}, err
|
|
}
|
|
}
|
|
}
|
|
|
|
skipping:
|
|
|
|
reader := bytes.NewReader(this.PackageF)
|
|
this.Manifest, err = packet.ReadPacketFromFile(reader)
|
|
if err != nil {
|
|
return Package{}, err
|
|
}
|
|
|
|
if !ed25519.Verify(this.PublicKey, this.PackageF, this.Signature) {
|
|
return Package{}, errors_packets.ErrInvalidSignature
|
|
}
|
|
|
|
return this, nil
|
|
}
|
|
|
|
func getFileHTTP(url string) ([]byte, error) {
|
|
resp, err := http.Get(url)
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
if resp.StatusCode != http.StatusOK {
|
|
return nil, errors_packets.ErrResponseNot200OK
|
|
}
|
|
|
|
fileBytes, err := io.ReadAll(resp.Body)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
return fileBytes, nil
|
|
}
|