b867612961
Note that the cmd is and will always be licensed under GPL, NOT LGPL, like the rest of the program.
384 lines
12 KiB
Go
384 lines
12 KiB
Go
package common
|
|
|
|
import (
|
|
"crypto/ed25519"
|
|
"crypto/x509"
|
|
"encoding/pem"
|
|
"errors"
|
|
"eternity/lib"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"github.com/fatih/color"
|
|
)
|
|
|
|
var DefaultLogger = lib.Logger{
|
|
LogFunc: func(log lib.Log) string {
|
|
var severityPretty string
|
|
switch log.Level {
|
|
case "INFO":
|
|
severityPretty = color.GreenString("[INFO]")
|
|
case "WARN":
|
|
severityPretty = color.YellowString("[WARN]")
|
|
case "ERROR":
|
|
severityPretty = color.HiYellowString("[ERROR]")
|
|
case "CRITICAL":
|
|
severityPretty = color.HiRedString("[CRITICAL]")
|
|
case "FATAL":
|
|
fmt.Println(color.RedString("[FATAL]"), log.Content)
|
|
os.Exit(1)
|
|
default:
|
|
severityPretty = color.WhiteString("[" + strings.ToUpper(log.Level) + "]")
|
|
}
|
|
fmt.Println(severityPretty, log.Content)
|
|
if log.Prompt {
|
|
fmt.Print(": ")
|
|
var userInput string
|
|
_, err := fmt.Scanln(&userInput)
|
|
if err != nil {
|
|
fmt.Println(color.RedString("[FATAL]"), "Failed to read user input:", err)
|
|
os.Exit(1)
|
|
} else {
|
|
return userInput
|
|
}
|
|
}
|
|
return ""
|
|
},
|
|
PromptSupported: true,
|
|
}
|
|
|
|
// EternityJsonHandleError handles errors related to the parsing of eternity.json
|
|
func EternityJsonHandleError(err error, vagueError error, logger *lib.Logger) {
|
|
switch {
|
|
case errors.Is(vagueError, lib.ErrEternityJsonOpenError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to open eternity.json: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrEternityJsonReadError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to reading eternity.json: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrEternityJsonParseError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to parse eternity.json: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrEternityJsonMapError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to map eternity.json: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
}
|
|
}
|
|
|
|
// BuildEPKRemoveTemporaryDirectory removes the temporary directory created by BuildEPK
|
|
func BuildEPKRemoveTemporaryDirectory(tempDir string, logger *lib.Logger) {
|
|
err := os.RemoveAll(tempDir)
|
|
if err != nil {
|
|
logger.LogFunc(lib.Log{
|
|
Level: "ERROR",
|
|
Content: "Failed to cleanup temporary directory: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
}
|
|
}
|
|
|
|
// BuildEPKHandleError handles errors related to the building of EPKs
|
|
func BuildEPKHandleError(tempDir string, err error, vagueError error, logger *lib.Logger) {
|
|
switch {
|
|
case errors.Is(vagueError, lib.ErrBuildEPKChrootError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Chroot containers are not yet supported",
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKUnrestrictedError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Unrestricted builds are not yet supported",
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKTemporaryDirectoryError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create temporary directory: " + err.Error(),
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKCreateHooksError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create hooks directory: " + err.Error(),
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKCopyHooksError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to copy hooks: " + err.Error(),
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKBuildShError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create build.sh: " + err.Error(),
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKWritingBuildShError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to write to build.sh: " + err.Error(),
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKTargetRootError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create target root: " + err.Error(),
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKExecutingBuildShError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to start BubbleWrap container and run build.sh in it: " + err.Error(),
|
|
})
|
|
case errors.Is(vagueError, lib.ErrBuildEPKCountingFilesError):
|
|
BuildEPKRemoveTemporaryDirectory(tempDir, logger)
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to count files in target root: " + err.Error(),
|
|
})
|
|
}
|
|
}
|
|
|
|
// These errors are in the wrong order due to this function being rewritten.
|
|
// Oh well, not like it matters.
|
|
|
|
// PackageEPKHandleError handles errors related to the packaging of EPKs
|
|
func PackageEPKHandleError(err error, vagueError error, logger *lib.Logger) {
|
|
switch {
|
|
case errors.Is(vagueError, lib.ErrPackageEPKCreateDistDirError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create dist directory: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKMoveToDistError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to move to dist directory: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKTarError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create Tar archive: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKJSONMarshal):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to marshal JSON: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKCreateCompressionWriterError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create compression writer: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKCompressCloseError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to close compression writer: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKCannotOpenFile):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to open file for writing: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKCannotWriteFile):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to write to file: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKCannotCloseFile):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to close file: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKFailedToSeek):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to move file cursor: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, lib.ErrPackageEPKFailedToWriteHash):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to generate hash: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
}
|
|
}
|
|
|
|
var LoadPrivateKeyHomedirError = errors.New("failed to get home directory")
|
|
var LoadPrivateKeyReadError = errors.New("failed to read private key")
|
|
var LoadPrivateKeyGenerateError = errors.New("failed to generate private key")
|
|
var LoadPrivateKeyMarshalError = errors.New("failed to marshal private key")
|
|
var LoadPrivateKeyFolderCreateError = errors.New("failed to create folder")
|
|
var LoadPrivateKeyWriteError = errors.New("failed to write private key")
|
|
var LoadPrivateKeyEmptyBlockError = errors.New("blocks returned by pem.Decode are empty")
|
|
var LoadPrivateKeyParseError = errors.New("failed to parse private key")
|
|
var LoadPrivateKeyNotEd25519Error = errors.New("private key is not ed25519")
|
|
|
|
// LoadPrivateKey loads the private key from $HOME/.local/share/eternity/private.key
|
|
func LoadPrivateKey(logger *lib.Logger) (ed25519.PrivateKey, error, error) {
|
|
// Attempt to read the key from $HOME/.local/share/eternity/private.key
|
|
homeDir, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return nil, err, LoadPrivateKeyHomedirError
|
|
}
|
|
|
|
privateKeyBytes, err := os.ReadFile(filepath.Join(homeDir, ".local/share/eternity/private.key"))
|
|
if err != nil {
|
|
if !os.IsNotExist(err) {
|
|
return nil, err, LoadPrivateKeyReadError
|
|
} else {
|
|
response := logger.LogFunc(lib.Log{
|
|
Level: "INFO",
|
|
Content: "You need to add a private key to sign EPKs. Would you like to generate one now? (y/n)",
|
|
Prompt: true,
|
|
})
|
|
if strings.ToLower(response) == "y" {
|
|
_, privateKey, err := ed25519.GenerateKey(nil)
|
|
if err != nil {
|
|
return nil, err, LoadPrivateKeyGenerateError
|
|
}
|
|
|
|
privateKeyBytes, err := x509.MarshalPKCS8PrivateKey(privateKey)
|
|
if err != nil {
|
|
return nil, err, LoadPrivateKeyMarshalError
|
|
}
|
|
|
|
privateKeyFile := pem.EncodeToMemory(&pem.Block{
|
|
Type: "ED25519 PRIVATE KEY",
|
|
Bytes: privateKeyBytes,
|
|
})
|
|
|
|
err = os.MkdirAll(filepath.Join(homeDir, ".local/share/eternity"), 0700)
|
|
if err != nil {
|
|
return nil, err, LoadPrivateKeyFolderCreateError
|
|
}
|
|
|
|
err = os.WriteFile(filepath.Join(homeDir, ".local/share/eternity/private.key"), privateKeyFile, 0600)
|
|
if err != nil {
|
|
return nil, err, LoadPrivateKeyWriteError
|
|
}
|
|
|
|
logger.LogFunc(lib.Log{
|
|
Level: "INFO",
|
|
Content: "Private key written to disk",
|
|
Prompt: false,
|
|
})
|
|
|
|
return privateKey, nil, nil
|
|
} else if strings.ToLower(response) == "n" {
|
|
logger.LogFunc(lib.Log{
|
|
Level: "INFO",
|
|
Content: "Understood. Please add a ed25519 private key to " + filepath.Join(homeDir, ".local/share/eternity/private.key") + " in order to sign EPKs and repositories",
|
|
Prompt: false,
|
|
})
|
|
|
|
os.Exit(1)
|
|
}
|
|
}
|
|
}
|
|
|
|
block, _ := pem.Decode(privateKeyBytes)
|
|
if block == nil {
|
|
return nil, nil, LoadPrivateKeyEmptyBlockError
|
|
}
|
|
|
|
privateKey, err := x509.ParsePKCS8PrivateKey(block.Bytes)
|
|
if err != nil {
|
|
return nil, err, LoadPrivateKeyParseError
|
|
}
|
|
|
|
ed25519PrivateKey, ok := privateKey.(ed25519.PrivateKey)
|
|
if !ok {
|
|
return nil, nil, LoadPrivateKeyNotEd25519Error
|
|
}
|
|
|
|
return ed25519PrivateKey, nil, nil
|
|
}
|
|
|
|
// LoadPrivateKeyHandleError handles errors related to LoadPrivateKey
|
|
func LoadPrivateKeyHandleError(err error, vagueError error, logger *lib.Logger) {
|
|
switch {
|
|
case errors.Is(vagueError, LoadPrivateKeyHomedirError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to get home directory: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyReadError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to read private key: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyGenerateError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to generate private key: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyMarshalError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to marshal private key: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyFolderCreateError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to create eternity config folder (are you missing permissions?): " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyWriteError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to write private key to disk: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyEmptyBlockError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Blocks returned by pem.Decode are empty",
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyParseError):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Failed to parse private key: " + err.Error(),
|
|
Prompt: false,
|
|
})
|
|
case errors.Is(vagueError, LoadPrivateKeyNotEd25519Error):
|
|
logger.LogFunc(lib.Log{
|
|
Level: "FATAL",
|
|
Content: "Private key is not a ed25519 key",
|
|
Prompt: false,
|
|
})
|
|
}
|
|
}
|