diff --git a/chipper/cmd/windows/main.go b/chipper/cmd/windows/main.go index 87c24374..82c54741 100755 --- a/chipper/cmd/windows/main.go +++ b/chipper/cmd/windows/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "os" "os/exec" @@ -22,7 +23,7 @@ import ( var mBoxTitle = "wire-pod" var mBoxError = `There was an error starting wire-pod: ` var mBoxAlreadyRunning = "Wire-pod is already running. You must quit that instance before starting another one. Exiting." -var mBoxSuccess = `Wire-pod has started successfully! It is now running in the background. It can be stopped in the system tray.` +var mBoxSuccess = `Wire-pod has started successfully! It is now running in the background and can be managed in the system tray.` var mBoxIcon = "./icons/start-up-full.png" func getNeedsSetupMsg() string { @@ -48,14 +49,33 @@ func main() { os.Exit(1) } } - os.WriteFile(conf+"\\runningPID", []byte(strconv.Itoa(os.Getpid())), 0777) - os.WriteFile(filepath.Join(os.TempDir(), "/wirepodrunningPID"), []byte(strconv.Itoa(os.Getpid())), 0777) + k, err := registry.OpenKey(registry.CURRENT_USER, `Software\wire-pod`, registry.WRITE|registry.READ) + if err != nil { + fmt.Println("Error reading from registry: " + err.Error()) + return + } + defer k.Close() - keyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\wire-pod` - k, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE) + pidPre, _, err := k.GetIntegerValue("LastRunningPID") + if err == nil { + fmt.Println(int(pidPre)) + _, err := os.FindProcess(int(pidPre)) + if err == nil || errors.Is(err, os.ErrPermission) { + zenity.Error( + "Wire-pod is already running.", + zenity.ErrorIcon, + zenity.Title(mBoxTitle), + ) + os.Exit(1) + } + } + + err = k.SetQWordValue("LastRunningPID", uint64(os.Getpid())) if err != nil { - ErrMsg(fmt.Errorf("error opening key from the registry: " + err.Error())) + fmt.Println("Error writing to registry: " + err.Error()) + return } + val, _, err := k.GetStringValue("InstallPath") if err != nil { ErrMsg(fmt.Errorf("error getting value from the registry: " + err.Error())) @@ -69,10 +89,13 @@ func main() { } func ExitProgram(code int) { - conf, _ := os.UserConfigDir() - os.Remove(conf + "/runningPID") + k, err := registry.OpenKey(registry.CURRENT_USER, `Software\wire-pod`, registry.WRITE|registry.READ) + if err != nil { + fmt.Println("Error reading from registry: " + err.Error()) + os.Exit(code) + } + k.DeleteValue("LastRunningPID") os.Exit(code) - } func onExit() { @@ -99,6 +122,7 @@ func onReady() { systray.SetTooltip("wire-pod is starting...") mQuit := systray.AddMenuItem("Quit", "Quit wire-pod") mBrowse := systray.AddMenuItem("Web interface", "Open web UI") + mConfig := systray.AddMenuItem("Config folder", "Open config folder in case you need to. The web UI should have everything you need.") go func() { for { @@ -112,6 +136,9 @@ func onReady() { ExitProgram(0) case <-mBrowse.ClickedCh: go openBrowser("http://" + botsetup.GetOutboundIP().String() + ":" + vars.WebPort) + case <-mConfig.ClickedCh: + conf, _ := os.UserConfigDir() + go openFileExplorer(filepath.Join(conf, vars.PodName)) } } }() @@ -142,3 +169,19 @@ func openBrowser(url string) { logger.Println(err) } } + +func openFileExplorer(path string) error { + var cmd *exec.Cmd + + switch runtime.GOOS { + case "windows": + cmd = exec.Command("explorer", path) + case "darwin": + cmd = exec.Command("open", path) + case "linux": + cmd = exec.Command("xdg-open", path) + default: + return fmt.Errorf("unsupported platform") + } + return cmd.Start() +} diff --git a/chipper/cmd/windows/win-initwirepod.go b/chipper/cmd/windows/win-initwirepod.go index 5fc13896..b8953a5e 100644 --- a/chipper/cmd/windows/win-initwirepod.go +++ b/chipper/cmd/windows/win-initwirepod.go @@ -7,6 +7,7 @@ import ( "net/http" "os" "path/filepath" + "strings" "time" chipperpb "github.com/digital-dream-labs/api/go/chipperpb" @@ -49,6 +50,7 @@ func NeedsSetupMsg() { zenity.Icon(mBoxIcon), zenity.Title(mBoxTitle), zenity.ExtraButton("Open browser"), + zenity.OKLabel("OK"), ) if err != nil { if err == zenity.ErrExtraButton { @@ -275,8 +277,14 @@ func StartChipper(fromInit bool) { go httpServe(httpListenerTwo) } - systray.SetTooltip("wire-pod is running.") - if fromInit { + systray.SetTooltip("wire-pod is running.\n" + "http://" + botsetup.GetOutboundIP().String() + ":" + vars.WebPort) + var discrete bool + if len(os.Args) > 1 { + if strings.Contains(os.Args[1], "-d") { + discrete = true + } + } + if fromInit && !discrete { go zenity.Info( mBoxSuccess, zenity.Icon(mBoxIcon), diff --git a/chipper/cmd/wire-pod-installer/install.go b/chipper/cmd/wire-pod-installer/install.go index 039aa3e1..80091bf4 100644 --- a/chipper/cmd/wire-pod-installer/install.go +++ b/chipper/cmd/wire-pod-installer/install.go @@ -8,12 +8,16 @@ import ( "os" "path/filepath" "strings" + "time" ) func InstallWirePod(is InstallSettings) error { UpdateInstallStatus("Stopping any wire-pod instances...") StopWirePodIfRunning() + UpdateInstallStatus("Uninstalling any previous wire-pod instances (user data will be kept)...") + DeleteAnyOtherInstallation() + UpdateInstallStatus("Removing any wire-pod files (if they exist)...") os.RemoveAll(is.Where) @@ -31,7 +35,6 @@ func InstallWirePod(is InstallSettings) error { 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) @@ -138,5 +141,6 @@ func InstallWirePod(is InstallSettings) error { UpdateInstallStatus("Done!") UpdateInstallBar(100) + time.Sleep(time.Second / 3) return nil } diff --git a/chipper/cmd/wire-pod-installer/main.go b/chipper/cmd/wire-pod-installer/main.go index c36a5ba6..5d15ed38 100644 --- a/chipper/cmd/wire-pod-installer/main.go +++ b/chipper/cmd/wire-pod-installer/main.go @@ -62,7 +62,6 @@ func GetStatusChan() chan string { func ExecuteDetached(program string) error { cmd := exec.Command(program) - // Start the program in a new process group to make it detached cmd.SysProcAttr = &syscall.SysProcAttr{CreationFlags: syscall.CREATE_NEW_PROCESS_GROUP} return cmd.Start() } @@ -131,7 +130,10 @@ func DoInstall(myApp fyne.App, is InstallSettings) { card.Refresh() } }() - InstallWirePod(is) + err := InstallWirePod(is) + if err != nil { + fmt.Println("error installing wire-pod: " + err.Error()) + } window.Hide() PostInstall(myApp, is) } @@ -146,13 +148,16 @@ func GetPreferences(myApp fyne.App) { is.RunAtStartup = checked }) + launchOnStartup.SetChecked(true) + is.RunAtStartup = true + installDir := widget.NewEntry() installDir.SetText(DefaultInstallationDirectory) selectDirButton := widget.NewButton("Select Directory", func() { dlg := dialog.NewFolderOpen(func(uri fyne.ListableURI, err error) { if uri != nil { - installDir.SetText(uri.Path()) + installDir.SetText(filepath.Join(uri.Path(), "wire-pod")) } }, window) dlg.Show() @@ -191,9 +196,7 @@ func GetPreferences(myApp fyne.App) { } func StopWirePodIfRunning() { - confDir, _ := os.UserConfigDir() - podDir := confDir + "/wire-pod" - podPid, err := os.ReadFile(podDir + "/runningPID") + podPid, err := os.ReadFile(filepath.Join(os.TempDir(), "/wirepodrunningPID")) if err == nil { pid, _ := strconv.Atoi(string(podPid)) // doesn't work on unix, but should on Windows @@ -205,12 +208,10 @@ func StopWirePodIfRunning() { fmt.Println("Stopped") } } + CheckWirePodRunningViaRegistry() } func ValidateInstallDirectory(dir string) bool { - if dir == "C:\\Program Files" || dir == "C:\\Program Files\\" { - return false - } var dirWithoutLast string splitDir := strings.Split(dir, "\\") dirWithoutLast = splitDir[0] @@ -233,6 +234,19 @@ func main() { fmt.Println("installer must be run as administrator") os.Exit(0) } + fmt.Println("Getting tag from GitHub") + tag, err := GetLatestReleaseTag("kercre123", "wire-pod") + if err != nil { + fmt.Println("Error getting: " + err.Error()) + zenity.Error( + "Error getting latest GitHub tag from GitHub, exiting: "+err.Error(), + zenity.ErrorIcon, + zenity.Title("wire-pod installer"), + ) + os.Exit(0) + } + GitHubTag = tag + fmt.Println(tag) iconBytes, err := iconData.ReadFile("ico/pod.png") if err != nil { fmt.Println(err) diff --git a/chipper/cmd/wire-pod-installer/registry.go b/chipper/cmd/wire-pod-installer/registry.go index 3c3c68c3..2b322bae 100644 --- a/chipper/cmd/wire-pod-installer/registry.go +++ b/chipper/cmd/wire-pod-installer/registry.go @@ -1,19 +1,57 @@ package main import ( + "encoding/json" + "errors" "fmt" + "io" "log" + "net/http" + "os" "os/exec" "path/filepath" "golang.org/x/sys/windows/registry" ) +var GitHubTag string + func UpdateRegistry(is InstallSettings) { + UpdateUninstallRegistry(is) + UpdateSoftwareRegistry(is) +} + +func DeleteAnyOtherInstallation() { + keyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\wire-pod` + k, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE|registry.SET_VALUE) + if err != nil { + return + } + instPath, _, err := k.GetStringValue("InstallPath") + if err != nil { + soft, err := registry.OpenKey(registry.CURRENT_USER, `Software\wire-pod`, registry.QUERY_VALUE|registry.SET_VALUE) + if err != nil { + return + } + val, _, err := soft.GetStringValue("InstallPath") + if err != nil { + return + } + fmt.Println("Running uninstaller") + cmd := exec.Command(val) + cmd.Env = os.Environ() + cmd.Env = append(cmd.Env, "RUN_DISCRETE=true") + cmd.Run() + } else { + os.RemoveAll(instPath) + } +} + +func UpdateUninstallRegistry(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" + displayVersion := GitHubTag publisher := "github.com/kercre123" uninstallString := filepath.Join(is.Where, `\uninstall.exe`) installLocation := filepath.Join(is.Where, `\chipper\chipper.exe`) @@ -37,13 +75,58 @@ func UpdateRegistry(is InstallSettings) { k.SetStringValue("Publisher", publisher) k.SetStringValue("UninstallString", uninstallString) k.SetStringValue("InstallLocation", installLocation) - k.SetStringValue("InstallPath", is.Where) fmt.Println("Registry entries successfully created") } +func UpdateSoftwareRegistry(is InstallSettings) { + keyPath := `Software\wire-pod` + k, err := registry.OpenKey(registry.CURRENT_USER, keyPath, registry.QUERY_VALUE|registry.SET_VALUE) + if err != nil { + k, _, err = registry.CreateKey(registry.CURRENT_USER, keyPath, registry.ALL_ACCESS) + if err != nil { + fmt.Printf("Error creating registry key: %v\n", err) + return + } + } + defer k.Close() + k.SetStringValue("InstallPath", is.Where) + k.SetStringValue("PodVersion", GitHubTag) +} + +type Release struct { + TagName string `json:"tag_name"` +} + +func GetLatestReleaseTag(owner, repo string) (string, error) { + url := fmt.Sprintf("https://api.github.com/repos/%s/%s/releases/latest", owner, repo) + + resp, err := http.Get(url) + if err != nil { + return "", err + } + defer resp.Body.Close() + + body, err := io.ReadAll(resp.Body) + if err != nil { + return "", err + } + + var release Release + if err := json.Unmarshal(body, &release); err != nil { + return "", err + } + + return release.TagName, nil +} + func RunPodAtStartup(is InstallSettings) { key, _ := registry.OpenKey(registry.CURRENT_USER, `Software\Microsoft\Windows\CurrentVersion\Run`, registry.SET_VALUE) - key.SetStringValue("wire-pod", filepath.Join(is.Where, "\\chipper\\chipper.exe")) + + // Constructing the command to set the environment variable and run chipper.exe + //cmd.exe /C start "" "C:\Program Files\wire-pod\chipper\chipper.exe" -d + cmd := fmt.Sprintf(`cmd.exe /C start "" "` + filepath.Join(is.Where, "chipper\\chipper.exe") + `" -d`) + + key.SetStringValue("wire-pod", cmd) } func AllowThroughFirewall(is InstallSettings) { @@ -77,3 +160,27 @@ func AllowThroughFirewall(is InstallSettings) { log.Println("Firewall rule added successfully.") } + +func CheckWirePodRunningViaRegistry() { + k, err := registry.OpenKey(registry.CURRENT_USER, `Software\wire-pod`, registry.WRITE|registry.READ) + if err != nil { + fmt.Println("Error reading from registry: " + err.Error()) + return + } + defer k.Close() + + // Write a value + val, _, err := k.GetIntegerValue("LastRunningPID") + if err != nil { + fmt.Println("Error reading from registry: " + err.Error()) + return + } + // doesn't work on unix, but should on Windows + podProcess, err := os.FindProcess(int(val)) + if err == nil || errors.Is(err, os.ErrPermission) { + fmt.Println("Stopping wire-pod") + podProcess.Kill() + podProcess.Wait() + fmt.Println("Stopped") + } +} diff --git a/chipper/cmd/wire-pod-installer/uninstall/main.go b/chipper/cmd/wire-pod-installer/uninstall/main.go index 3361f182..61853f88 100644 --- a/chipper/cmd/wire-pod-installer/uninstall/main.go +++ b/chipper/cmd/wire-pod-installer/uninstall/main.go @@ -1,6 +1,7 @@ package main import ( + "errors" "fmt" "os" "path/filepath" @@ -10,6 +11,8 @@ import ( "golang.org/x/sys/windows/registry" ) +var discrete bool + func StopWirePodIfRunning() { podPid, err := os.ReadFile(filepath.Join(os.TempDir(), "/wirepodrunningPID")) if err == nil { @@ -23,20 +26,64 @@ func StopWirePodIfRunning() { fmt.Println("Stopped") } } + CheckWirePodRunningViaRegistry() +} + +func CheckWirePodRunningViaRegistry() { + k, err := registry.OpenKey(registry.CURRENT_USER, `Software\wire-pod`, registry.WRITE|registry.READ) + if err != nil { + fmt.Println("Error reading from registry: " + err.Error()) + return + } + defer k.Close() + + // Write a value + val, _, err := k.GetIntegerValue("LastRunningPID") + if err != nil { + fmt.Println("Error reading from registry: " + err.Error()) + return + } + // doesn't work on unix, but should on Windows + podProcess, err := os.FindProcess(int(val)) + if err == nil || errors.Is(err, os.ErrPermission) { + fmt.Println("Stopping wire-pod") + podProcess.Kill() + podProcess.Wait() + fmt.Println("Stopped") + } } func main() { + if os.Getenv("RUN_DISCRETE") == "true" { + discrete = true + } + if !discrete { + err := zenity.Question( + "Are you sure you want to uninstall wire-pod?", + zenity.QuestionIcon, + zenity.Title("wire-pod uninstaller"), + zenity.OKLabel("Yes"), + ) + if errors.Is(err, zenity.ErrCanceled) { + os.Exit(1) + } + } StopWirePodIfRunning() - err := zenity.Question( - "Would you like to remove application data, like saved bot settings or API preferences?", - zenity.ExtraButton("No"), - ) - if err == nil { - conf, _ := os.UserConfigDir() - os.RemoveAll(filepath.Join(conf, "wire-pod")) + if !discrete { + err := zenity.Question( + "Would you like to remove application data, including saved bot settings and API preferences?", + zenity.ExtraButton("No"), + zenity.QuestionIcon, + zenity.NoCancel(), + zenity.Title("wire-pod uninstaller"), + ) + if err == nil { + conf, _ := os.UserConfigDir() + os.RemoveAll(filepath.Join(conf, "wire-pod")) + } } - keyPath := `Software\Microsoft\Windows\CurrentVersion\Uninstall\wire-pod` - k, err := registry.OpenKey(registry.LOCAL_MACHINE, keyPath, registry.QUERY_VALUE) + keyPath := `Software\wire-pod` + k, err := registry.OpenKey(registry.CURRENT_USER, keyPath, registry.QUERY_VALUE) if err != nil { fmt.Println(err) return @@ -48,7 +95,10 @@ func main() { } k.Close() + keyPath = `Software\Microsoft\Windows\CurrentVersion\Uninstall\wire-pod` registry.DeleteKey(registry.LOCAL_MACHINE, keyPath) + keyPath = `Software\wire-pod` + registry.DeleteKey(registry.CURRENT_USER, keyPath) DontRunPodAtStartup() @@ -57,11 +107,13 @@ func main() { os.RemoveAll(filepath.Join(val, "chipper")) os.RemoveAll(filepath.Join(val, "vector-cloud")) os.Remove("C:\\ProgramData\\Microsoft\\Windows\\Start Menu\\Programs\\wire-pod.lnk") - zenity.Info( - "wire-pod has successfully been uninstalled.", - zenity.InfoIcon, - zenity.Title("wire-pod uninstaller"), - ) + if !discrete { + zenity.Info( + "wire-pod has successfully been uninstalled.", + zenity.InfoIcon, + zenity.Title("wire-pod uninstaller"), + ) + } os.RemoveAll(val) os.Exit(0) }