118 lines
2.6 KiB
Go
118 lines
2.6 KiB
Go
//go:build windows
|
|
|
|
package update
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"syscall"
|
|
)
|
|
|
|
// Apply downloads the new build into the user Downloads folder.
|
|
// It does NOT replace the running exe or spawn a self-updater (AV false-positive pattern).
|
|
func Apply(ctx context.Context, manifestURL string) (string, error) {
|
|
st, err := Check(ctx, manifestURL)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if !st.Available {
|
|
if st.Error != "" {
|
|
return "", fmt.Errorf("%s", st.Error)
|
|
}
|
|
return "", fmt.Errorf("обновление не найдено")
|
|
}
|
|
if !allowedDownloadURL(st.URL) {
|
|
return "", fmt.Errorf("URL обновления должен быть на git.evilfox.cc или files.de4ima.uk")
|
|
}
|
|
|
|
dir, err := downloadsDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
_ = os.MkdirAll(dir, 0o755)
|
|
dest := filepath.Join(dir, fmt.Sprintf("Navis-%s-windows-amd64.exe", sanitizeFileVer(st.Latest)))
|
|
tmp := dest + ".part"
|
|
_ = os.Remove(tmp)
|
|
_ = os.Remove(dest)
|
|
|
|
if err := downloadFile(ctx, st.URL, tmp); err != nil {
|
|
return "", err
|
|
}
|
|
if st.SHA256 != "" {
|
|
sum, err := fileSHA256(tmp)
|
|
if err != nil {
|
|
_ = os.Remove(tmp)
|
|
return "", err
|
|
}
|
|
if !equalFoldHex(sum, st.SHA256) {
|
|
_ = os.Remove(tmp)
|
|
return "", fmt.Errorf("checksum mismatch")
|
|
}
|
|
}
|
|
if err := os.Rename(tmp, dest); err != nil {
|
|
_ = os.Remove(tmp)
|
|
return "", err
|
|
}
|
|
|
|
_ = revealInExplorer(dest)
|
|
return fmt.Sprintf("Скачано v%s:\n%s\nЗакройте Navis и запустите новый файл.", st.Latest, dest), nil
|
|
}
|
|
|
|
func downloadsDir() (string, error) {
|
|
if u := os.Getenv("USERPROFILE"); u != "" {
|
|
return filepath.Join(u, "Downloads"), nil
|
|
}
|
|
home, err := os.UserHomeDir()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
return filepath.Join(home, "Downloads"), nil
|
|
}
|
|
|
|
func revealInExplorer(path string) error {
|
|
path, err := filepath.Abs(path)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
cmd := exec.Command("explorer.exe", "/select,", path)
|
|
const createNoWindow = 0x08000000
|
|
cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: createNoWindow}
|
|
return cmd.Start()
|
|
}
|
|
|
|
func sanitizeFileVer(v string) string {
|
|
out := make([]rune, 0, len(v))
|
|
for _, r := range v {
|
|
switch {
|
|
case r >= '0' && r <= '9', r >= 'a' && r <= 'z', r >= 'A' && r <= 'Z', r == '.', r == '-':
|
|
out = append(out, r)
|
|
}
|
|
}
|
|
if len(out) == 0 {
|
|
return "update"
|
|
}
|
|
return string(out)
|
|
}
|
|
|
|
func equalFoldHex(a, b string) bool {
|
|
if len(a) != len(b) {
|
|
return false
|
|
}
|
|
for i := 0; i < len(a); i++ {
|
|
ca, cb := a[i], b[i]
|
|
if ca >= 'A' && ca <= 'F' {
|
|
ca += 'a' - 'A'
|
|
}
|
|
if cb >= 'A' && cb <= 'F' {
|
|
cb += 'a' - 'A'
|
|
}
|
|
if ca != cb {
|
|
return false
|
|
}
|
|
}
|
|
return true
|
|
}
|