mirror of
https://github.com/PeernetOfficial/core.git
synced 2026-07-19 19:47:49 +01:00
New system submodule to support package files.
This commit is contained in:
@@ -5,6 +5,7 @@ BlockchainGlobal: "data/blockchain global/" # Blockchain global caches block
|
||||
WarehouseMain: "data/warehouse main/" # Warehouse main stores the actual data of files shared by the end-user.
|
||||
SearchIndex: "data/search index/" # Local search index of blockchain records. Empty to disable.
|
||||
GeoIPDatabase: "data/GeoLite2-City.mmdb" # GeoLite2 City database to provide GeoIP information.
|
||||
DataFolder: "data/" # Data folder.
|
||||
|
||||
# Listen defines all IP:Port combinations to listen on. If empty, it will listen on all IPs automatically on available ports.
|
||||
# IPv6 must be in the form "[IPv6]:Port". This setting is only recommended to be set on servers.
|
||||
|
||||
@@ -29,6 +29,7 @@ type Config struct {
|
||||
WarehouseMain string `yaml:"WarehouseMain"` // Warehouse main stores the actual data of files shared by the end-user.
|
||||
SearchIndex string `yaml:"SearchIndex"` // Local search index of blockchain records. Empty to disable.
|
||||
GeoIPDatabase string `yaml:"GeoIPDatabase"` // GeoLite2 City database to provide GeoIP information.
|
||||
DataFolder string `yaml:"DataFolder"` // Data folder.
|
||||
|
||||
// Target for the log messages: 0 = Log file, 1 = Stdout, 2 = Log file + Stdout, 3 = None
|
||||
LogTarget int `yaml:"LogTarget"`
|
||||
|
||||
16
go.mod
16
go.mod
@@ -1,6 +1,6 @@
|
||||
module github.com/PeernetOfficial/core
|
||||
|
||||
go 1.16
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/IncSW/geoip2 v0.1.2
|
||||
@@ -8,10 +8,16 @@ require (
|
||||
github.com/enfipy/locker v1.1.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/gorilla/mux v1.8.0
|
||||
github.com/gorilla/websocket v1.4.2
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a
|
||||
github.com/gorilla/websocket v1.5.0
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de
|
||||
golang.org/x/sys v0.0.0-20220325203850-36772127a21f
|
||||
gopkg.in/ini.v1 v1.66.4
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||
lukechampine.com/blake3 v1.1.7
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 // indirect
|
||||
github.com/stretchr/testify v1.7.1 // indirect
|
||||
)
|
||||
|
||||
39
go.sum
39
go.sum
@@ -2,34 +2,35 @@ github.com/IncSW/geoip2 v0.1.2 h1:v7iAyDiNZjHES45P1JPM3SMvkw0VNeJtz0XSVxkRwOY=
|
||||
github.com/IncSW/geoip2 v0.1.2/go.mod h1:adcasR40vXiUBjtzdaTTKL/6wSf+fgO4M8Gve/XzPUk=
|
||||
github.com/akrylysov/pogreb v0.10.1 h1:FqlR8VR7uCbJdfUob916tPM+idpKgeESDXOA1K0DK4w=
|
||||
github.com/akrylysov/pogreb v0.10.1/go.mod h1:pNs6QmpQ1UlTJKDezuRWmaqkgUE2TuU0YTWyqJZ7+lI=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/enfipy/locker v1.1.0 h1:2zVJ0ky7cS1Vjs0x6OQWFiT2dSEiHrI5/O2KCz1fgGc=
|
||||
github.com/enfipy/locker v1.1.0/go.mod h1:uuj+dvWHECshK8rkHcw+ZOb9SLo16yc0Em/JGUqRqko=
|
||||
github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
|
||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
|
||||
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
|
||||
github.com/gorilla/websocket v1.4.2 h1:+/TMaTYc4QFitKJxsQ7Yye35DkWvkdLcvGKqM+x0Ufc=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
|
||||
github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc=
|
||||
github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838 h1:71vQrMauZZhcTVK6KdYM+rklehEEwb3E+ZhaE5jrPrE=
|
||||
golang.org/x/crypto v0.0.0-20220131195533-30dcbda58838/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd h1:O7DYs+zxREGLKzKoMQrtrEacpb0ZVXA5rIwylE2Xchk=
|
||||
golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a h1:ppl5mZgokTT8uPkmYOyEUmPTr3ypaKkg5eFOGrAmxxE=
|
||||
golang.org/x/sys v0.0.0-20220204135822-1c1b9b1eba6a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
|
||||
github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.7.1 h1:5TQK59W5E3v0r2duFAb7P95B6hEeOyEnHRa8MjYSMTY=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064 h1:S25/rfnfsMVgORT4/J61MJ7rdyseOZOyvLIrZEZ7s6s=
|
||||
golang.org/x/crypto v0.0.0-20220321153916-2c7772ba3064/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc=
|
||||
golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
|
||||
golang.org/x/sys v0.0.0-20220325203850-36772127a21f h1:TrmogKRsSOxRMJbLYGrB4SBbW+LJcEllYBLME5Zk5pU=
|
||||
golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/ini.v1 v1.66.4 h1:SsAcf+mM7mRZo2nJNGt8mZCjG8ZRaNGMURJw7BsIST4=
|
||||
gopkg.in/ini.v1 v1.66.4/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b h1:h8qDotaEPuJATrMmW04NCwg7v22aHH28wwpauUhK9Oo=
|
||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
lukechampine.com/blake3 v1.1.7 h1:GgRMhmdsuK8+ii6UZFDL8Nb+VyMwadAgcJyfYHxG6n0=
|
||||
|
||||
91
system/Execute.go
Normal file
91
system/Execute.go
Normal file
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
File Name: Execute.go
|
||||
Copyright: 2021 Peernet s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Execute the actions described in the info header
|
||||
func (update *UpdatePackage) Execute(DataFolder, PluginFolder string) (err error) {
|
||||
for _, action := range update.Header.Actions {
|
||||
switch action.Action {
|
||||
case "extract":
|
||||
destination := resolveFolders(action.Target, DataFolder, PluginFolder)
|
||||
|
||||
for _, f := range update.Reader.File {
|
||||
// filter the filename based on source
|
||||
if !strings.HasPrefix(f.Name, action.Source) {
|
||||
continue
|
||||
}
|
||||
|
||||
err := unzipFile(f, destination)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Deletes the update package file
|
||||
func (update *UpdatePackage) Delete() (err error) {
|
||||
return os.Remove(update.Filename)
|
||||
}
|
||||
|
||||
func unzipFile(f *zip.File, destination string) error {
|
||||
// 4. Check if file paths are not vulnerable to Zip Slip
|
||||
filePath := filepath.Join(destination, f.Name)
|
||||
if !strings.HasPrefix(filePath, filepath.Clean(destination)+string(os.PathSeparator)) {
|
||||
return errors.New("invalid file path: " + filePath)
|
||||
}
|
||||
|
||||
// 5. Create directory tree
|
||||
if f.FileInfo().IsDir() {
|
||||
if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 6. Create a destination file for unzipped content
|
||||
destinationFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer destinationFile.Close()
|
||||
|
||||
// 7. Unzip the content of a file and copy it to the destination file
|
||||
zippedFile, err := f.Open()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zippedFile.Close()
|
||||
|
||||
if _, err := io.Copy(destinationFile, zippedFile); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func resolveFolders(folder, dataFolder, pluginFolder string) (resolved string) {
|
||||
resolved = strings.Replace(folder, "%plugin%", pluginFolder, 1)
|
||||
resolved = strings.Replace(resolved, "%data%", dataFolder, 1)
|
||||
|
||||
return resolved
|
||||
}
|
||||
137
system/Read Package.go
Normal file
137
system/Read Package.go
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
File Name: Read Package.go
|
||||
Copyright: 2021 Peernet s.r.o.
|
||||
Author: Peter Kleissner
|
||||
*/
|
||||
|
||||
package system
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"io/ioutil"
|
||||
"path"
|
||||
"strings"
|
||||
|
||||
"gopkg.in/ini.v1"
|
||||
)
|
||||
|
||||
type UpdatePackage struct {
|
||||
Filename string // Filename of the update package
|
||||
Err error // Parsing error if any
|
||||
Header *IniFile // Header info
|
||||
Reader *zip.ReadCloser // Access to files in the ZIP file
|
||||
}
|
||||
|
||||
// IniFile contains the parsed data from the info.ini file
|
||||
type IniFile struct {
|
||||
Name string // Name of the package
|
||||
Organization string // Organization that created this package
|
||||
Architecture string // Target architecture. For example "windows/amd64".
|
||||
Actions []IniAction // Actions to take
|
||||
}
|
||||
|
||||
type IniAction struct {
|
||||
Action string `ini:"action"` // Action: extract, delete
|
||||
Source string `ini:"source"` // Folder or file in the ZIP file
|
||||
Target string `ini:"target"` // Target folder or file. Certain virtual folders such as "%plugins%" are supported.
|
||||
}
|
||||
|
||||
const IniFilename = "info.ini"
|
||||
|
||||
// ParseUpdateFiles returns a list of parsed update packages.
|
||||
// It will check each file in the directory if a ZIP file containing a valid info.ini file.
|
||||
// The caller must close all returned readers.
|
||||
func ParseUpdateFiles(Directory string) (files []UpdatePackage, err error) {
|
||||
// check all files in the directory
|
||||
filesDir, err := ioutil.ReadDir(Directory)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, file := range filesDir {
|
||||
if file.IsDir() {
|
||||
continue
|
||||
}
|
||||
|
||||
filename := file.Name()
|
||||
|
||||
// ZIP file?
|
||||
if strings.ToLower(path.Ext(filename)) == ".zip" {
|
||||
filenamePath := path.Join(Directory, filename)
|
||||
|
||||
// check if the ZIP archive contains a info.ini file
|
||||
reader, err := zip.OpenReader(filenamePath)
|
||||
if err != nil { // invalid ZIP file
|
||||
continue
|
||||
}
|
||||
|
||||
// read info.ini file
|
||||
file, err := reader.Open(IniFilename)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
data, err := ioutil.ReadAll(file)
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
header, err := ParseIniFile(data)
|
||||
|
||||
files = append(files, UpdatePackage{Filename: filenamePath, Err: err, Header: header, Reader: reader})
|
||||
}
|
||||
}
|
||||
|
||||
return files, nil
|
||||
}
|
||||
|
||||
func ParseIniFile(data []byte) (header *IniFile, err error) {
|
||||
inidata, err := ini.Load(data)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// parse the main section first
|
||||
section, err := inidata.GetSection("main")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name, err := section.GetKey("name")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
organization, err := section.GetKey("organization")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
architecture, err := section.GetKey("architecture")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
header = &IniFile{
|
||||
Name: name.String(),
|
||||
Organization: organization.String(),
|
||||
Architecture: architecture.String(),
|
||||
}
|
||||
|
||||
// parse any other section
|
||||
for _, section := range inidata.Sections() {
|
||||
if section.Name() == "main" || section.Name() == "DEFAULT" {
|
||||
continue
|
||||
}
|
||||
|
||||
var action IniAction
|
||||
|
||||
if section.MapTo(&action) != nil {
|
||||
continue
|
||||
}
|
||||
|
||||
header.Actions = append(header.Actions, action)
|
||||
}
|
||||
|
||||
return header, nil
|
||||
}
|
||||
52
system/readme.md
Normal file
52
system/readme.md
Normal file
@@ -0,0 +1,52 @@
|
||||
# System
|
||||
|
||||
This submodule defines how files and plugins are managed and updated.
|
||||
|
||||
## Package Files
|
||||
|
||||
Package files are ZIP files containing a file `info.ini` that describes which actions to take. They provide updates and installation of individual files, plugins, or entire clients.
|
||||
|
||||
Actions may specify target files or folders. Such paths may use virtual paths that will be automatically resolved.
|
||||
|
||||
```
|
||||
%plugin% = Plugin folder
|
||||
%data% = Data folder
|
||||
```
|
||||
|
||||
## Sample Package File
|
||||
|
||||
A sample package file `TextViewer.zip` looks like this:
|
||||
|
||||
```
|
||||
│ info.ini
|
||||
│
|
||||
└───TextViewer
|
||||
Microsoft.Extensions.DependencyInjection.Abstractions.dll
|
||||
Peernet.Browser.Plugins.TextViewer.deps.json
|
||||
Peernet.Browser.Plugins.TextViewer.dll
|
||||
Peernet.SDK.dll
|
||||
```
|
||||
|
||||
The `info.ini` file:
|
||||
|
||||
```ini
|
||||
[main]
|
||||
name = "Text Viewer Plugin"
|
||||
organization = "Peernet s.r.o."
|
||||
architecture = "windows/amd64"
|
||||
|
||||
[action1]
|
||||
action = extract
|
||||
source = TextViewer
|
||||
target = "%plugin%"
|
||||
```
|
||||
|
||||
## Security Implications
|
||||
|
||||
The actions currently only allow to extract any arbitrary file to the local disk. Later, the destination directory will be limited to the Peernet installation directory.
|
||||
|
||||
Signatures for packages are not implemented yet. They will be added in a subsequent version.
|
||||
|
||||
## Target OS
|
||||
|
||||
It is up to the client to select and download the appropriate packages.
|
||||
Reference in New Issue
Block a user