Skip to content

Commit

Permalink
Windows installer now installs and does progress correctly. Also adds…
Browse files Browse the repository at this point in the history
… to registry and creates start menu shortcut. Broken at the moment, though

Former-commit-id: fe4e071
  • Loading branch information
kercre123 committed Nov 13, 2023
1 parent 15b996a commit a0ebb94
Show file tree
Hide file tree
Showing 17 changed files with 335 additions and 47 deletions.
Binary file added chipper/cmd/windows/app.syso
Binary file not shown.
5 changes: 5 additions & 0 deletions chipper/cmd/windows/rc/app.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
id ICON "windows/icons/ico/pod16x16.ico"
id ICON "windows/icons/ico/pod24x24.ico"
id ICON "windows/icons/ico/pod32x32.ico"
id ICON "windows/icons/ico/pod48x48.ico"
id ICON "windows/icons/ico/pod256x256.ico"
Binary file removed chipper/cmd/wire-pod-installer/ico/pod.ico
Binary file not shown.
135 changes: 135 additions & 0 deletions chipper/cmd/wire-pod-installer/install.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package main

import (
"archive/zip"
"fmt"
"io"
"net/http"
"os"
"path/filepath"
"strings"
)

func InstallWirePod(is InstallSettings) error {
UpdateInstallStatus("Stopping any wire-pod instances...")
StopWirePodIfRunning()

UpdateInstallStatus("Removing any wire-pod files (if they exist)...")
os.RemoveAll(is.Where)

UpdateInstallBar(0)
UpdateInstallStatus("Starting download...")

// Start downloading the file
resp, err := http.Get(amd64podURL)
if err != nil {
return fmt.Errorf("error getting wire-pod from GitHub: %s", err)
}
defer resp.Body.Close()

totalBytes := resp.ContentLength
var bytesRead int64 = 0

// Create a temporary file to store the download
UpdateInstallStatus("Creating temp file...")
tempFile, err := os.CreateTemp("", "wire-pod-*.zip")
if err != nil {
return fmt.Errorf("error creating a temp file: %s", err)
}
defer tempFile.Close()
defer os.Remove(tempFile.Name()) // Clean up

// Copy the download stream to the temp file with progress tracking
UpdateInstallStatus("Downloading wire-pod from latest release on GitHub...")
progressReader := io.TeeReader(resp.Body, tempFile)
buffer := make([]byte, 32*1024) // 32KB buffer
for {
n, err := progressReader.Read(buffer)
bytesRead += int64(n)
if n == 0 || err != nil {
break
}
UpdateInstallBar(float64(40) * float64(bytesRead) / float64(totalBytes))
}

if err != nil && err != io.EOF {
return fmt.Errorf("error while downloading: %s", err)
}

UpdateInstallBar(40)
UpdateInstallStatus("Starting extraction...")

// Open the zip file
zipReader, err := zip.OpenReader(tempFile.Name())
if err != nil {
return fmt.Errorf("error reading zip file: %s", err)
}
defer zipReader.Close()

// Process each file in the zip
for i, f := range zipReader.File {
// Skip the root directory
if f.Name == "wire-pod/" || strings.HasPrefix(f.Name, "wire-pod/") && f.FileInfo().IsDir() {
continue
}

// Adjust the file path to exclude the 'wire-pod/' prefix
adjustedPath := strings.TrimPrefix(f.Name, "wire-pod/")
UpdateInstallStatus("Extracting: " + adjustedPath)
fpath := filepath.Join(is.Where, adjustedPath)

// Check for ZipSlip (Directory traversal)
if !strings.HasPrefix(fpath, filepath.Clean(is.Where)+string(os.PathSeparator)) {
return fmt.Errorf("illegal file path: %s", fpath)
}

// Create all directories needed for the file path
if f.FileInfo().IsDir() {
os.MkdirAll(fpath, os.ModePerm)
continue
}

// Create the directories if necessary
if err := os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return fmt.Errorf("error creating directories: %s", err)
}

// Extract the file
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return fmt.Errorf("error opening file for writing: %s", err)
}

rc, err := f.Open()
if err != nil {
outFile.Close()
return fmt.Errorf("error opening zip contents: %s", err)
}

