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