diff --git a/rxsm-updater/rxsm-updater.go b/rxsm-updater/rxsm-updater.go new file mode 100644 index 0000000..7db57e7 --- /dev/null +++ b/rxsm-updater/rxsm-updater.go @@ -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 +} diff --git a/rxsm-updater/update.go b/rxsm-updater/update.go new file mode 100644 index 0000000..0f42fdb --- /dev/null +++ b/rxsm-updater/update.go @@ -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 +}