_, err = io.Copy(outFile, rc)
outFile.Close()
rc.Close()

if err != nil {
return fmt.Errorf("error writing file: %s", err)
}

// Update the progress bar for each file processed
UpdateInstallBar(40 + float64(40)*(float64(i)+1)/float64(len(zipReader.File)))
}

// Update status and progress bar for the final phase
UpdateInstallBar(81)
UpdateInstallStatus("Updating registry...")

UpdateRegistry(is)
UpdateInstallBar(90)

UpdateInstallStatus("Creating shortcut...")
CreateShortcut(is)

UpdateInstallStatus("Done!")

UpdateInstallBar(100)
return nil
}
103 changes: 58 additions & 45 deletions chipper/cmd/wire-pod-installer/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
"os"
"strconv"
"strings"
"time"

"fyne.io/fyne/v2"
"fyne.io/fyne/v2/app"
Expand All @@ -19,16 +18,43 @@ import (
//go:embed ico
var iconData embed.FS

var amd64podURL string = "https://github.com/kercre123/wire-pod/releases/latest/download/wire-pod-win-amd64.zip"

var DefaultInstallationDirectory string = "C:\\Program Files\\wire-pod"

var icon *fyne.StaticResource

var installerStatusUpdate chan string
var installerBarUpdate chan float64

type InstallSettings struct {
RunAtStartup bool
AutoUpdate bool
Where string
}

func UpdateInstallStatus(status string) {
select {
case installerStatusUpdate <- status:
default:
}
}

func UpdateInstallBar(status float64) {
select {
case installerBarUpdate <- status / 100:
default:
}
}

func GetBarChan() chan float64 {
return installerBarUpdate
}

func GetStatusChan() chan string {
return installerStatusUpdate
}

func PostInstall(myApp fyne.App) {
var shouldStartPod bool = true
window := myApp.NewWindow("wire-pod installer")
Expand Down Expand Up @@ -62,8 +88,7 @@ func PostInstall(myApp fyne.App) {
window.Show()
}

func DoInstall(myApp fyne.App) {
var barStatus float64
func DoInstall(myApp fyne.App, is InstallSettings) {
window := myApp.NewWindow("wire-pod installer")
window.Resize(fyne.Size{Width: 600, Height: 100})
window.CenterOnScreen()
Expand All @@ -77,20 +102,23 @@ func DoInstall(myApp fyne.App) {
bar,
))

barChan := GetBarChan()
statusChan := GetStatusChan()

window.Show()
i := 0
for {
time.Sleep(time.Second / 2)
barStatus = barStatus + 0.1
card.SetSubTitle(fmt.Sprint(i))
card.Refresh()
bar.SetValue(barStatus)
bar.Refresh()
i = i + 1
if i == 10 {
break
go func() {
for val := range barChan {
bar.SetValue(val)
card.Refresh()
}
}
}()
go func() {
for val := range statusChan {
card.SetSubTitle(val)
card.Refresh()
}
}()
InstallWirePod(is)
window.Hide()
PostInstall(myApp)
}
Expand Down Expand Up @@ -130,7 +158,7 @@ func GetPreferences(myApp fyne.App) {
)
} else {
window.Hide()
DoInstall(myApp)
DoInstall(myApp, is)
}
})

Expand Down Expand Up @@ -189,43 +217,28 @@ func ValidateInstallDirectory(dir string) bool {
}

func main() {
if !CheckIfElevated() {
fmt.Println("installer must be run as administrator")
os.Exit(0)
}
iconBytes, err := iconData.ReadFile("ico/pod.png")
if err != nil {
fmt.Println(err)
}
installerBarUpdate = make(chan float64)
installerStatusUpdate = make(chan string)
icon = fyne.NewStaticResource("icon", iconBytes)
myApp := app.New()
GetPreferences(myApp)
myApp.Run()
os.Exit(0)
}

