Browse Source

Create updater

master
NGnius (Graham) 5 years ago
parent
commit
e9dd07f202
2 changed files with 209 additions and 0 deletions
  1. +117
    -0
      rxsm-updater/rxsm-updater.go
  2. +92
    -0
      rxsm-updater/update.go

+ 117
- 0
rxsm-updater/rxsm-updater.go View File

@@ -0,0 +1,117 @@
// Created 2019-09-17 by NGnius

package main

import (
"runtime"
"os"
"io"
"log"
"archive/zip"
"path/filepath"
)

const (
DownloadTempFile string = "rxsm-update.zip"
)

func main() {
InstallRXSMUpdate(func(int, string){})
// TODO: implement callback for Qt window
}

func InstallRXSMUpdate(statusCallback func(progress int, description string)) {
f, openErr := os.Open(DownloadTempFile)
if openErr != nil {
log.Println("Error opening zip file")
log.Println(openErr)
return
}
statusCallback(1, "Extracting")
f.Sync()
f.Seek(0,0)
fStat, statErr := f.Stat()
if statErr != nil {
log.Println("Error retrieving file stats")
log.Println(statErr)
statusCallback(-1, "Extracting failed")
}
unpackErr := unpackRXSMInstall(f, fStat.Size(), func(p int, d string){statusCallback(1, d)})
if unpackErr != nil {
log.Println("Error extracting update")
log.Println(unpackErr)
statusCallback(-1, "Extracting failed")
return
}
statusCallback(2, "Installing")
// TODO: is there installation required?
rmErr := os.Remove(DownloadTempFile)
if rmErr != nil {
log.Println("Error removing download file")
log.Println(rmErr)
statusCallback(-1, "Installing failed")
return
}
}

func unpackRXSMInstallPath(path string) (error) {
f, openErr := os.Open(path)
if openErr != nil {
return openErr
}
defer f.Close()
fStat, statErr := f.Stat()
if statErr != nil {
return statErr
}
return unpackRXSMInstall(f, fStat.Size(), func(int, string){})
}

func unpackRXSMInstall(reader io.ReaderAt, size int64, statusCallback func(progress int, description string)) (error) {
// progress is out of 100
// open zip
zipFile, zipErr := zip.NewReader(reader, size)
if zipErr != nil {
return zipErr
}
// NOTE: this could use go routines, but doesn't to keep your PC (& RXSM) usable while updating
// (And also to make the loading bar coherent)
for i, f := range zipFile.File {
if !f.FileHeader.Mode().IsDir() {
filename := filepath.Clean(f.FileHeader.Name)
statusCallback(100*(i+1)/len(zipFile.File), "Unpacking "+filename)
if filepath.IsAbs(filename) {
if runtime.GOOS == "windows" {
filename = filename[4:] // D:\\path\to\keep -> path\to\keep
} else {
filename = filename[1:] // /path/to/keep -> path/to/keep
}
}
if len(filename) > len(runtime.GOOS)+1 {
// remove windows/ or darwin/ or linux/ from path if exists
if filename[:len(runtime.GOOS)] == runtime.GOOS {
filename = filename[len(runtime.GOOS)+1:]
}
}
dirErr := os.MkdirAll(filepath.Dir(filename), os.ModeDir | os.ModePerm)
if dirErr != nil {
log.Println("Dir err "+filepath.Dir(filename))
return dirErr
}
newFile, createErr := os.Create(filename)
if createErr != nil {
return createErr
}
srcFileReadCloser, zipOpenErr := f.Open()
if zipOpenErr != nil {
return zipOpenErr
}
defer srcFileReadCloser.Close()
_, copyErr := io.Copy(newFile, srcFileReadCloser)
if copyErr != nil {
return copyErr
}
}
}
return nil
}

+ 92
- 0
rxsm-updater/update.go View File

@@ -0,0 +1,92 @@
// Created 2019-09-12 by NGnius

package main

import (
"net/http"
"encoding/json"
"io"
"io/ioutil"
"runtime"
"bytes"
"log"
)

const (
DNT_ON = "1"
DNT_OFF = "0"
)

var (
ExtraHeader map[string][]string = make(map[string][]string)
client http.Client = http.Client{}
)

type updateStruct struct {
Status int `json:"status"`
Reason string `json:"reason"`
Url string `json:"url"`
IsOutOfDate bool `json:"out-of-date"`
}

func CheckForUpdate(baseURL string, version string, platform string) (downloadURL string, isOutOfDate bool, ok bool) {
body_map := make(map[string]string)
body_map["version"] = version
body_map["platform"] = platform
body_bytes, marshalErr := json.Marshal(body_map)
if marshalErr != nil {
log.Println(marshalErr)
return
}
req, _ := http.NewRequest("POST", baseURL+"/"+"update", bytes.NewReader(body_bytes))
for key, elem := range ExtraHeader {
req.Header[key] = elem
}
resp, httpErr := client.Do(req)
if httpErr != nil || resp.StatusCode != 200 {
log.Println(req.Header)
log.Println(httpErr)
if resp != nil {
log.Println(resp.StatusCode)
}
return
}
defer resp.Body.Close()
resp_struct := updateStruct{}
resp_body_bytes, readAllErr := ioutil.ReadAll(resp.Body)
if readAllErr != nil {
log.Println(readAllErr)
return
}
unmarshalErr := json.Unmarshal(resp_body_bytes, &resp_struct)
if unmarshalErr != nil {
log.Println(unmarshalErr)
return
}
isOutOfDate = resp_struct.IsOutOfDate
downloadURL = resp_struct.Url
ok = true
return
}

func SimpleCheckForUpdate(baseURL string, version string) (downloadURL string, isOutOfDate bool, ok bool) {
return CheckForUpdate(baseURL, version, runtime.GOOS+"/"+runtime.GOARCH)
}

func DownloadUpdate(downloadURL string, dest io.Writer) (ok bool) {
req, _ := http.NewRequest("GET", downloadURL, nil)
for key, elem := range ExtraHeader {
req.Header[key] = elem
}
resp, httpErr := client.Do(req)
if httpErr != nil || resp.StatusCode != 200 {
return
}
defer resp.Body.Close()
_, copyErr := io.Copy(dest, resp.Body)
if copyErr != nil {
return
}
ok = true
return
}