/* 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 }