mirror of
https://github.com/chenjia404/go-p2ptunnel.git
synced 2025-10-05 15:27:02 +08:00
406 lines
9.5 KiB
Go
406 lines
9.5 KiB
Go
package update
|
||
|
||
import (
|
||
"archive/tar"
|
||
"archive/zip"
|
||
"bytes"
|
||
"compress/gzip"
|
||
"crypto/sha512"
|
||
"encoding/hex"
|
||
"fmt"
|
||
"io"
|
||
"net/http"
|
||
"os"
|
||
"path"
|
||
"path/filepath"
|
||
"runtime"
|
||
"strconv"
|
||
"strings"
|
||
"time"
|
||
|
||
"github.com/ProtonMail/go-crypto/openpgp"
|
||
"github.com/polydawn/refmt/json"
|
||
)
|
||
|
||
func CheckGithubVersion(Version string) {
|
||
githubName := "go-p2ptunnel"
|
||
githubPath := "chenjia404/go-p2ptunnel"
|
||
|
||
archivesFormat := "tar.gz"
|
||
if runtime.GOOS == "windows" {
|
||
archivesFormat = "zip"
|
||
}
|
||
|
||
r, err := http.Get(fmt.Sprintf("https://api.github.com/repos/%s/releases/latest", githubPath))
|
||
if err != nil {
|
||
return
|
||
}
|
||
b, err := io.ReadAll(r.Body)
|
||
var v interface{}
|
||
err = json.Unmarshal(b, &v)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
|
||
data := v.(map[string]interface{})
|
||
|
||
githubVerion := fmt.Sprintf("%s", data["tag_name"])
|
||
githubVerion = strings.Replace(githubVerion, "v", "", 1)
|
||
if compareVersion(githubVerion, Version) > 0 {
|
||
fmt.Println("GitHub版本更高")
|
||
} else {
|
||
fmt.Println("不需要升级")
|
||
return
|
||
}
|
||
|
||
githubPublishedTime, _ := time.ParseInLocation("2006-01-02T15:04:05Z", fmt.Sprintf("%s", data["published_at"]), time.Local)
|
||
if time.Now().Sub(githubPublishedTime) < (time.Second * 3600) {
|
||
fmt.Println("更新时间不足1个小时,延迟更新")
|
||
return
|
||
}
|
||
updateFileUrl := fmt.Sprintf("https://github.com/%s/releases/download/v%s/%s_%s_%s_%s.%s", githubPath, githubVerion, githubName, githubVerion, runtime.GOOS, runtime.GOARCH, archivesFormat)
|
||
// Get the data
|
||
resp, err := http.Get(updateFileUrl)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
|
||
if resp.StatusCode == 404 {
|
||
fmt.Println("文件不存在,404错误" + updateFileUrl)
|
||
return
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 创建一个文件用于保存
|
||
out, err := os.Create("update." + archivesFormat)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
}
|
||
defer out.Close()
|
||
|
||
// 然后将响应流和文件流对接起来
|
||
_, err = io.Copy(out, resp.Body)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
} else {
|
||
fmt.Println("下载最新安装包成功")
|
||
}
|
||
|
||
out, err = os.Open("update." + archivesFormat)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
}
|
||
h := sha512.New()
|
||
if _, err := io.Copy(h, out); err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
|
||
fileSha512 := hex.EncodeToString(h.Sum(nil))
|
||
|
||
checksumsFileURL := fmt.Sprintf("https://github.com/%s/releases/download/v%s/checksums.txt", githubPath, githubVerion)
|
||
r, err = http.Get(checksumsFileURL)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
b, err = io.ReadAll(r.Body)
|
||
checksums := string(b)
|
||
if strings.Index(checksums, fileSha512) < 0 {
|
||
|
||
fmt.Println("文件sha512错误" + fileSha512)
|
||
return
|
||
}
|
||
|
||
ascFileURL := fmt.Sprintf("https://github.com/%s/releases/download/v%s/%s_%s_%s_%s.%s.asc", githubPath, githubVerion, githubName, githubVerion, runtime.GOOS, runtime.GOARCH, archivesFormat)
|
||
err = DownloadFile(ascFileURL, fmt.Sprintf("update.%s.asc", archivesFormat))
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
|
||
Verify, err := VerifySignature(fmt.Sprintf("update.%s", archivesFormat))
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
if !Verify {
|
||
fmt.Println("gpg签名不通过")
|
||
return
|
||
}
|
||
|
||
exeFilename, _ := os.Executable()
|
||
|
||
//删除老文件
|
||
if FileExists(path.Base(exeFilename) + ".old") {
|
||
err = os.Remove(path.Base(exeFilename) + ".old")
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
}
|
||
|
||
err = os.Rename(path.Base(exeFilename), path.Base(exeFilename)+".old")
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
|
||
if archivesFormat == "zip" {
|
||
|
||
err = Unzip(fmt.Sprintf("update.%s", archivesFormat), ".")
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
} else {
|
||
err = UnTarGz(fmt.Sprintf("update.%s", archivesFormat), "")
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return
|
||
}
|
||
}
|
||
|
||
fmt.Println("current version: ", Version)
|
||
fmt.Println("Update to version: ", githubVerion)
|
||
fmt.Println("Ready to restart")
|
||
os.Exit(0)
|
||
}
|
||
|
||
func Unzip(zipPath, dstDir string) error {
|
||
// open zip file
|
||
reader, err := zip.OpenReader(zipPath)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer reader.Close()
|
||
for _, file := range reader.File {
|
||
if err := unzipFile(file, dstDir); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
|
||
func UnTarGz(tarFile, dest string) error {
|
||
srcFile, err := os.Open(tarFile)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer srcFile.Close()
|
||
gr, err := gzip.NewReader(srcFile)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer gr.Close()
|
||
tr := tar.NewReader(gr)
|
||
for {
|
||
hdr, err := tr.Next()
|
||
if err != nil {
|
||
if err == io.EOF {
|
||
break
|
||
} else {
|
||
return err
|
||
}
|
||
}
|
||
if hdr.Typeflag != tar.TypeDir {
|
||
|
||
// Get file information
|
||
fi := hdr.FileInfo()
|
||
filename := dest + hdr.Name
|
||
file, err := createFile(filename)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
io.Copy(file, tr)
|
||
// Set the file permission, so that it can be guaranteed to be the same as the original file permission. If not set, it will be set according to the umask of the current system.
|
||
os.Chmod(fi.Name(), fi.Mode().Perm())
|
||
}
|
||
}
|
||
return nil
|
||
}
|
||
func createFile(name string) (*os.File, error) {
|
||
if strings.LastIndex(name, "/") >= 0 {
|
||
|
||
err := os.MkdirAll(string([]rune(name)[0:strings.LastIndex(name, "/")]), 0755)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
}
|
||
return os.Create(name)
|
||
}
|
||
|
||
func unzipFile(file *zip.File, dstDir string) error {
|
||
// create the directory of file
|
||
filePath := path.Join(dstDir, file.Name)
|
||
if file.FileInfo().IsDir() {
|
||
if err := os.MkdirAll(filePath, os.ModePerm); err != nil {
|
||
return err
|
||
}
|
||
return nil
|
||
}
|
||
if err := os.MkdirAll(filepath.Dir(filePath), os.ModePerm); err != nil {
|
||
return err
|
||
}
|
||
|
||
// open the file
|
||
rc, err := file.Open()
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer rc.Close()
|
||
|
||
// create the file
|
||
w, err := os.Create(filePath)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
defer w.Close()
|
||
|
||
w.Chmod(0777)
|
||
|
||
// save the decompressed file content
|
||
_, err = io.Copy(w, rc)
|
||
return err
|
||
}
|
||
|
||
func compareVersion(version1 string, version2 string) int {
|
||
var res int
|
||
ver1Strs := strings.Split(version1, ".")
|
||
ver2Strs := strings.Split(version2, ".")
|
||
ver1Len := len(ver1Strs)
|
||
ver2Len := len(ver2Strs)
|
||
verLen := ver1Len
|
||
if len(ver1Strs) < len(ver2Strs) {
|
||
verLen = ver2Len
|
||
}
|
||
for i := 0; i < verLen; i++ {
|
||
var ver1Int, ver2Int int
|
||
if i < ver1Len {
|
||
ver1Int, _ = strconv.Atoi(ver1Strs[i])
|
||
}
|
||
if i < ver2Len {
|
||
ver2Int, _ = strconv.Atoi(ver2Strs[i])
|
||
}
|
||
if ver1Int < ver2Int {
|
||
res = -1
|
||
break
|
||
}
|
||
if ver1Int > ver2Int {
|
||
res = 1
|
||
break
|
||
}
|
||
}
|
||
return res
|
||
}
|
||
|
||
func DownloadFile(url string, dest string) error {
|
||
// Get the data
|
||
resp, err := http.Get(url)
|
||
|
||
if resp.StatusCode == 404 {
|
||
fmt.Println("文件不存在,404错误" + url)
|
||
return http.ErrMissingFile
|
||
}
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return err
|
||
}
|
||
defer resp.Body.Close()
|
||
|
||
// 创建一个文件用于保存
|
||
out, err := os.Create(dest)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
}
|
||
defer out.Close()
|
||
|
||
_, err = io.Copy(out, resp.Body)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return err
|
||
} else {
|
||
fmt.Println("签名文件下载成功")
|
||
}
|
||
|
||
return nil
|
||
}
|
||
|
||
var publicKey = `-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||
|
||
mQINBGRVILgBEACxqkRKodS2Mfxn6GTYvUDaBSgQCjT/GMqmto38buSing9PCXv6
|
||
QMWko8Ax7cKVkxEKGD+4T+AD2mLfhpjLBlMOcxqBwuJ4YVsWkHH2TLHc/gU3DL9Y
|
||
ajH9Lt8TF+Xin/pBfGdOBXGeKK2Az8RshK5D3w3E89//plL15kaR0BWbVIp6Ne0P
|
||
c5D7BNboRuqJGAY+aYEipWAHLZW5M2dD1wgVjUpZRwWv+qIKuQ+hri+fxehFjz3S
|
||
8ElwqZu8JQHxcO3b3m3j11x1qfekqRvNf/dxMpuS+ymenAjOmDDlarmSTj9RTzrA
|
||
97uYi2meIr5e85yMNk5n8Ks7HOQyQ1K6J7YBodjItO7bp1EE5xSecNsaIT2kBQX3
|
||
0+uga0IsZkA6MIC8caWfkMIXrdyLse4XFywCdOGI3BhrA6QV/7ZAXRBs5HtO6SQO
|
||
eVfDptZ0VCvmWG8v6d5mBJ6081FylHEoDYXfJVwgRo71UR334WBpRJZQNV76p383
|
||
muUSq05IcwjbAdyol26enqO2s5LRNs7OeISAhQ+u2LV6LJK+G23JKbmIuWD7Rhol
|
||
gLDXYukoIlOcY7x++qnqoLT8V1aNFE/4XDAd+/Xq7VdgvKbPZxxEkXj9LMrPBIaS
|
||
9/1Nmiq/ni779pnGCFDS7UUFLJvWjEDgWKnZb8MYBdyvq9T9biecJ2oR6wARAQAB
|
||
tCJjaGVuamlhNDA0IDxjaGVuamlhYmxvZ0BnbWFpbC5jb20+iQJXBBMBCABBFiEE
|
||
4TRiUu1mI2TKN/cWGJvnloM2naMFAmRVILgCGw8FCQPDFwgFCwkIBwICIgIGFQoJ
|
||
CAsCBBYCAwECHgcCF4AACgkQGJvnloM2naMQJA/+OxZGpywGLf+C1Wi9iVsSb0UA
|
||
Xit9yOujEpgttgJBdZcfP/1W5G7Vlt9pEH1ByJ28RHlSrEdMkycYhmvnDPdCTg+c
|
||
x3NtjWP8xWXsWN9upPPnn3ZdtsSDZ2YQOMjunP7mucRW8NofDFytPFgSVb6+NcqM
|
||
9Obcd6gmOY3qoQcv4XofdlP6ObFZxvr/mGKdSBgWgOQivGK8QtimNeC/V5ChJKyl
|
||
rueQJ1RRnGtlTXW3tNPNmYkXeVR/TVZgVHyIBHjlNHRV7V8Wgm+vsNIo7xPD/PHL
|
||
3Kq2pmuz8EpcJpNK1+IYsQJTEx9+Y4E4Vjjp/U6WBjGDWXF5KrdTKMsRsHvhxOW7
|
||
C/u6e9gG/eHPLo5Pw3Dg5MWZh/+dRZ/1kWoKhabp719CCPOh9SBgUdSc8RAoVTwp
|
||
b/UHSPokJPPlpBWU7mdBJ+fCapswHU8Gg4WnwBrm2C+p7GEXZiJ6f5n2Ic9rVu6x
|
||
mkmOANLziPe7kC8T6830d0l2nlyCR/oKoGrQ8+bQNChHHhGWtr4O/uCrES5NK/Xr
|
||
kT6OojW8UeV5ngFa0fFurYcMahHHaoy/S3bduGMk3yiFI8Wh7LkZQO6ugkoesvqv
|
||
YSCJJrSTjHnBCkddmOHpDpvgOe+COOrVCe42PNSovTJ+14rhMTsYOWShLLOdC02L
|
||
/xTrrn8LrU9TVEUWf4I=
|
||
=l1Ub
|
||
-----END PGP PUBLIC KEY BLOCK-----`
|
||
|
||
func VerifySignature(filename string) (bool, error) {
|
||
keyring, err := openpgp.ReadArmoredKeyRing(bytes.NewReader([]byte(publicKey)))
|
||
if err != nil {
|
||
fmt.Println("Read Armored Key Ring: " + err.Error())
|
||
return false, err
|
||
}
|
||
|
||
signature, err := os.Open(filename + ".asc")
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return false, err
|
||
}
|
||
defer signature.Close()
|
||
|
||
verificationTarget, err := os.Open(filename)
|
||
if err != nil {
|
||
fmt.Println(err)
|
||
return false, err
|
||
}
|
||
defer verificationTarget.Close()
|
||
|
||
entity, err := openpgp.CheckArmoredDetachedSignature(keyring, verificationTarget, signature, nil)
|
||
if err != nil {
|
||
fmt.Println("Check Detached Signature: " + err.Error())
|
||
return false, err
|
||
}
|
||
if entity.PrimaryKey.KeyIdString() == "189BE79683369DA3" {
|
||
return true, nil
|
||
} else {
|
||
return false, nil
|
||
}
|
||
|
||
}
|
||
|
||
func FileExists(path string) bool {
|
||
_, err := os.Stat(path) //os.Stat获取文件信息
|
||
if err != nil {
|
||
if os.IsExist(err) {
|
||
return true
|
||
}
|
||
return false
|
||
}
|
||
return true
|
||
}
|