package plugins
import (
"archive/zip"
"errors"
"fmt"
"io"
"path/filepath"
"strings"
)
const (
PackageExtension = ".ndp"
manifestFileName = "manifest.json"
wasmFileName = "plugin.wasm"
)
func validPluginID(id string) bool {
if id == "." || id == ".." || strings.ContainsAny(id, `/\`) || !filepath.IsLocal(id) {
return false
}
return strings.TrimRight(id, ". ") == id
}
func pluginIDFromPath(path string) (string, bool) {
id := strings.TrimSuffix(filepath.Base(path), PackageExtension)
if !validPluginID(id) {
return "", false
}
return id, true
}
type ndpPackage struct {
Manifest *Manifest
WasmBytes []byte
}
func openPackage(ndpPath string) (*ndpPackage, error) {
zr, err := zip.OpenReader(ndpPath)
if err != nil {
return nil, fmt.Errorf("opening package: %w", err)
}
defer zr.Close()
var manifestBytes []byte
var wasmBytes []byte
for _, f := range zr.File {
switch f.Name {
case manifestFileName:
manifestBytes, err = readZipFile(f)
if err != nil {
return nil, fmt.Errorf("reading manifest: %w", err)
}
case wasmFileName:
wasmBytes, err = readZipFile(f)
if err != nil {
return nil, fmt.Errorf("reading wasm: %w", err)
}
}
}
if manifestBytes == nil {
return nil, errors.New("package missing manifest.json")
}
if wasmBytes == nil {
return nil, errors.New("package missing plugin.wasm")
}
manifest, err := ParseManifest(manifestBytes)
if err != nil {
return nil, fmt.Errorf("parsing manifest: %w", err)
}
return &ndpPackage{
Manifest: manifest,
WasmBytes: wasmBytes,
}, nil
}
func ReadManifest(ndpPath string) (*Manifest, error) {
zr, err := zip.OpenReader(ndpPath)
if err != nil {
return nil, fmt.Errorf("opening package: %w", err)
}
defer zr.Close()
for _, f := range zr.File {
if f.Name == manifestFileName {
manifestBytes, err := readZipFile(f)
if err != nil {
return nil, fmt.Errorf("reading manifest: %w", err)
}
manifest, err := ParseManifest(manifestBytes)
if err != nil {
return nil, fmt.Errorf("parsing manifest: %w", err)
}
return manifest, nil
}
}
return nil, errors.New("package missing manifest.json")
}
func readZipFile(f *zip.File) ([]byte, error) {
rc, err := f.Open()
if err != nil {
return nil, err
}
defer rc.Close()
return io.ReadAll(rc)
}