// func runElevated() {
// drv, err := os.Open("\\\\.\\PHYSICALDRIVE0")
// if err != nil {
// for _, arg := range os.Args {ico
// if strings.Contains(arg, "-esc") {
// fmt.Println("Privledge escalation failed")
// os.Exit(0)
// }
// }
// verb := "runas"
// exe, _ := os.Executable()
// cwd, _ := os.Getwd()
// args := strings.Join(os.Args[1:], "-esc")

// verbPtr, _ := syscall.UTF16PtrFromString(verb)
// exePtr, _ := syscall.UTF16PtrFromString(exe)
// cwdPtr, _ := syscall.UTF16PtrFromString(cwd)
// argPtr, _ := syscall.UTF16PtrFromString(args)

// var showCmd int32 = 1 //SW_NORMAL

// err := windows.ShellExecute(0, verbPtr, exePtr, argPtr, cwdPtr, showCmd)
// if err != nil {
// fmt.Println(err)
// os.Exit(0)
// }
// }
// drv.Close()
// }
func CheckIfElevated() bool {
drv, err := os.Open("\\\\.\\PHYSICALDRIVE0")
if err != nil {
return false
}
drv.Close()
return true
}
6 changes: 5 additions & 1 deletion chipper/cmd/wire-pod-installer/rc/app.rc
Original file line number Diff line number Diff line change
@@ -1,2 +1,6 @@
id ICON "cmd/wire-pod-installer/rc/pod.ico"
id ICON "windows/icons/ico/pod16x16.ico"
id ICON "windows/icons/ico/pod24x24.ico"
id ICON "windows/icons/ico/pod32x32.ico"
id ICON "windows/icons/ico/pod48x48.ico"
id ICON "windows/icons/ico/pod256x256.ico"
1 24 "cmd/wire-pod-installer/rc/app.manifest"
39 changes: 39 additions & 0 deletions chipper/cmd/wire-pod-installer/registry.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package main

import (
"fmt"
"path/filepath"

"golang.org/x/sys/windows/registry"
)

func UpdateRegistry(is InstallSettings) {
keyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\wire-pod`
appName := "wire-pod"
displayIcon := filepath.Join(is.Where, `\chipper\icons\ico\pod256x256.ico`)
displayVersion := "1.0.0"
publisher := "github.com/kercre123"
uninstallString := filepath.Join(is.Where, `\uninstall.exe`)
installLocation := filepath.Join(is.Where, `\chipper\chipper.exe`)
k, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE|registry.SET_VALUE)
if err != nil {
k, _, err = registry.CreateKey(registry.LOCAL_MACHINE, keyPath, registry.ALL_ACCESS)
if err != nil {
fmt.Printf("Error creating registry key: %v\n", err)
return
}
}
defer k.Close()

err = k.SetStringValue("DisplayName", appName)
if err != nil {
fmt.Printf("Error setting DisplayName: %v\n", err)
return
}
k.SetStringValue("DisplayIcon", displayIcon)
k.SetStringValue("DisplayVersion", displayVersion)
k.SetStringValue("Publisher", publisher)
k.SetStringValue("UninstallString", uninstallString)
k.SetStringValue("InstallLocation", installLocation)
fmt.Println("Registry entries successfully created")
}
36 changes: 36 additions & 0 deletions chipper/cmd/wire-pod-installer/shortcut.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package main

import (
"path/filepath"

"github.com/go-ole/go-ole"
"github.com/go-ole/go-ole/oleutil"
)

func CreateShortcut(is InstallSettings) {
ole.CoInitialize(0)
defer ole.CoUninitialize()

unknown, err := oleutil.CreateObject("WScript.Shell")
if err != nil {
panic(err)
}
defer unknown.Release()

wshell, err := unknown.QueryInterface(ole.IID_IDispatch)
if err != nil {
panic(err)
}
defer wshell.Release()

cs, err := oleutil.CallMethod(wshell, "CreateShortcut", "C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\wire-pod.lnk")
if err != nil {
panic(err)
}
shortcut := cs.ToIDispatch()
defer shortcut.Release()

oleutil.PutProperty(shortcut, "TargetPath", filepath.Join(is.Where, "\\chipper\\chipper.exe"))
// Set other properties as needed
oleutil.CallMethod(shortcut, "Save")
}
Loading

0 comments on commit a0ebb94

Please sign in to comment.